4

According to the changelog for Julia 0.5,

Each function and closure now has its own type.

Does this mean it's now possible to provide more detailed information to a higher-order function, e.g. foo(bar :: Function{Float64}) = ..., as opposed to pre-0.5, where the type of bar could not be more specific than Function?

If so, what is the correct way of doing so? If not, what is the actual import of this change, beyond the compiler being able to better optimise the generated code? TIA.

Arets Paeglis
  • 3,856
  • 4
  • 35
  • 44
  • Turns out there is a partial solution to this problem in the form of [type-safe function wrappers](https://github.com/yuyichao/FunctionWrappers.jl). – Arets Paeglis Oct 12 '16 at 18:55

1 Answers1

5

Not really no. I see what you are getting at, and I like it, but it is not possible. (Certainly not currently, possibly not ever. Maybe oneday using traits.)

Lets look at an example: foo and bar

julia> foo(x::String) = println(x)
foo (generic function with 1 method)

julia> foo(x::Int64) = println(x+1)
foo (generic function with 2 methods)

julia> bar(x...) = println(x)
bar (generic function with 1 method)

What is the type hierarchy for foo?

julia> typeof(foo)
#foo

julia> supertype(typeof(foo))
Function

julia> supertype(supertype(typeof(foo)))
Any

So we see that they type of the foo function is a #foo which is a subtype of Function. Note that # means this is a generated name, you can't put hash's in names when just writing code, but the julia compiler (using the term loosely) can.

Why isn't its super-supertype more specific, than just function? What would it be? Function{Int64} or Function{String}? Functions in julia, do not have type-signatures, Methods do. A function is just a name for multiple dispatch, a method is actually what is dispatched to. Roughly speaking the function name says which table should i look in, and the types of the arguments (ie it's type signature) are the key for looking up, in that table. The method itself is what is returned, using that key.

So lets continue with our example and see what we can do:

julia> dothing(f::typeof(foo)) = f(rand([randstring(), rand(Int64)]))
dothing (generic function with 1 method)

julia> dothing(foo)
3139374763834167054

julia> dothing(foo)
Ed2kNGrd


julia> dothing(bar)
ERROR: MethodError: no method matching dothing(::#bar)
Closest candidates are:
  dothing(::#foo) at REPL[11]:1

So we have sucessfully restricted dothing, to only take a #foo as its arguement. See it throws an error when you give it a #bar. This isn't really useful since the foo function is the only thing of type #foo.

We could use a Union though:

julia> dootherthing(f::Union{typeof(foo),typeof(bar)}) = f(rand([randstring(), rand(Int64)]))
dootherthing (generic function with 1 method)

julia> dootherthing(foo)
9107791406050657562

julia> dootherthing(foo)
SmB2Xmw8

julia> dootherthing(bar)
("1IpZIMnx",)

julia> dootherthing(bar)
(-6356894350805213697,)


julia> dootherthing(str)
ERROR: UndefVarError: str not defined

julia> dootherthing(string)
ERROR: MethodError: no method matching dootherthing(::Base.#string)
Closest candidates are:
  dootherthing(::Union{#bar,#foo}) at REPL[19]:1

dootherthing accepts a #foo or a #bar. Either function works.

This has limited applications, as a whitelist.

Frames Catherine White
  • 27,368
  • 21
  • 87
  • 137