Skip to content

Commit b23bfa5

Browse files
authored
specify supertype in interface macro (#31)
* specify type in interface macro * bugfix * cleanup
1 parent 262a4ff commit b23bfa5

File tree

5 files changed

+26
-9
lines changed

5 files changed

+26
-9
lines changed

src/implements.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ function _implements_inner(interface, objtype; show=false)
5656
optional_keys = ()
5757
end
5858
quote
59+
# Chreck that the type matches
60+
let objtype = $objtype, interface=$interface
61+
objtype <: Interfaces.requiredtype(interface) || throw(ArgumentError("$objtype is not a subtype of $(Interfaces.requiredtype(interface))"))
62+
end
5963
# Define a `implements` trait stating that `objtype` implements `interface`
6064
$Interfaces.implements(::Type{<:$interfacetype}, ::Type{<:$objtype}) = true
6165
$Interfaces.implements(T::Type{<:$interfacetype{Options}}, O::Type{<:$objtype}) where Options =

src/interface.jl

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,17 @@ Returns the components of the interface, as a `NamedTuple` of `NamedTuple`.
3535
"""
3636
function components end
3737

38+
"""
39+
requiredtype(::Type{<:Interface})
40+
41+
Returns the supertype required for all interface implementations.
42+
"""
43+
function requiredtype end
44+
3845
"""
3946
@interface(interfacename, components, [description])
4047
41-
Define an interface.
48+
Define an interface that can apply to types `<: Any`.
4249
4350
```julia
4451
components = (
@@ -50,17 +57,19 @@ components = (
5057
)
5158
description = "A description of the interface"
5259
53-
@interface MyInterface components description
60+
@interface MyInterface Any components description
5461
```
5562
"""
56-
macro interface(interface::Symbol, components, description)
63+
macro interface(interface::Symbol, type, components, description)
5764
quote
65+
@assert $type isa Type
66+
@assert $components isa NamedTuple{(:mandatory,:optional)}
67+
@assert $description isa String
5868
# Define the interface type (should it be concrete?)
5969
abstract type $interface{Components} <: $Interfaces.Interface{Components} end
6070
# Define the interface component methods
61-
@assert $components isa NamedTuple{(:mandatory,:optional)}
71+
$Interfaces.requiredtype(::Type{<:$interface}) = $type
6272
$Interfaces.components(::Type{<:$interface}) = $components
63-
@assert $description isa String
6473
$Interfaces.description(::Type{<:$interface}) = $description
6574
# Generate a docstring for the interface
6675
let description=$description,

src/test.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ test(T::Type{<:Interface}, obj; kw...) = test(T, typeof(obj), (obj,); kw...)
5252
function _test(T::Type{<:Interface}, O::Type, objs::TestObjectWrapper;
5353
show=true, keys=nothing
5454
)
55+
56+
O <: requiredtype(T) || throw(ArgumentError("$O is not a subtype of $(requiredtype(T))"))
5557
check_coherent_types(O, objs)
5658
if show
5759
print("Testing ")

test/advanced.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function neutral end
2929
function multiplication end
3030
function inversion end
3131

32-
@interface GroupInterface (
32+
@interface GroupInterface Number (
3333
mandatory = (;
3434
neutral_check = (
3535
"neutral stable" => a::Arguments -> neutral(typeof(a.x)) isa typeof(a.x),

test/basic.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ module Animals
1919

2020
using Interfaces
2121

22+
abstract type Animal end
23+
2224
function age end
2325
function walk end
2426
function talk end
@@ -42,7 +44,7 @@ description = """
4244
Defines a generic interface for animals to do the things they do best.
4345
"""
4446

45-
@interface AnimalInterface components description
47+
@interface AnimalInterface Animal components description
4648

4749
end;
4850

@@ -52,7 +54,7 @@ using Interfaces
5254

5355
# Now we implement the `AnimalInterface`, for a `Duck`.
5456

55-
struct Duck
57+
struct Duck <: Animals.Animal
5658
age::Int
5759
end
5860

@@ -80,7 +82,7 @@ The `@implements` macro takes two arguments.
8082
@implements Animals.AnimalInterface{(:walk,:talk)} Duck
8183

8284
# Now let's see what happens when the interface is not correctly implemented.
83-
struct Chicken end
85+
struct Chicken <: Animals.Animal end
8486

8587
# As expected, the tests fail
8688
chickens = [Chicken()]

0 commit comments

Comments
 (0)