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

Commit e155b09

Browse files
committed
added some development notes.
1 parent d0321b2 commit e155b09

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed

Diff for: dev_notes.md

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
Development notes
2+
=================
3+
4+
Road blocks
5+
-----------
6+
7+
### Expressiveness of types and how to describe method signatures
8+
9+
see branch m3/para-methods
10+
11+
The definition of traits hinges on being able to specify the types of
12+
function arguments. This is currently not possible because of
13+
parameterized functions:
14+
```julia
15+
f{T}(a::Int, b::T, c::T) = ...
16+
```
17+
18+
The signature of the function cannot be captured in a type-tuple,
19+
instead it needs something like a constrained tuple:
20+
```julia
21+
{T}(a::Int, b::T, c::T)
22+
```
23+
24+
Specifying a trait involves specifying method signatures
25+
```julia
26+
@traitdef Tr1{X} begin
27+
f{T}(a::X, b::T, c::T) = ...
28+
end
29+
```
30+
31+
which then need to be compared to the signatures of the method of `f`
32+
for a specific `X`: for example, `istrait(Tr1{Int})`
33+
would need to check whether at least one method signature `sig` of `f`
34+
satisfies `tsig<:sig` where `tsig={T}(a::Int, b::T, c::T)` (i.e. for
35+
all allowed types in the trait there is a method which fits). Syntax
36+
for this is being discussed in issue
37+
[#6984](https://github.com/JuliaLang/julia/issues/6984#issuecomment-49804492).
38+
39+
Actually, I'm not sure whether `tsig<:sig` is right. For above `Tr1`,
40+
and a method with `sig=(a::Int, b, c)`, then the set of allowed type
41+
signatures by `Tr1` is a subset of `sig`, thus `tsig={T}(a::Int, b::T,
42+
c::T)<:(a::Int, b, c)=sig`. However, I'm not sure that is what should
43+
be intended by the traitdef of `Tr1`. Conversely, for non-parametric
44+
types, say `tsig=(a::Integer)` & `sig=(a::Real)` means `tsig<:sig`,
45+
which should mean that a trait would be satisfied.
46+
47+
So, I think, for a trait-signature to be satisfied the following
48+
condition need to hold:
49+
50+
- `tsig<:sig` for just the types themselves (sans parametric constraints)
51+
- `tsig==sig` for the parametric constraints. I.e. the constraints on `sig`
52+
need to be identical to `tsig`.
53+
54+
The reason for the second rule is: If the constraints are weaker on
55+
`tsig` then on `sig`, it can happen that a argument tuple is not
56+
accepted by the method with `sig` even though it would be on a method
57+
with `tsig`. Conversely, if the constrains are weaker on
58+
`sig` then on `tsig`, then not all the trait-constraints are
59+
fulfilled and thus the trait is not fulfilled.
60+
61+
Let's call this `tsig<<:sig`.
62+
63+
Compare this to ordinary method dispatch:
64+
```
65+
julia> g(a,b) = 1
66+
g (generic function with 1 method)
67+
68+
julia> g{T}(a::T,b::T) = 2
69+
g (generic function with 2 methods)
70+
71+
julia> g(5,6) # here the parametric constrained one gets called, as it is more specific
72+
2
73+
74+
julia> g(5,6.)
75+
1
76+
```
77+
78+
This means `{I}(I, I)<:(Any, Any)` but not `(Any, Any)<:{I}(I, I)`,
79+
which does not help for the traits-problem... Having a trait:
80+
81+
```julia
82+
@traidef Tr{X} begin
83+
g{T<:X}(T, T)
84+
end
85+
```
86+
87+
I think should mean that defining `g{T}(a::T,b::T) = 2` fulfils `Tr` for all
88+
types but defining `g(a,b) = 1` does not.
89+
90+
How about
91+
```Julia
92+
@traidef Tr{X} begin
93+
g{T<:X}(T, T, Integer)
94+
end
95+
```
96+
would `g{T<:Integer}(T, T, T)` fulfil `Tr{Integer}`? Probably yes as
97+
the parametric constraints are stronger than needed.
98+
99+
And what about the special-casing of dispatch of type parameters in
100+
invariant positions?
101+
```
102+
f{T}(y::T, x::A{T}) = 1 # does match f(1, A{Number}())
103+
```
104+
I think there is nothing special about it for traits?
105+
106+
107+
108+
#### Ideas
109+
Instead of using `method_exists` compare a fake method with the
110+
methods at hand. Also use a Type-variant method to encode the return
111+
type:
112+
113+
Methods-cache may do something similar.
114+
Base._methods (video at 28min)
115+
116+
#### References:
117+
- https://github.com/JuliaLang/julia/issues/9043
118+
-[Add syntactic sugar for covariant actual type parameters #6984](https://github.com/JuliaLang/julia/issues/6984)
119+
- [use { } for tuple types? #8470](https://github.com/JuliaLang/julia/issues/8470)
120+
- [WIP: redesign of tuples and tuple types #10380](https://github.com/JuliaLang/julia/pull/10380)
121+
- Jeff's talk: "Introduction to Julia Internals"
122+
123+
124+
### Return types
125+
126+
[Variance of function types](https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29#Function_types)
127+
suggests that "it is safe to substitute a function `f` instead of a
128+
function `g` if `f` accepts a more general type of arguments and
129+
returns a more specific type than `g`." I.e. the `->` type constructor
130+
is contravariant in the input type and covariant in the output type.
131+
132+
However, when using muliple dispatch,
133+
[wikipedia says](https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29#Avoiding_the_need_for_covariant_argument_types):
134+
"... types used for runtime method selection are covariant while types
135+
not used for runtime method selection of the method are
136+
contravariant." So, bottom line is that Julia's generic functions are
137+
*covariant* in both *argument types* and *return types*:
138+
```
139+
(X1,Y1)->Z1 <: (X2,Y2)->Z2
140+
=>
141+
(X1,Y1,Z1) <: (X2,Y2,Z2)
142+
```
143+
144+
This means, that return types can be analysed in the same way as
145+
argument types, at least in principle. However, the devil may be in
146+
the details as the return type of function can only be queried with
147+
`Base.return_types`. Example:
148+
```julia
149+
@traitdef Tr{X} begin
150+
g{T<:X}(::T, ::T) = T
151+
end
152+
g(::Int, ::Int) = Int
153+
istrait(Tr{Int}) # == true
154+
```
155+
156+
157+
#### References:
158+
159+
- [adding a `@typeof f(..)`](https://github.com/JuliaLang/julia/issues/8027#issuecomment-52519612)
160+
- [Wikipedia about variance](https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29)
161+
162+
Monotonic return types:
163+
164+
- [julia-dev thread](https://groups.google.com/forum/#!msg/julia-dev/OGTUtAeozVw/cRQyuJQSFFgJ)
165+
- [return type declarations #1090](https://github.com/JuliaLang/julia/issues/1090#issuecomment-35642896)
166+
167+
Ideas
168+
-----
169+
### Merging implicit interfaces specified on types with traits
170+
171+
see branch m3/type-traits
172+
173+
How can traits be used for what types are used? Say what is the
174+
interface for an `AbstractArray`? Say it is made up of a bunch of
175+
single parameter traits, then `AbstractArray => BunchOfTraits{AbstractArray}`.
176+
However, the converse is not necessarily true, namely when
177+
`BunchOfTraits{X}` does not imply `X==AbstractArray`.
178+
179+
References:
180+
181+
- [Tim](https://github.com/JuliaLang/julia/pull/10458#issuecomment-77957672)
182+
about ambiguities and having general methods.
183+
- [PR 10312](https://github.com/JuliaLang/julia/pull/10312) case about duck-typing
184+
maps and such as now many things are callable
185+
- [Issue #5](https://github.com/JuliaLang/julia/issues/5#issuecomment-37901282)
186+
old discussion on multiple inheritance
187+
- there is one discussion about the interface for AbstractArray
188+
[#10064](https://github.com/JuliaLang/julia/issues/10064) and related
189+
- [#987](https://github.com/JuliaLang/julia/issues/987)
190+
- [#9586](https://github.com/JuliaLang/julia/issues/9586)
191+
192+
193+
- instead of creating a new trait, define one for an abstact type
194+
- check for a concrete subtype will consist of checking that the trait
195+
is fulfilled.
196+
- traitfns can use these type-traits `f{X; X<:AbstractArray}` instead
197+
of `f{X<:AbstractArray}` to check that all of the interfaces of its
198+
abstract super-types are fullfilled.
199+
200+
How is the istrait checking done? Dispatch on type, if a DataType is
201+
found look up the corresponding trait.
202+
203+
How are the traits stored?
204+
- make a normal trait, say AbstractArrayTrait. Insert them into
205+
`Traits` module, that way they will be available irrespective of
206+
where the original type was defined.
207+
208+
209+
Todo:
210+
- [ ] implement issue [#8](https://github.com/mauro3/Traits.jl/issues/8)

0 commit comments

Comments
 (0)