Skip to content

Commit ee53060

Browse files
committed
Updated to where syntax, fixes #46
1 parent 1fa580a commit ee53060

File tree

5 files changed

+383
-73
lines changed

5 files changed

+383
-73
lines changed

src/SimpleTraits.jl

Lines changed: 103 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ it with Not{}, e.g. Not{Tr1{X,Y}}
4343
Not
4444

4545
# Helper to strip an even number of Not{}s off: Not{Not{T}}->T
46-
stripNot{T<:Trait}(::Type{T}) = T
47-
stripNot{T<:Trait}(::Type{Not{T}}) = Not{T}
48-
stripNot{T<:Trait}(::Type{Not{Not{T}}}) = stripNot(T)
46+
stripNot(::Type{T}) where {T<:Trait} = T
47+
stripNot(::Type{Not{T}}) where {T<:Trait} = Not{T}
48+
stripNot(::Type{Not{Not{T}}}) where {T<:Trait} = stripNot(T)
4949

5050
"""
5151
A trait is defined as full-filled if this function is the identity
@@ -63,14 +63,14 @@ Usually this function is defined when using the `@traitimpl` macro.
6363
However, instead of using `@traitimpl` one can define a method for
6464
`trait` to implement a trait, see the README.
6565
"""
66-
trait{T<:Trait}(::Type{T}) = Not{T}
67-
trait{T<:Trait}(::Type{Not{T}}) = trait(T)
66+
trait(::Type{T}) where {T<:Trait} = Not{T}
67+
trait(::Type{Not{T}}) where {T<:Trait} = trait(T)
6868

6969
## Under the hood, a trait is then implemented for specific types by
7070
## defining:
7171
# trait(::Type{Tr1{Int,Float64}}) = Tr1{Int,Float64}
7272
# or
73-
# trait{I<:Integer,F<:FloatingPoint}(::Type{Tr1{I,F}}) = Tr1{I,F}
73+
# trait(::Type{Tr1{I,F}}) where {I<:Integer,F<:FloatingPoint} = Tr1{I,F}
7474
#
7575
# Note due to invariance, this does probably not the right thing:
7676
# trait(::Type{Tr1{Integer,FloatingPoint}}) = Tr1{Integer, FloatingPoint}
@@ -83,7 +83,7 @@ istrait(Tr1{Int,Float64}) => return true or false
8383
```
8484
"""
8585
istrait(::Any) = error("Argument is not a Trait.")
86-
istrait{T<:Trait}(tr::Type{T}) = trait(tr)==stripNot(tr) ? true : false # Problem, this can run into issue #265
86+
istrait(tr::Type{T}) where {T<:Trait} = trait(tr)==stripNot(tr) ? true : false # Problem, this can run into issue #265
8787
# thus is redefine when traits are defined
8888
"""
8989
@@ -148,8 +148,8 @@ macro traitimpl(tr)
148148
push!(paras, esc(v))
149149
end
150150
arg = :(::Type{$trname{$(paras...)}})
151-
fnhead = :($curmod.trait{$(curly...)}($arg))
152-
isfnhead = :($curmod.istrait{$(curly...)}($arg))
151+
fnhead = :($curmod.trait($arg) where {$(curly...)})
152+
isfnhead = :($curmod.istrait($arg) where {$(curly...)})
153153
if !negated
154154
return quote
155155
$fnhead = $trname{$(paras...)}
@@ -171,8 +171,8 @@ macro traitimpl(tr)
171171
fn = Expr(:call, GlobalRef(SimpleTraits, :!), fn)
172172
end
173173
return esc(quote
174-
function SimpleTraits.trait{$(P1...)}(::Type{$Tr{$(P1...)}})
175-
return $fn($(P2...)) ? $Tr{$(P1...)} : Not{$Tr{$(P1...)}}
174+
function SimpleTraits.trait(::Type{$Tr{$(P1...)}}) where {$(P1...)}
175+
return $fn($(P2...)) ? $Tr{$(P1...)} : Not{$Tr{$(P1...)}}
176176
end
177177
nothing
178178
end)
@@ -182,16 +182,16 @@ macro traitimpl(tr)
182182
end
183183

184184
# Defining a function dispatching on the trait (or not)
185-
# @traitfn f{X,Y; Tr1{X,Y}}(x::X,y::Y) = ...
186-
# @traitfn f{X,Y; !Tr1{X,Y}}(x::X,y::Y) = ... # which is just sugar for:
187-
# @traitfn f{X,Y; Not{Tr1{X,Y}}}(x::X,y::Y) = ...
185+
# @traitfn f(x::X,y::Y) where {X,Y; Tr1{X,Y}} = ...
186+
# @traitfn f(x::X,y::Y) where {X,Y; !Tr1{X,Y}} = ... # which is just sugar for:
187+
# @traitfn f(x::X,y::Y) where {X,Y; Not{Tr1{X,Y}}} = ...
188188

189189
dispatch_cache = Dict() # to ensure that the trait-dispatch function is defined only once per pair
190190
let
191191
global traitfn
192192
function traitfn(tfn, cur_module)
193193
# Need
194-
# f{X,Y}(x::X,Y::Y) = f(trait(Tr1{X,Y}), x, y)
194+
# f(x::X,Y::Y) where {X,Y} = f(trait(Tr1{X,Y}), x, y)
195195
# f(::False, x, y)= ...
196196
if tfn.head==:macrocall
197197
hasmac = true
@@ -213,27 +213,53 @@ let
213213
# trait: expression of the trait
214214
# trait0: expression of the trait without any gensym'ed symbols
215215
#
216+
# oldfn_syntax: set to true if the old parametric syntax is used
217+
#
216218
# (The variables without gensym'ed symbols are mostly used for the key of the dispatch cache)
217219

218220
fhead = tfn.args[1]
219221
fbody = tfn.args[2]
220222

221-
fname, paras, args0, kwargs = @match fhead begin
222-
f_{paras__}(args0__;kwargs__) => (f,paras,args0,kwargs)
223-
f_(args0__; kwargs__) => (f,[],args0,kwargs)
224-
f_{paras__}(args0__) => (f,paras,args0,[])
225-
f_(args0__) => (f,[],args0,[])
223+
# TODO 1.0: remove
224+
out = @match fhead begin
225+
f_{paras__}(args0__;kwargs__) => (f,paras,args0,kwargs,true)
226+
f_(args0__; kwargs__) => (f,[],args0,kwargs,false)
227+
f_{paras__}(args0__) => (f,paras,args0,[],true)
228+
f_(args0__) => (f,[],args0,[],false)
229+
f_(args0__; kwargs__) where paras__ => (f,paras,args0,kwargs,false)
230+
f_(args0__) where paras__ => (f,paras,args0,[],false)
231+
end
232+
if out==nothing
233+
error("Could not parse function-head: $fhead. Note that several `where` are not supported.")
226234
end
235+
fname, paras, args0, kwargs, oldfn_syntax = out
227236
haskwargs = length(kwargs)>0
228-
if length(paras)>0 && isa(paras[1],Expr) && paras[1].head==:parameters
229-
# this is a Traits.jl style function
230-
trait = paras[1].args[1]
237+
# extract parameters and traits from paras and/or args0
238+
239+
if length(paras)==0
240+
maybe_traitor = true
241+
else
242+
if paras[1] isa Expr && paras[1].head==:parameters
243+
maybe_traitor = false
244+
length(paras)<2 && error("Cannot parse function parameters: $para")
245+
typs = paras[2:end]
246+
trait = paras[1].args[1]
247+
elseif paras[1] isa Expr && paras[1].head==:bracescat
248+
maybe_traitor = false
249+
length(paras)!=1 && error("Cannot parse function parameters: $para")
250+
typs = paras[1].args[1:1]
251+
trait = paras[1].args[2]
252+
else
253+
maybe_traitor = true
254+
# the processing happens below
255+
end
256+
end
257+
if !maybe_traitor
231258
trait0 = trait # without gensym'ed types, here identical
232-
typs = paras[2:end]
233259
typs0 = typs # without gensym'ed types, here identical
234260
args1 = insertdummy(args0)
235-
else
236-
# This is a Traitor.jl style function. Change it into a Traits.jl function.
261+
else # This is a Traitor.jl style function (or invalid).
262+
# Change paras & args0 into a Traits.jl function.
237263
# Find the traitor:
238264
typs0 = deepcopy(paras) # without gensym'ed types
239265
typs = paras
@@ -293,34 +319,64 @@ let
293319
else
294320
pushloc = poploc = nothing
295321
end
296-
# create the function containing the logic
322+
# create the function containing the logic. Do it separately if old-school parametric functions
323+
# (TODO: delete the oldfn_syntax branches in Julia 1.0 (or 0.7 already?))
297324
retsym = gensym()
298325
if hasmac
299-
fn = :(@dummy $fname{$(typs...)}($val, $(strip_kw(args1)...); $(kwargs...)) = ($pushloc; $retsym = $fbody; $poploc; $retsym))
326+
fn = if oldfn_syntax
327+
:(@dummy $fname{$(typs...)}($val, $(strip_kw(args1)...); $(kwargs...)) = ($pushloc; $retsym = $fbody; $poploc; $retsym))
328+
else
329+
:(@dummy $fname($val, $(strip_kw(args1)...); $(kwargs...)) where {$(typs...)} = ($pushloc; $retsym = $fbody; $poploc; $retsym))
330+
end
300331
fn.args[1] = mac # replace @dummy
301332
else
302-
fn = :($fname{$(typs...)}($val, $(strip_kw(args1)...); $(kwargs...)) = ($pushloc; $retsym = $fbody; $poploc; $retsym))
333+
fn = if oldfn_syntax
334+
:($fname{$(typs...)}($val, $(strip_kw(args1)...); $(kwargs...)) = ($pushloc; $retsym = $fbody; $poploc; $retsym))
335+
else
336+
:($fname($val, $(strip_kw(args1)...); $(kwargs...)) where {$(typs...)} = ($pushloc; $retsym = $fbody; $poploc; $retsym))
337+
end
303338
end
304339
# Create the trait dispatch function
305340
ex = fn
306341
key = (cur_module, fname, typs0, strip_kw(args0), trait0_opposite)
307342
if !(key keys(dispatch_cache)) # define trait dispatch function
308343
if !haskwargs
309-
ex = quote
310-
$fname{$(typs...)}($(args1...)) = (Base.@_inline_meta(); $fname($curmod.trait($trait),
311-
$(strip_tpara(strip_kw(args1))...)
312-
)
313-
)
314-
$ex
344+
ex = if oldfn_syntax
345+
quote
346+
$fname{$(typs...)}($(args1...)) = (Base.@_inline_meta(); $fname($curmod.trait($trait),
347+
$(strip_tpara(strip_kw(args1))...)
348+
)
349+
)
350+
$ex
351+
end
352+
else
353+
quote
354+
$fname($(args1...)) where {$(typs...)} = (Base.@_inline_meta(); $fname($curmod.trait($trait),
355+
$(strip_tpara(strip_kw(args1))...)
356+
)
357+
)
358+
$ex
359+
end
315360
end
316361
else
317-
ex = quote
318-
$fname{$(typs...)}($(args1...);kwargs...) = (Base.@_inline_meta(); $fname($curmod.trait($trait),
319-
$(strip_tpara(strip_kw(args1))...);
320-
kwargs...
321-
)
322-
)
323-
$ex
362+
ex = if oldfn_syntax
363+
quote
364+
$fname{$(typs...)}($(args1...);kwargs...) = (Base.@_inline_meta(); $fname($curmod.trait($trait),
365+
$(strip_tpara(strip_kw(args1))...);
366+
kwargs...
367+
)
368+
)
369+
$ex
370+
end
371+
else
372+
quote
373+
$fname($(args1...);kwargs...) where {$(typs...)} = (Base.@_inline_meta(); $fname($curmod.trait($trait),
374+
$(strip_tpara(strip_kw(args1))...);
375+
kwargs...
376+
)
377+
)
378+
$ex
379+
end
324380
end
325381
end
326382
dispatch_cache[key] = (haskwargs, args0)
@@ -347,14 +403,14 @@ end
347403
Defines a function dispatching on a trait. Examples:
348404
349405
```julia
350-
@traitfn f{X; Tr1{X}}(x::X,y) = ...
351-
@traitfn f{X; !Tr1{X}}(x::X,y) = ...
406+
@traitfn f(x::X,y) where {X; Tr1{X}} = ...
407+
@traitfn f(x::X,y) where {X; !Tr1{X}} = ...
352408
353-
@traitfn f{X,Y; Tr2{X,Y}}(x::X,y::Y) = ...
354-
@traitfn f{X,Y; !Tr2{X,Y}}(x::X,y::Y) = ...
409+
@traitfn f(x::X,y::Y) where {X,Y; Tr2{X,Y}} = ...
410+
@traitfn f(x::X,y::Y) where {X,Y; !Tr2{X,Y}} = ...
355411
```
356412
357-
Note that the second example is just syntax sugar for `@traitfn f{X,Y; Not{Tr1{X,Y}}}(x::X,y::Y) = ...`.
413+
Note that the second example is just syntax sugar for `@traitfn f(x::X,y::Y) where {X,Y; Not{Tr1{X,Y}}} = ...`.
358414
"""
359415
macro traitfn(tfn)
360416
@static if isdefined(Base, Symbol("@__MODULE__"))

src/base-traits.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ Base.@deprecate_binding IsFastLinearIndex IsIndexLinear
6868

6969
"Trait of all iterator types"
7070
@traitdef IsIterator{X}
71-
@generated function SimpleTraits.trait{X}(::Type{IsIterator{X}})
71+
@generated function SimpleTraits.trait(::Type{IsIterator{X}}) where {X}
7272
hasmethod(start, Tuple{X}) ? :(IsIterator{X}) : :(Not{IsIterator{X}})
7373
end
7474

test/backtraces.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
@traitimpl BacktraceTr{Integer}
55

6-
@traitfn foo{X; BacktraceTr{X}}(A::X) = backtrace()
7-
@traitfn foo{X; !BacktraceTr{X}}(A::X) = backtrace()
6+
@traitfn foo(A::X) where {X; BacktraceTr{X}} = backtrace()
7+
@traitfn foo(A::X) where {X; !BacktraceTr{X}} = backtrace()
88
###### END: LINE NUMBER SENSITIVITY ##########
99
# (the tests below depend on the particular line numbers in the code above)
1010

0 commit comments

Comments
 (0)