Typeof a function

What does the output mean? #1, #2, and couting.

julia> typeof((a::Int64) -> a + 1)
var"#1#2"
julia> typeof((a::Int64) -> a + 1)
var"#3#4"

This is a gensymed name.
It means nothing.
It is just a unique name for an otherwise anonymous function, that is certain not to clash,

1 Like

What is the type of a anonymous function? Maybe Function? I have failed to find relative information in the documentation.

The type of an anonymous function depends on the anonymous function. A location that defines an anonymous function generates a new type with a name generated with help of the gensym function.

1 Like

Do you mean that the type of an anonymous function depends on where it is defined (line number, or something else) ?

Any documentation for this?

The name of the type depends on how many times the function gensym has been called before it. Several things in the language use the gensym function to generate unique symbols.

The fields within the generated type on the other hand depend on what variables were closed over by the anonymous function.

So, the type of each anonymous function is an unique type represented by an unique symbol which itself does not carry any information on the type of the function.

I am curious that if this is just a temporary implementation choice, or a rational design decision like using * for concatenating string.

This is not unique to anonymous functions, every function has its own unique type:

julia> typeof(sin)
typeof(sin)

It’s just that anyonymous functions, as the name suggests, don’t have a name, so they get a gensymmed one.

3 Likes

In julia, each function has its own type and subtypes Function. For example, the type of the sin function is typeof(sin) (for lack of a better name).

Now, anonymous functions don’t have a name, but we still need some somewhat human readable identifier to refer to - that’s where that generated name comes in. Notably, these anonymous functions also each have their own type, just like regular named functions.

2 Likes

If you have two anonymous functions with the exact same syntax, how would you distinguish them? Julia doesn’t have argument types and return types of functions as part of the type of a function, like Haskell for example (which also wouldn’t help, since we assumed syntax was the exact same and the functions can still be distinct objects in spite of being functionally identical). The only way to make it clear to human readers is to give them a generated name internally.

1 Like

This looks like that typeof is not evaluated at all. :grinning:

Roughly, typeof(sin) is the same as typeof(cos). Right?

No. typeof(sin) !== typeof(cos). They only share a common supertype, Function.

The type of any function f is just written/printed like typeof(f) to make it clear we’re talking about the type of f and not f itself. You can pass functions around like any other object, after all.

1 Like

Julia redefined the type of functions?

Would you tell me where I can find relative information in Julia docs?

Nothing is redefined here. Maybe this StackOverflow answer helps:

https://stackoverflow.com/questions/52351852/puzzling-results-for-julia-typeof

1 Like

No, julia didn’t redefine anything - that’s literally the type of that specific function.

There’s the abstract type Function and its concrete subtypes, e.g. typeof(sin), typeof(cos), typeof(tan) etc. which are all distinct types, each with a singleton instance (in these cases, sin for typeof(sin), cos for typeof(cos)…)

1 Like

Now IMHO, I have got a better understanding of typeof functions. If anything wrong, please correct me.

  1. Why I said that Julia redefined type of functions?

    Take sin and cos for examples. Roughly, let’s do middle school maths, then, they both map a real number to another real number between -1 and 1:

    sin: R -> R
    cos: R -> R
    

    In Haskell, types are just as what we do in maths:

    Prelude> :t sin
    sin :: Floating a => a -> a
    Prelude> :t cos
    cos :: Floating a => a -> a
    

    In C/C++, we can say that they are treated as sharing the same type:

    typedef double (* f_t)(double x);
    f_t f1 = sin;
    f_t f2 = cos;
    

    While in Julia, it is not the case. So I said that type is redefined.

  2. Why the redefinition?

    In Julia, method multiple dispatch is type-based. In below answer, valued-based (let’s say that mean/sum are values) multiple dispatch is achieved with the help of typeof. This may be one of the reasons, if not the only.

    Puzzling results for Julia typeof - Stack Overflow

Julia, unlike Haskell, doesn’t have function type. The input and output types of a function is not part of the typing system (they are known to compiler for compilation purpose). As others have said here, the type of a function f is simply typeof(f) and that’s the end of the story. There’s no information regarding the possible or actual input types and output types of f here.

1 Like

Ah, I think I see where the confusion comes from! In most other programming languages, a “function” is usually the name and the associated argument types. In julia, that’s not the case - a function is just a name (though it’s usually associated with a namespace as well, we can ignore that here for now). As soon as you associate specific argument types (i.e. a signature) to a function, you have a method of that function.

For example, consider the sin example. In Base julia, sin is a single function, but has many methods:

julia> methods(sin)                     
# 13 methods for generic function "sin":

one of those handles arguments of type Float64:

julia> sin(1.0)   
0.8414709848078965

another handles complex arguments:

julia> sin(1im)           
0.0 + 1.1752011936438014im

These are still the same function though! In julia a “function” is mostly a common name for a certain operation, paired with specializations for specific argument types we call “method”. Crucially, the function alone does not give julia any knowledge about its argument types or the type of the object that will be returned from it. That is method specific, not function specific (and we can’t pass around specific methods of a function because we don’t know which method will be called until it is called).


Now, since we want to possibly pass sin (or any other function, really) around and may want to specialize how a function works based on the function it gets passed in, we have to make each function have its own type. This will be a little clearer printed in 1.7:

julia> typeof(sin)                                               
typeof(sin) (singleton type of function sin, subtype of Function)

Why is this useful? Well, sometimes we can take advantage of properties (e.g. commutativity) of certain functions to do something smarter. reduce(+, array) for example does Kahan summation on arrays of floating point arguments, which reduces accumulation error. If reduce couldn’t distinguish + from * (or any other binary operation, really), it wouldn’t necessarily be allowed to do that.

3 Likes

Don’t you mean pairwise summation? That’s what sum does, at least.