# Type inference from list of functions

**URL:** https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538
**Category:** New to Julia
**Tags:** type
**Created:** [May 23, 2019, 7:12pm UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538 "2019-05-23T19:12:22Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![misha\_mikhasenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/misha_mikhasenko/32/5060_2.png) [@misha\_mikhasenko](https://discourse.julialang.org/u/misha_mikhasenko)
#### Post date: [May 23, 2019, 7:12pm UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/1 "2019-05-23T19:12:23Z")

</div>

I was surprised that type inference does not work in this case

```julia
for i=1:3
    @eval $(Symbol("f"*string(i)))(x) = x^$(i)+$(i) 
end
#
@code_warntype f1(1.1) # it is Float64, fine!
#
function test(i)
    list_of_functions = [f1,f2,f3]
    return list_of_functions[i](rand())
end
@code_warntype test(1) # Any

```

what would be the correct way call a function by given index?

---

<div class="post-metadata">

### Author: ![misha\_mikhasenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/misha_mikhasenko/32/5060_2.png) [@misha\_mikhasenko](https://discourse.julialang.org/u/misha_mikhasenko)
#### Post date: [May 23, 2019, 7:40pm UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/2 "2019-05-23T19:40:47Z")

</div>

For recording, I have the other few “wrong” ways to write the same code,

```julia
function testII(i)
    return @eval $(Symbol("f"*string(i)))(rand())
end
@code_warntype testII(1) # Any
# 
function testIII(i)
    f = f1
    i==2 && (f = f2)
    i==3 && (f = f3)
    return f(rand())
end
@code_warntype testIII(1) # Any

```

In the next two examples, the correct type is inferred, however, they are incovinient,

```julia
# code for function is repeated for every f[i]
function testIII(i)
    i==1 && return f1(rand())
    i==2 && return f2(rand())
    return f3(rand())
end
@code_warntype testIII(1) # Float64
#
# not really what was required
# also, the code is not readable for students
for i=1:3
    @eval function $(Symbol("test_"*string(i)))()
        return $(Symbol("f"*string(i)))(rand())
    end
end
@code_warntype test_2() # Float64

```

---

<div class="post-metadata">

### Author: ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)
#### Post date: [May 23, 2019, 10:11pm UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/3 "2019-05-23T22:11:30Z")

</div>

The type information is lost at `[f1,f2,f3]` because that vector is of type `Vector{Function}`. A tuple will instead store the type of each of the three arguments (i.e. the type of the functions), so you need:

```julia
function test(i)
    list_of_functions = (f1,f2,f3)
    return list_of_functions[i](rand())
end

```

The variable `i` is still not known at compile time, but thanks to constant propagation, Julia figures it out, you just need to test the whole thing inside another function. Its more obvious that its working if the three function return different types

```julia
f1(x) = 1
f2(x) = "a"
f3(x) = nothing
test1() = test(1)
test2() = test(2)
test3() = test(3)
@code_warntype test1() # Int
@code_warntype test2() # String
@code_warntype test3() # Nothing

```

---

<div class="post-metadata">

### Author: ![misha\_mikhasenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/misha_mikhasenko/32/5060_2.png) [@misha\_mikhasenko](https://discourse.julialang.org/u/misha_mikhasenko)
#### Post date: [May 24, 2019, 7:37am UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/4 "2019-05-24T07:37:54Z")

</div>

Thank you very much, that is good to know!  
Would it propagate constant tuples or more complicated structures as well?

However, it does not solve my problem, still. I know that the return type of `test(i)` is the same as the type of any of `f` functions (they all, supposedly, have the same type). I guess, there should be a way how to let `julia` figure this out. Any ideas?

---

<div class="post-metadata">

### Author: ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)
#### Post date: [May 24, 2019, 7:57am UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/5 "2019-05-24T07:57:34Z")

</div>

> [@misha\_mikhasenko](#):
>
> However, it does not solve my problem, still.

Sorry, not quite understanding why it doesn’t. The version I gave above will make it so `test(i)` is inferred correctly, including if all the `f`’s return the same type. Isn’t that what you were looking for?

---

<div class="post-metadata">

### Author: ![misha\_mikhasenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/misha_mikhasenko/32/5060_2.png) [@misha\_mikhasenko](https://discourse.julialang.org/u/misha_mikhasenko)
#### Post date: [May 24, 2019, 8:18am UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/6 "2019-05-24T08:18:34Z")

</div>

Yes, I thought so from your reply. Then, I tested (Julia Version 1.1.1 (2019-05-16)),

```julia
f1(x) = 1.0
f2(x) = 1.0
f3(x) = 1.0
function test(i)
    list_of_functions = (f1,f2,f3)
    return list_of_functions[i](rand())
end
@code_warntype test(1) # Any

```

and it did not work for me.

Are you saying that in the same code the type in inferred for you?  
Or it works only when constant is propagated during the compilation as in your example?

---

<div class="post-metadata">

### Author: ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)
#### Post date: [May 24, 2019, 8:40am UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/7 "2019-05-24T08:40:54Z")

</div>

Ah I see, yes, what you’re seeing is right, its just that constant propagation only works for things inside functions, hence why I put the call inside of one with `test1() = test(1)` in my example. In general Julia only does all possible optimizations, constant propagation included, for things inside functions, so you’ll definitely want to put all speed-critical code inside of them.

---

<div class="post-metadata">

### Author: ![misha\_mikhasenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/misha_mikhasenko/32/5060_2.png) [@misha\_mikhasenko](https://discourse.julialang.org/u/misha_mikhasenko)
#### Post date: [May 24, 2019, 8:49am UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/8 "2019-05-24T08:49:15Z")

</div>

In my working example, there are many function indices like `i`, they are wrapped in more complicated arrays, so It is not straightforward to precompile for the given indices. (possible, although)

In principle, the problem would be solved if I could specify the type of function, like in c++

```nohighlight
std::vector<std::function<double(double)> > list_of_functions

```

It seems to me that such inference could be handled by the julia compiler.

Thank you for replies, anyway.

---

<div class="post-metadata">

### Author: ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)
#### Post date: [May 24, 2019, 8:56am UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/9 "2019-05-24T08:56:19Z")

</div>

Ah ok I think I understand the confusion. I don’t mean that you actually need to write functions `test1`, `test2`, etc… I just mean that your calls to `test(1)`, `test(2)`, etc… (for any arbitrary `i`) need to be literally inside _some_ function, and they will be inferred correctly. The only place they will not be inferred correctly is if you call them directly from the REPL (because not all optimizations are turned on there), which is what you are doing when you did `@code_warntype test(1)` and got `Any`.

---

<div class="post-metadata">

### Author: ![misha\_mikhasenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/misha_mikhasenko/32/5060_2.png) [@misha\_mikhasenko](https://discourse.julialang.org/u/misha_mikhasenko)
#### Post date: [May 24, 2019, 8:57am UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/10 "2019-05-24T08:57:37Z")

</div>

I got it, thank you a lot!

---

<div class="post-metadata">

### Author: ![misha\_mikhasenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/misha_mikhasenko/32/5060_2.png) [@misha\_mikhasenko](https://discourse.julialang.org/u/misha_mikhasenko)
#### Post date: [May 24, 2019, 10:12am UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/11 "2019-05-24T10:12:49Z")

</div>

Are only scalar constants propagated?

```julia
f1(x) = 1.0
f2(x) = 1.0
f3(x) = 1.0
function test(vect)
    i = vect[1]
    list_of_functions = (f1,f2,f3)
    return list_of_f[i](rand())
end
#
const v = [1,2,3,1,3,3]
test1() = test(v)
@code_warntype test1() # Any

```

---

<div class="post-metadata">

### Author: ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)
#### Post date: [May 24, 2019, 10:59am UTC](https://discourse.julialang.org/t/type-inference-from-list-of-functions/24538/12 "2019-05-24T10:59:03Z")

</div>

> [@misha\_mikhasenko](#):
>
> In principle, the problem would be solved if I could specify the type of function, like in c++
> 
> ```julia
> std::vector<std::function<double(double)> > list_of_functions
> 
> ```

You can do this with [GitHub - yuyichao/FunctionWrappers.jl](https://github.com/yuyichao/FunctionWrappers.jl)

```julia
using FunctionWrapper
using BenchmarkTools

const F64F64Func = FunctionWrapper{Float64,Tuple{Float64}}

const funcs_FW = F64F64Func[]
const funcs = Function[]

for i in 1:100
    push!(funcs_FW, F64F64Func(x -> x + i))
    push!(funcs, x -> x + i)
end

function run(fs)
    s = 0.0
    for i in 1.0:100.0
        for f in fs
            s += f(i)
        end
    end
    return s
end

@btime run(funcs_FW)
@btime run(funcs)

```
