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"
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 gensym
ed name.
It means nothing.
It is just a unique name for an otherwise anonymous function, that is certain not to clash,
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.
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.
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.
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.
This looks like that typeof
is not evaluated at all.
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.
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
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)
…)
Now IMHO, I have got a better understanding of typeof
functions. If anything wrong, please correct me.
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.
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.
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.
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.
Don’t you mean pairwise summation? That’s what sum
does, at least.