# Function as Type Parameter

**URL:** https://discourse.julialang.org/t/function-as-type-parameter/102378
**Category:** General Usage
**Tags:** question, parametric-types
**Created:** [August 1, 2023, 11:12pm UTC](https://discourse.julialang.org/t/function-as-type-parameter/102378 "2023-08-01T23:12:35Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![AlexanderNenninger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexandernenninger/32/24602_2.png) [@AlexanderNenninger](https://discourse.julialang.org/u/AlexanderNenninger)
#### Post date: [August 1, 2023, 11:12pm UTC](https://discourse.julialang.org/t/function-as-type-parameter/102378/1 "2023-08-01T23:12:36Z")

</div>

Consider a struct, that takes a function as a type parameter:

```julia
struct Foo{F} end

function call(foo::Foo{F}, x) where {F}
    F(x)
end

```

Seems great at first, as can be seen by

```julia
# This works
r = Base.RefValue(3)
foo = Foo{x -> r[] * x}()
call(foo, 10)

```

Performance is also way better than having the function as a field.

But I can’t use this struct in functions if it wraps a closure wrapping a mutable type:

```julia
function usesfoo(d)
    foo = Foo{x -> x + d[]}()
    call(foo, 2)
end

usesfoo(Base.RefValue(3))

```

How can I work around it, while keeping the good performance ([lazyarrays.jl · GitHub](https://gist.github.com/AlexanderNenninger/461f37315e45071a8c91b18d73901431))?

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [August 1, 2023, 11:39pm UTC](https://discourse.julialang.org/t/function-as-type-parameter/102378/2 "2023-08-01T23:39:07Z")

</div>

Maybe a callable struct with the function as a field.

> [@AlexanderNenninger](#):
>
> ```julia
> struct Foo{F}
> f::F
> end
> (f::F)(args...) = f.f(args...)
> 
> ```

---

<div class="post-metadata">

### Author: ![AlexanderNenninger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexandernenninger/32/24602_2.png) [@AlexanderNenninger](https://discourse.julialang.org/u/AlexanderNenninger)
#### Post date: [August 2, 2023, 6:23am UTC](https://discourse.julialang.org/t/function-as-type-parameter/102378/3 "2023-08-02T06:23:22Z")

</div>

That was my original implementation, but then I have to call it via its function pointer. In my test case it was about 40x slower ([lazyarrays.jl · GitHub](https://gist.github.com/AlexanderNenninger/461f37315e45071a8c91b18d73901431))

I specifically want F to be statically compiled into Foo

---

<div class="post-metadata">

### Author: ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)
#### Post date: [August 2, 2023, 7:04am UTC](https://discourse.julialang.org/t/function-as-type-parameter/102378/4 "2023-08-02T07:04:44Z")

</div>

> [@AlexanderNenninger](#):
>
> In my test case it was about 40x slower

😱

I had no idea

---

<div class="post-metadata">

### Author: ![Vasily\_Pisarev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vasily_pisarev/32/7929_2.png) [@Vasily\_Pisarev](https://discourse.julialang.org/u/Vasily_Pisarev)
#### Post date: [August 2, 2023, 7:17am UTC](https://discourse.julialang.org/t/function-as-type-parameter/102378/5 "2023-08-02T07:17:17Z")

</div>

It looks like your implementation is not quite what @lmiq proposes. Mind that field type for `f` is `F`, not `Function`.  
If the implementation is redone as

```julia
mutable struct LazyFunctionArray{F,T,N} <: AbstractArray{T,N}
    const f::F # Julia 1.8 const field syntax for convenience
    const size::NTuple{N,Int}
    ncalls::Int
end

const LazyFunctionVector{F,T} = LazyFunctionArray{F,T,1}
const LazyFunctionMatrix{F,T} = LazyFunctionArray{F,T,2}

function LazyFunctionArray(T::Type, f::F, dims::Vararg{Int,N}) where {N,F<:Function}
    LazyFunctionArray{F,T,N}(f, dims, 0)
end
...

```

then the difference between `benchmark_eval_1()` and `benchmark_eval_2()` vanishes.

---

<div class="post-metadata">

### Author: ![AlexanderNenninger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexandernenninger/32/24602_2.png) [@AlexanderNenninger](https://discourse.julialang.org/u/AlexanderNenninger)
#### Post date: [August 2, 2023, 8:27am UTC](https://discourse.julialang.org/t/function-as-type-parameter/102378/6 "2023-08-02T08:27:54Z")

</div>

Thank you, this exactly what I need. Here’s the final code for future reference:

```plaintext
# Quick implementation of a lazy Array. Yes, it's really *that* simple.
mutable struct LazyFunctionArray{F<:Function,T,N} <: AbstractArray{T,N}
    const f::F # Julia 1.8 const field syntax for convenience
    const size::NTuple{N,Int}
    ncalls::Int
end
const LazyFunctionVector{F,T} = LazyFunctionArray{F,T,1}
const LazyFunctionMatrix{F,T} = LazyFunctionArray{F,T,2}

function LazyFunctionArray(T::Type, f::F, dims::Vararg{Int,N}) where {F<:Function,N}
    LazyFunctionArray{F,T,N}(f, dims, 0)
end

function LazyFunctionArray(f::F, dims::Vararg{Int,N}) where {F<:Function,N}
    LazyFunctionArray(Float64, f, dims...)
end

function LazyFunctionVector(T::Type, f::F, n::Int) where {F<:Function}
    LazyFunctionVector{T,F}(f, (n,), 0)
end

function LazyFunctionVector(f::F, n::Int) where {F<:Function}
    LazyFunctionVector(Float64, f, n)
end

function LazyFunctionMatrix(T::Type, f::F, n::Int, m::Int) where {F<:Function}
    LazyFunctionMatrix{F,T}(f, (n, m), 0)
end

function LazyFunctionMatrix(f::F, n::Int, m::Int) where {F<:Function}
    LazyFunctionMatrix(Float64, f, n, m)
end

function Base.size(A::LazyFunctionArray)
    A.size
end

function Base.getindex(A::LazyFunctionArray{F,T,1,}, i::Int) where {F<:Function,T}
    A.ncalls += 1
    A.f(i)
end

function Base.getindex(A::LazyFunctionArray{F,T,N}, I::Vararg{Int,N}) where {F<:Function,T,N}
    A.ncalls += 1
    A.f(I...)
end

function benchmark_eval()
    A = LazyFunctionArray((x, y) -> x + y, 1000, 1000)
    m = Matrix(A)
    @benchmark m = Matrix($A)
end

benchmark_eval()

```
