Skip to content
This repository was archived by the owner on Feb 7, 2019. It is now read-only.

Commit fa5b29e

Browse files
committed
Merge pull request #10 from mauro3/m3/para-methods
Major overhaul of methods checking in `@traitsdef` Fixes #2, #8
2 parents e155b09 + 096c551 commit fa5b29e

19 files changed

+1157
-630
lines changed

NEWS.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
Feature updates for Traits.jl
22
=============================
33

4+
14 April 2015
5+
-------------
6+
7+
Major overhaul of `istrait` function. Now it uses custom-programmed
8+
checks instead of `method_exists`. This allows parameterized methods
9+
to be used both in the `@traitdef` and in the trait implementation.
10+
This may have pushed Traits.jl into usable terrain. Closes issues #2
11+
and #8.
12+
413
30 March 2015
514
-------------
615

README.md

+55-65
Original file line numberDiff line numberDiff line change
@@ -26,57 +26,67 @@ mentioned in (1). But Julia does not support (2) or (3) yet. (2) is
2626
fairly easy to implement. However, dispatch on a "contract" is not
2727
easily possible, but Tim Holy recently came up with
2828
[a trick](https://github.com/JuliaLang/julia/issues/2345#issuecomment-54537633).
29-
The cool thing about that trick is that the code for a trait-dispatch
30-
function should be identical to a duck-typed function, i.e. there is
31-
no loss in performance.
29+
The cool thing about that trick is that the generated machine-code for
30+
a trait-dispatch function should be identical to a duck-typed
31+
function, i.e. there is no loss in performance.
3232

3333
`Traits.jl` adds those kind of traits to Julia, using Tim's trick
34-
combined with stagedfunctions. See also the Julia-issue
34+
combined with stagedfunctions and extensive facilities to define
35+
traits. See also the Julia-issue
3536
[#6975](https://github.com/JuliaLang/julia/issues/6975) concerning
3637
interfaces/traits.
3738

38-
Example:
39+
Example `examples/ex1.jl`:
3940
```julia
4041
using Traits
41-
# Check Cmp-trait (comparison) which is implemented in src/commontraits.jl
42-
@assert istrait(Cmp{Int,Float64})
43-
@assert istrait(Cmp{Int,String})==false
42+
# Check Cmp-trait (comparison) which is implemented in Traits.jl/src/commontraits.jl
43+
@assert istrait(Cmp{Int,Float64}) # Int and Float64 can be compared
44+
@assert istrait(Cmp{Int,String})==false # Int and String cannot be compared
4445

4546
# make a new trait and add a type to it:
4647
@traitdef MyTr{X,Y} begin
47-
foobar(X,Y) -> Bool
48+
foobar(X,Y) -> Bool # All type-tuples for which there is a method foo
49+
# with that signature belong to MyTr
4850
end
4951
type A
5052
a::Int
5153
end
52-
foobar(a::A, b::A) = a.a==b.a
53-
@assert istrait(MyTr{A,A}) # true
54+
@assert istrait(MyTr{A,A})==false # foobar not implement yet
55+
foobar(a::A, b::A) = a.a==b.a # implement it
56+
@assert istrait(MyTr{A,A}) # voila!
5457
@assert istrait(MyTr{Int,Int})==false
5558

5659
# make a function which dispatches on traits:
5760
@traitfn ft1{X,Y; Cmp{X,Y}}(x::X,y::Y) = x>y ? 5 : 6
5861
@traitfn ft1{X,Y; MyTr{X,Y}}(x::X,y::Y) = foobar(x,y) ? -99 : -999
5962

60-
ft1(4,5) # 6
61-
ft1(A(5), A(6)) # -999
63+
ft1(4,5) # ==6 i.e. dispatches to first definition
64+
ft1(A(5), A(6)) # ==-999 i.e. dispatches to second definition
6265

6366
ft1("asdf", 6)
6467
# -> ERROR: TraitException("No matching trait found for function ft1")
6568
```
6669

67-
This is an experimental package and I will not try to keep backwards
68-
compatibility as I move on. But please give it a try in your code and
69-
give feedback. I will try to document the new features in [NEWS](NEWS.md).
70+
# Package status
71+
72+
New features are documented in [NEWS](NEWS.md) as they are added. I
73+
keep some notes, musings and plans in [dev_notes.md](docs/dev_notes.md).
74+
75+
This is a fairly experimental package and I will not try to keep
76+
backwards compatibility as I move on. Please try it out and give me
77+
feedback, issues or pull requests!
7078

7179
# Syntax
72-
(source in `examples/ex2.jl`)
80+
The source of below examples is in `examples/ex2.jl`. Most of the
81+
important functions are documented and will respond to `?` in the REPL.
7382

74-
Trait definition:
83+
Trait definition (for details see [traitdef.md](docs/traitdef.md)):
7584
```julia
7685
using Traits
7786
# simple
7887
@traitdef Tr1{X} begin
79-
fun1(X) -> Number
88+
fun1(X) -> Number # this means a method with signature fun1(::X)
89+
# returning a Number
8090
end
8191
@traitdef Tr2{X,Y} begin
8292
fun2(X,Y) -> Number
@@ -101,13 +111,12 @@ end
101111
end
102112
```
103113
Note that return-type checking is quite experimental. It can be
104-
turned off by defining `Main.Traits_check_return_types=false` before
105-
`using Traits`.
114+
turned off with `check_return_types(false)`.
106115

107116

108-
Trait implementation:
117+
Trait implementation and checking with `istrait`:
109118
```julia
110-
# manual, i.e. just define the functions
119+
# manual definiton, i.e. just define the functions
111120
fun1(x::Int) = 5x
112121
@assert istrait(Tr1{Int})
113122

@@ -159,45 +168,50 @@ catch e
159168
end
160169
```
161170

162-
Trait functions & dispatch:
171+
Trait functions & dispatch (for details see [traitfns.md](docs/traitfns.md)):
163172
```julia
164-
@traitfn tf1{X, Y; Tr1{X}, Tr1{Y}}(a::X, b::Y) = fun1(a) + fun1(b)
165-
@traitfn tf1{X, Y; Tr2{X,Y}}(a::X, b::Y) = fun2(a,b)
173+
@traitfn tf1{X, Y; Tr1{X}, Tr1{Y}}(a::X, b::Y) = fun1(a) + fun1(b) # I
174+
@traitfn tf1{X, Y; Tr1{X}, Tr1{Y}}(a::X, b::Y, c::Int) = fun1(a) + fun1(b) + c # II
175+
@traitfn tf1{X, Y; Tr2{X,Y}}(a::X, b::Y) = fun2(a,b) # III
166176
# Note that all the type-parameters are in the {} and that all
167177
# arguments need a type parameter (a limitation of the
168-
# macro-parser). Bad examples are:
178+
# macro-parser). This doesn't work:
169179
#
170180
# julia> @traitfn ttt1{X, Y; Tr1{X}, Tr1{Y}}(a::X, b::Y, c) = fun1(a) + fun1(b) + c
171181
# ERROR: type Symbol has no field args
172182
#
173-
# julia> @traitfn ttt1{X, Y; Tr1{X}, Tr1{Y}}(a::X, b::Y, c::Int) = fun1(a) + fun1(b) + c
174-
# ERROR: X3 not defined
175-
#
176183
# But this works:
177184
#
178185
# julia> @traitfn ttt1{X, Y, Z; Tr1{X}, Tr1{Y}}(a::X, b::Y, c::Z) = fun1(a) + fun1(b) + c
179186
# ttt1 (generic function with 6 methods)
180187

181188

182189
# tf1 now dispatches on traits
183-
tf1(5.,6.) # -> 77 (Float64 is part of Tr1 but not Tr2)
190+
@assert tf1(5.,6.)==77. # -> 77 ; dispatches to I because istrait(Tr1{Float64})
191+
# but not istrait(Tr2{Float64,Float64})
192+
@assert tf1(5.,6.,77)==154. # -> 154. ; dispatches to II because of the extra argument
184193

185194
# Errors because of dispatch ambiguity:
186195
try
187-
tf1(5,6) # Int is part of Tr1{Int} and Tr2{Int, Int}
196+
tf1(5,6) # istrait(Tr1{Int}) and istrait(Tr2{Int,Int}) are both true!
188197
catch e
189198
println(e)
190199
end
191200

192-
# adding a type to Tr1 will make it work with tf1:
201+
# Implementing Tr1 for a type will make it work with tf1:
193202
type MyType
194203
a::Int
195204
end
205+
try
206+
tf1(MyType(8), 9) # not implemented yet
207+
catch e
208+
println(e)
209+
end
196210
@traitimpl Tr1{MyType} begin
197211
fun1(x::MyType) = x.a+9
198212
end
199213

200-
tf1(MyType(8), 9) # -> 62
214+
@assert tf1(MyType(8), 9)==62 # -> 62 ; dispatches to I
201215
```
202216

203217
# Generated code
@@ -221,7 +235,7 @@ top:
221235
```
222236

223237
However, for more complicated functions code is not quite the same,
224-
see `test/traitdispatch.jl`.
238+
see `test/perf/perf.jl`.
225239

226240
# Inner workings
227241

@@ -248,12 +262,12 @@ In Julia dispatch works on types, to extend this to traits I use
248262
His trick uses a function to check whether its input types satisfy
249263
certain conditions (only dependent on their type) and returns one type
250264
or another depending on the outcome. That check-function is then used
251-
for dispatch in another function. Example of Tim's trick:
265+
for dispatch in another function. Example of Tim's trick (`examples/ex_tims_traits.jl`):
252266
```julia
253267
type Trait1 end
254268
type Trait2 end
255269
type Trait3 end
256-
# now define function
270+
# now define function f which should dispatch on those traits
257271
f(x,y) = _f(x,y, checkfn(x,y))
258272
_f(x,y,::Type{Trait1}) = x+y
259273
_f(x,y,::Type{Trait2}) = x-y
@@ -265,12 +279,12 @@ checkfn(::Int, ::Int) = Trait1
265279
checkfn(::Int, ::FloatingPoint) = Trait2
266280
checkfn(::FloatingPoint, ::FloatingPoint) = Trait3
267281
# use
268-
f(3,4) # 7
269-
f(3,4.) # -1.0
270-
f(3.,4.) # 12.0
282+
@assert f(3,4)==7 # Trait1
283+
@assert f(3,4.)==-1.0 # Trait2
284+
@assert f(3.,4.)==12.0 # Trait3
271285
# add another type-tuple to Trait3
272286
checkfn(::String, ::String) = Trait3
273-
f("Lorem ", "Ipsum") # "Lorem Ipsum"
287+
@assert f("Lorem ", "Ipsum")=="Lorem Ipsum"
274288
```
275289

276290
What does this add compared to what we had before with usual dispatch?
@@ -334,30 +348,6 @@ trait-hierarchies into account. Although, note that it is easily
334348
possible to have unsolvable ambiguities with trait-dispatch as traits
335349
do not have a strict hierarchy like types.
336350

337-
# To ponder
338-
339-
- For many "traits" in Julia, only a few functions need to be
340-
implemented to provide many more. For example for comparison only
341-
`isless` and `==` need to be implemented to automatically get `>`,
342-
`<`, `>=`, `<=`. It would be nice to somehow specify or query those
343-
automatic functions.
344-
345-
- Are there better ways for trait-dispatch?
346-
347-
- Sometimes it would be good to get at type parameters, for instance
348-
for Arrays and the like:
349-
```julia
350-
@traitdef Indexable{X{Y}} begin
351-
getindex(X, Any) -> Y
352-
setindex!(X, Y, Any) -> X
353-
end
354-
```
355-
This problem is similar to triangular dispatch and may be solved
356-
by: https://github.com/JuliaLang/julia/issues/6984#issuecomment-49751358
357-
358-
# Issues
359-
360-
361351
# Other trait implementations
362352

363353
See the Julia-issue

dev_notes.md renamed to docs/dev_notes.md

+29
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,35 @@
11
Development notes
22
=================
33

4+
Planned work
5+
------------
6+
7+
- [ ] Making it easy to specify traits for datatype, see issue #1
8+
- [ ] improve dispatch of traitfn, see issue #5
9+
10+
To ponder
11+
---------
12+
13+
- For many "traits" in Julia, only a few functions need to be
14+
implemented to provide many more. For example for comparison only
15+
`isless` and `==` need to be implemented to automatically get `>`,
16+
`<`, `>=`, `<=`. It would be nice to somehow specify or query those
17+
automatic functions.
18+
19+
- Are there better ways for trait-dispatch?
20+
21+
- Sometimes it would be good to get at type parameters, for instance
22+
for Arrays and the like:
23+
```julia
24+
@traitdef Indexable{X{Y}} begin
25+
getindex(X, Any) -> Y
26+
setindex!(X, Y, Any) -> X
27+
end
28+
```
29+
This problem is similar to triangular dispatch and may be solved
30+
by: https://github.com/JuliaLang/julia/issues/6984#issuecomment-49751358
31+
32+
433
Road blocks
534
-----------
635

0 commit comments

Comments
 (0)