@@ -7,7 +7,7 @@ const curmod = module_name(current_module())
7
7
# This is basically just adding a few convenience functions & macros
8
8
# around Holy Traits.
9
9
10
- export Trait, istrait, @traitdef , @traitimpl , @traitfn , Not
10
+ export Trait, istrait, supertraits, @traitdef , @traitimpl , @traitfn , Not
11
11
12
12
# All traits are concrete subtypes of this trait. SUPER is not used
13
13
# but present to be compatible with Traits.jl.
@@ -24,26 +24,35 @@ immutable Tr1{X,Y} <: Trait end
24
24
where X and Y are the types involved in the trait.
25
25
26
26
27
- (SUPER is not used here but in Traits.jl, thus retained for possible
28
- future compatibility.)
27
+ The type parameter SUPER of Trait is needed to specify super-traits (a
28
+ tuple).
29
29
"""
30
30
abstract Trait{SUPER}
31
31
32
+ " Return super-traits"
33
+ supertraits {T<:Trait} (t:: Type{T} ) = t. super. parameters[1 ]
34
+
32
35
"""
33
36
The set of all types not belonging to a trait is encoded by wrapping
34
37
it with Not{}, e.g. Not{Tr1{X,Y}}
35
38
"""
36
- abstract Not{T<: Trait } <: Trait
39
+ immutable Not{T<: Trait } <: Trait end
37
40
38
41
# Helper to strip an even number of Not{}s off: Not{Not{T}}->T
39
42
stripNot {T<:Trait} (:: Type{T} ) = T
40
43
stripNot {T<:Trait} (:: Type{Not{T}} ) = Not{T}
41
44
stripNot {T<:Trait} (:: Type{Not{Not{T}}} ) = stripNot (T)
42
45
43
46
"""
47
+ This is the function used to do the trait-dispatch. Note that
48
+ anything but a constant method will probably not be inlined away by
49
+ the JIT and will lead to slower dynamic dispatch.
50
+
44
51
A trait is defined as full-filled if this function is the identity
45
52
function for that trait. Otherwise it returns the trait wrapped in
46
- `Not`.
53
+ `Not`. Note that *no super-trait checking* is done when this function
54
+ is called. Thus, if appropriate, before this function is defined for
55
+ a trait a check that its super-traits are defined should be done.
47
56
48
57
Example:
49
58
```
@@ -70,35 +79,61 @@ trait{T<:Trait}(::Type{Not{T}}) = trait(T)
70
79
71
80
"""
72
81
This function checks whether a trait is fulfilled by a specific
73
- set of types.
82
+ set of types:
74
83
```
75
84
istrait(Tr1{Int,Float64}) => return true or false
76
85
```
86
+
87
+ or that all traits of a Tuple of traits are fulfilled
88
+ ```
89
+ istrait( Tuple{Tr1{Int,Float64}, Tr2{Int}} ) => return true or false
90
+ ```
77
91
"""
78
92
istrait (:: Any ) = error (" Argument is not a Trait." )
79
- istrait {T<:Trait} (tr:: Type{T} ) = trait (tr)== stripNot (tr) ? true : false # Problem, this can run into issue #265
93
+ function istrait {T<:Trait} (tr:: Type{T} )
94
+ ! isleaftype (tr) && error (" Not all parameters of $tr are specified, thus not a valid trait." )
95
+ trait (tr)== stripNot (tr) ? true : false # Problem, this can run into issue #265
96
+ end
80
97
# thus is redefine when traits are defined
98
+ istrait {T<:Tuple} (tr:: Type{T} ) = mapreduce (istrait, & , true , tr. parameters)
99
+
81
100
"""
82
101
83
- Used to define a trait. Traits, like types, are camel cased. I
84
- suggest to start them with a verb, e.g. `IsImmutable`, to distinguish
85
- them from actual types, which are usually nouns.
102
+ Defines a trait. Traits need to have one or more (type-)parameters to
103
+ specify the type to which the trait is applied. For instance
104
+ `IsImmutable{Int}` signifies that `Int` is part of `IsImmutable`
105
+ (although whether that is true needs to be checked with the `istrait`
106
+ function). Most traits will be one-parameter traits, however, several
107
+ parameters are useful when there is a "contract" between several
108
+ types.
109
+
110
+ Traits can be sub-traits of one or several other traits. A trait is
111
+ then fulfilled if it is fulfilled itself and all its super-traits are
112
+ also fulfilled.
113
+
114
+ Traits, like types, are camel cased. I suggest to start them with a
115
+ verb, e.g. `IsImmutable`, to distinguish them from actual types, which
116
+ are usually nouns.
86
117
87
- Traits need to have one or more (type-)parameters to specify the type
88
- to which the trait is applied. For instance `IsImmutable{Int}`
89
- signifies that `Int` is part of `IsImmutable` (although whether that
90
- is true needs to be checked with the `istrait` function). Most traits
91
- will be one-parameter traits, however, several parameters are useful
92
- when there is a "contract" between several types.
93
118
94
119
Examples:
95
120
```julia
96
121
@traitdef IsFast{X}
97
- @traitdef IsSlow{X,Y}
122
+ @traitdef IsSuperFast{X} <: IsFast{X}
123
+ @traitdef IsHyper{X}
124
+ @traitdef IsHyperFast{X} <: IsSuperFast{X}, IsHyper{X}
98
125
```
99
126
"""
100
127
macro traitdef (tr)
101
- :(immutable $ (esc (tr)) <: Trait end )
128
+ Tr, Pr, SUPER = @match tr begin
129
+ Tr_{Pr__} <: SUPER__ => (Tr, Pr, SUPER)
130
+ Tr_{Pr__} <: SUPER1_ ,SUPER__ => (Tr, Pr, [SUPER1, SUPER... ])
131
+ Tr_{Pr__} => (Tr, Pr, [])
132
+ end
133
+ tmp = :(Tuple{})
134
+ append! (tmp. args, SUPER)
135
+ T = esc (:($ Tr{$ (Pr... )}))
136
+ :(immutable $ T <: Trait{$(esc(tmp))} end )
102
137
end
103
138
104
139
"""
@@ -112,8 +147,10 @@ Example:
112
147
```
113
148
"""
114
149
macro traitimpl (tr)
115
- # makes
150
+ # makes from @traitimpl Tr1{Int,Float64}
151
+ # istrait(supertrait(Tr1{Int,Float64})) || error("...")
116
152
# trait{X1<:Int,X2<:Float64}(::Type{Tr1{X1,X2}}) = Tr1{X1,X2}
153
+ # istrait{X1<:Int,X2<:Float64}(::Type{Tr1{X1,X2}}) = true
117
154
if tr. args[1 ]== :Not || isnegated (tr)
118
155
tr = tr. args[2 ]
119
156
negated = true
@@ -129,17 +166,21 @@ macro traitimpl(tr)
129
166
push! (paras, esc (v))
130
167
end
131
168
arg = :(:: Type{$trname{$(paras...)}} )
132
- fnhead = :($ curmod. trait {$(curly...)} ($ arg))
133
- isfnhead = :($ curmod. istrait {$(curly...)} ($ arg))
169
+ fnhead = :(trait {$(curly...)} ($ arg))
170
+ isfnhead = :(istrait {$(curly...)} ($ arg))
171
+ trr = :($ trname{$ (paras... )})
172
+ errstr = :(" Not all super-traits of $($ (esc (tr))) fulfilled" )
134
173
if ! negated
135
174
return quote
136
- $ fnhead = $ trname{$ (paras... )}
175
+ ! istrait (supertraits ($ (esc (tr)))) && error ($ errstr)
176
+ $ fnhead = $ trr
137
177
$ isfnhead = true # Add the istrait definition as otherwise
138
178
# method-caching can be an issue.
139
179
end
140
180
else
141
181
return quote
142
- $ fnhead = Not{$ trname{$ (paras... )}}
182
+ ! istrait (supertraits ($ (esc (tr)))) && error ($ errstr)
183
+ $ fnhead = Not{$ trr}
143
184
$ isfnhead = false # Add the istrait definition as otherwise
144
185
# method-caching can be an issue.
145
186
end
0 commit comments