I'm having an issue with type in functions, I've managed to write the minimal code that explains the problem:
immutable Inner{B<:Real, C<:Real}
a::B
c::C
end
immutable Outer{T}
a::T
end
function g(a::Outer{Inner})
println("Naaa")
end
inner = Inner(1, 1)
outer = Outer(inner)
g(outer)
Will lead to the method error
MethodError: no method matching g(::Outer{Inner{Int64,Int64}})
So basically, I don't want to have to say what the types of Inner are, I just want the function to make sure that it's an Outer{Inner} and not Outer{Float64} or something.
Any help would be appreciated
The type Inner{Int64,Int64} is a concrete Inner type and it is not a subtype of
Inner{Real, Real}, since different concrete types of Inner (Int64 or Float64)
can have different representations in memory.
According to the documentation, function g should be defined as:
function g(a::Outer{<:Inner})
println("Naaa")
end
so it can accept all arguments of type Inner.
Some examples, after define g with <::
# -- With Float32 --
julia> innerf32 = Inner(1.0f0, 1.0f0)
Inner{Float32,Float32}(1.0f0, 1.0f0)
julia> outerf32 = Outer(innerf32)
Outer{Inner{Float32,Float32}}(Inner{Float32,Float32}(1.0f0, 1.0f0))
julia> g(outerf32)
Naaa
# -- With Float64 --
julia> innerf64 = Inner(1.0, 1.0)
Inner{Float64,Float64}(1.0, 1.0)
julia> outerf64 = Outer(innerf64)
Outer{Inner{Float64,Float64}}(Inner{Float64,Float64}(1.0, 1.0))
julia> g(outerf64)
Naaa
# -- With Int64 --
julia> inneri64 = Inner(1, 1)
Inner{Int64,Int64}(1, 1)
julia> outeri64 = Outer(inneri64)
Outer{Inner{Int64,Int64}}(Inner{Int64,Int64}(1, 1))
julia> g(outeri64)
Naaa
More details at the documentation: Parametric Composite Type
Update: The way to declare an immutable composite type (as in the original question), have changed to:
struct Inner{B<:Real, C<:Real}
a::B
c::C
end
struct Outer{T}
a::T
end
Furthermore, function g could be declared with a parametric type:
function g(a::T) where T Outer{<:Inner}
println(a)
println(a.a)
println(a.c)
end
And hence, there is no need to create an instance of Outer before calling the function.
julia> ft64 = Inner(1.1, 2.2)
Inner{Float64,Float64}(1.1, 2.2)
julia> g(ft64)
Inner{Float64,Float64}(1.1, 2.2)
1.1
2.2
julia> i64 = Inner(3, 4)
Inner{Int64,Int64}(3, 4)
julia> g(i64)
Inner{Int64,Int64}(3, 4)
3
4
Related
For example
function f(x)
# do something
# then I assigned the outside variable name of 'x' to y
println(y)
end
f(1)
I will get
# something and
1
then,
a = 1
f(a)
I will get
# something and
"a"
Is it possible in julia? If not, how can I get my function operation log?
The most idiomatic way would be to slightly change your interface of f and require a keyword argument:
julia> function f(;kwargs...)
for (k, v) in kwargs
println("$k = $v")
end
end
f (generic function with 1 method)
julia> f(a = 1)
a = 1
Alternatively (short of inspecting stack traces), you need something macro-based:
julia> struct Quot
expr
value
end
julia> macro quot(e)
return :($Quot($(QuoteNode(e)), $e))
end
#quot (macro with 1 method)
julia> function f2(x::Quot)
println(x)
end
f2 (generic function with 1 method)
julia> x = 2
2
julia> f2(#quot x)
Quot(:x, 2)
Depending on what you need a simples macro that dumps function calls that still get executed could be:
macro logs(expr)
#info expr
expr
end
And this can be used as:
julia> a = π/2;
julia> #logs sin(a)
[ Info: sin(a)
1.0
How can I declare a Julia function that returns a function with a specific signature. For example, say I want to return a function that takes an Int and returns an Int:
function buildfunc()::?????
mult(x::Int) = x * 2
return mult
end
What should the question marks be replaced with?
One thing needs to be made clear.
Adding a type declaration on the returned parameter is just an assertion, not part of function definition. To understand what is going on look at the lowered (this is a pre-compilation stage) code of a function:
julia> f(a::Int)::Int = 2a
f (generic function with 1 method)
julia> #code_lowered f(5)
CodeInfo(
1 ─ %1 = Main.Int
│ %2 = 2 * a
│ %3 = Base.convert(%1, %2)
│ %4 = Core.typeassert(%3, %1)
└── return %4
)
In this case since the returned type is obvious this assertion will be actually removed during the compilation process (try #code_native f(5) to see yourself).
If you want for some reason to generate functions I recommend to use the #generated macro. Be warned: meta-programming is usually an overkill for solving any Julia related problem.
#generated function f2(x)
if x <: Int
quote
2x
end
else
quote
10x
end
end
end
Now we have a function f2 where the source code of f2 is going to depend on the parameter type:
julia> f2(3)
6
julia> f2(3.)
30.0
Note that this function generation is actually happening during the compile time:
julia> #code_lowered f2(2)
CodeInfo(
# REPL[34]:1 within `f2'
┌ # REPL[34]:4 within `macro expansion'
1 ─│ %1 = 2 * x
└──│ return %1
└
)
Hope that clears things out.
You can use Function type for this purpose. From Julia documentation:
Function is the abstract type of all functions
function b(c::Int64)::Int64
return c+2;
end
function a()::Function
return b;
end
Which prints:
julia> println(a()(2));
4
Julia will throw exception for Float64 input.
julia> println(a()(2.0));
ERROR: MethodError: no method matching b(::Float64) Closest candidates are: b(::Int64)
Now that fast anonymous functions are native to julia, do I still have to use the decorator, or is it automatically implemented. Also when I pass a function as an argument into another function, can I static type it? What can I do to improve the run speed.
FastAnonymous is definitely not necessary anymore. Here's how you can verify this yourself:
julia> #noinline g(f, x) = f(x) # prevent inlining so you know it's general
g (generic function with 1 method)
julia> h1(x) = g(identity, x)
h1 (generic function with 1 method)
julia> h2(x) = g(sin, x)
h2 (generic function with 1 method)
julia> #code_warntype h1(1)
Variables
#self#::Core.Compiler.Const(h1, false)
x::Int64
Body::Int64
1 ─ %1 = Main.g(Main.identity, x)::Int64
└── return %1
julia> #code_warntype h2(1)
Variables
#self#::Core.Compiler.Const(h2, false)
x::Int64
Body::Float64
1 ─ %1 = Main.g(Main.sin, x)::Float64
└── return %1
julia> h3(x) = g(z->"I'm a string", x)
h3 (generic function with 1 method)
julia> #code_warntype h3(1)
Variables
#self#::Core.Compiler.Const(h3, false)
x::Int64
#9::getfield(Main, Symbol("##9#10"))
Body::String
1 ─ (#9 = %new(Main.:(##9#10)))
│ %2 = #9::Core.Compiler.Const(getfield(Main, Symbol("##9#10"))(), false)
│ %3 = Main.g(%2, x)::Core.Compiler.Const("I'm a string", false)
└── return %3
In every case Julia knows the return type, and that requires that it "understand" what your function-argument is doing. Moreover:
julia> m = first(methods(g))
g(f, x) in Main at REPL[1]:1
julia> m.specializations
Core.TypeMapEntry(Core.TypeMapEntry(Core.TypeMapEntry(nothing, Tuple{typeof(g),typeof(identity),Int64}, nothing, svec(), 1, -1, MethodInstance for g(::typeof(identity), ::Int64), true, true, false), Tuple{typeof(g),typeof(sin),Int64}, nothing, svec(), 1, -1, MethodInstance for g(::typeof(sin), ::Int64), true, true, false), Tuple{typeof(g),getfield(Main, Symbol("##9#10")),Int64}, nothing, svec(), 1, -1, MethodInstance for g(::getfield(Main, Symbol("##9#10")), ::Int64), true, true, false)
This is a bit hard to read, but if you look carefully you'll see that g has been compiled for 3 inputs:
Tuple{typeof(identity), Int64}
Tuple{typeof(sin), Int64}
Tuple{getfield(Main, Symbol("##9#10")),Int64}
(The compiled versions also take g itself as an extra argument, for reasons having to do with things like the internal implementation of keyword-argument handling, but let's ignore that for now.) The last one is the generated name for the type implementing the anonymous function. What this shows you is that each function has its own type, which is the reason why passing functions as arguments is fast.
For the gurus, there is one other factor that can come in to play: because type inference is subject to the unsolvable halting problem, there are circumstances where inference will decide that this is all getting too complex and abort "early." In such cases (which are relatively rare), it can help to force the compiler to specialize against a particular argument. In our example, that would mean declaring g as
#noinline g(f::F, x) where F = f(x)
rather than
#noinline g(f, x) = f(x)
That ::F is normally unnecessary and appears useless, but you can use it as a compiler-hint to increase the amount of effort used to infer the result. I don't recommend doing that by default (it makes your code a bit harder to read), but if you see weird performance problems it's one thing to try.
Suppose we have a composite type:
mutable struct MyType{TF<:AbstractFloat, TI<:Integer}
a::TF
b::TF
end
We define a constructor
function MyType(a; b = 1.0)
return MyType(a, b)
end
I can broadcast MyType over an array of a's, but how can I do that for b's?
I tried to do
MyType.([1.0, 2.0, 3.0]; [:b, 1.0, :b, 2.0, :b, 3.0,])
But, this does not work.
Note that the above example is totally artificial. In reality, I have a composite type that takes in many fields, many of which are constructed using keyword arguments, and I only want to change a few of them into different values stored in an array.
I don't think you can do this with dot-notation, however, you can manually construct the broadcast call:
julia> struct Foo
a::Int
b::Int
Foo(a; b = 1) = new(a, b)
end
julia> broadcast((x, y) -> Foo(x, b = y), [1,2,3], [4,5,6])
3-element Array{Foo,1}:
Foo(1, 4)
Foo(2, 5)
Foo(3, 6)
julia> broadcast((x, y) -> Foo(x; y), [1,2,3], [:b=>4,:b=>5,:b=>6])
3-element Array{Foo,1}:
Foo(1, 4)
Foo(2, 5)
Foo(3, 6)
I would like to apply a set of functions to a value and get a set of values as output. I see in help?> groupby (DataFrames package) we can do:
> df |> groupby(:a) |> [sum, length]
> df |> groupby([:a, :b]) |> [sum, length]
but can we do
> [sum, length](groupby([:a, :b]))
MethodError: objects of type Array{Function,1} are not callable
square brackets [] for indexing an Array.
eval_user_input(::Any, ::Base.REPL.REPLBackend) at ./REPL.jl:64
in macro expansion at ./REPL.jl:95 [inlined]
in (::Base.REPL.##3#4{Base.REPL.REPLBackend})() at ./event.jl:68
or even
> [sum, length](1:5)
I would expect the output:
[15, 5]
Yes and no. (i.e. yes it's possible, but no, not with that syntax):
No: The syntax you see with |> and dataframes is not general syntax. It's just how the |> method is defined for dataframes. See its definition in file grouping.jl (line 377) and you'll see it's just a wrapper to another function, and it's defined to either accept a function, or a vector of functions.
PS: Note that the generic |> which "pipes" an argument into a function, only expects 1-argument functions on the right hand side, and has very little to do with this particular "dataframe-overloaded" method.
Yes:
You can apply a set of functions to a set of inputs in other ways.
One simple way, e.g. would be via a list comprehension:
julia> a = [1 2 3;2 3 4];
julia> [f(a) for f in [sum, length, size]]
3-element Array{Any,1}:
15
6
(2,3)
Or using map:
julia> map( (x) -> x(a), [sum, length, size])
etc.
PS: If you're keen to use |> to achieve this, clearly you could also do something like this:
julia> a |> (x) -> [sum(x), length(x), size(x)]
but presumably that defeats the purpose of what you're trying to do :)
Your proposed syntax is possible in Julia by adding a method to the type Array{T} (here, T is restricted to subtypes of Function):
julia> (a::Array{T}){T<:Function}(x) = [f(x) for f in a]
julia> [sin cos; exp sqrt](0)
2×2 Array{Float64,2}:
0.0 1.0
1.0 0.0
However, this has a large overhead if the number of functions is small. For maximum speed, one can use Tuples and a #generated function to unroll the loop manually:
julia> #generated (t::NTuple{N, Function}){N}(x) = :($((:(t[$i](x)) for i in 1:N)...),)
julia> (cos, sin)(0)
(1.0,0.0)