# Using parametric abstract type instead of function

**URL:** <https://discourse.julialang.org/t/using-parametric-abstract-type-instead-of-function/95227>\
**Category:** General Usage\
**Tags:** question, performance, function, struct\
**Created:** [February 26, 2023, 4:26pm UTC](https://discourse.julialang.org/t/using-parametric-abstract-type-instead-of-function/95227 "2023-02-26T16:26:13Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![harsh\_kumar](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/harsh_kumar/32/46042_2.png) [@harsh\_kumar](https://discourse.julialang.org/u/harsh_kumar)\
**Post date:** [February 26, 2023, 4:26pm UTC](https://discourse.julialang.org/t/using-parametric-abstract-type-instead-of-function/95227/1 "2023-02-26T16:26:13Z")

</div>

I have a function that takes an arg and callable type (shown below). I need to pass additional parameter information with the function. One of the clean ways that worked with Julia, is to define an abstract type with a parameter and define a function with the parameter (added dummy example). Probably this is not the intended use, and an alternative would be to define a function with Type or Val as input and use an anonymous function.

Will this lead to any unintended issues or performance deterioration? Also is there any other clean way to do this, without modifying the function main?

```julia
function main(arg, fn)
    res = do_work(arg)
    fn(res)
end 

abstract type fun{T} end

fun{T}(x) where {T} = convert(T, x)

# Example
do_work(x) = x
main(2.0, fun{Int}) # Return 2 (type int)

```

I

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [February 26, 2023, 5:53pm UTC](https://discourse.julialang.org/t/using-parametric-abstract-type-instead-of-function/95227/2 "2023-02-26T17:53:09Z")

</div>

I don’t know about performance but another approach is

```julia
function curried_converter(T::Type)
    return (x) -> convert(T, x) 
end

f = curried_converter(Int)
main(2.0, f)
# 2

```

Approach #2 (small edit to main)

```julia
function main(arg, T::Type)
    res::T = do_work(arg)
    return res
end 

# Example
do_work(x) = x
main(2.0, Int) # Return 2 (type int)

```

I’m not at my computer, but my guess it that the last approach is best because it doesn’t need to compile an extra function. But I’m not sure how hard your requirement to not edit `main` is.

EDIT: Error in `main` in approach 2. fixed.

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [February 26, 2023, 8:05pm UTC](https://discourse.julialang.org/t/using-parametric-abstract-type-instead-of-function/95227/3 "2023-02-26T20:05:23Z")

</div>

Ran some benchmarks:

> **Setup Code**
>
> ```julia-auto
> function main1(arg, fn)
> res = do_work(arg)
> fn(res)
> end
> do_work(x) = (x)
> 
> abstract type fun{T} end
> fun{T}(x) where {T} = convert(T,x)
> 
> function curried_converter(T::Type)
> return (x) -> convert(T, x) 
> end
> f = curried_converter(Int)
> 
> function main2(arg, T::Type)
> res::T = do_work(arg)
> return res
> end 
> 
> ```

### Timing Results

```julia-auto
@btime main1($(2.0), $(fun{Int}))
# first run: 150.000 ns (1 allocation: 16 bytes)
# 142.823 ns (1 allocation: 16 bytes)
@btime main1($(2.0), $(f))
# first run: 244.053 ns (1 allocation: 16 bytes)
# 129.499 ns (1 allocation: 16 bytes
@btime main2($(2.0), $(Int))
# first run: 141.543 ns (1 allocation: 16 bytes)
# 119.778 ns (1 allocation: 16 bytes)

```

If this is only going to be run once, the anonymous function is going to cost you compilation time. After running once, it’s faster to use the anonymous function. If you can edit main at all, looks like you get some benefit from using a more “normal” approach to specifying an output.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [February 26, 2023, 11:37pm UTC](https://discourse.julialang.org/t/using-parametric-abstract-type-instead-of-function/95227/4 "2023-02-26T23:37:10Z")

</div>

You really want to parameterise `T` in this case, or you are not specializing the function, and are actually putting a `DataType` in the field of the anonymous function struct instead of just inserting the the known `T` right into the `convert` method at compile time.

```julia
function curried_converter(::Type{T}) where T
    return (x) -> convert(T, x) 
end
f = curried_converter(Int)

```

And its a few ns:

```julia
@btime main1($(2.0), $(f))
julia> @btime main1($(2.0), $(f))
  4.354 ns (0 allocations: 0 bytes)
2

```

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [February 27, 2023, 12:36am UTC](https://discourse.julialang.org/t/using-parametric-abstract-type-instead-of-function/95227/5 "2023-02-27T00:36:29Z")

</div>

Nice. Just learned about the impact of using parametric types in function signatures allowing generated functions recently. Clearly I have not internalized it yet!

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [February 27, 2023, 3:32am UTC](https://discourse.julialang.org/t/using-parametric-abstract-type-instead-of-function/95227/6 "2023-02-27T03:32:27Z")

</div>

While trying this (and variants thereof), I found strange behavior. Some sort of path-dependence in a Julia session that depends on whether it previously threw an error, it seems.

In this demo, I make it throw an error and it runs slow forevermore. Then I restart the session, re-execute without throwing an error, and it runs fast.

Any ideas what’s going on here?

```plaintext
PS C:\Users\unime> julia
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.9.0-beta4 (2023-02-07)
 _/ |\ __'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |

julia> using BenchmarkTools
       function main(arg, fn)
           res = do_work(arg)
           fn(res)
       end
       function curried_converter(::Type{T}) where T
           return x -> convert(T, x)
       end;

julia> main(2.0, curried_converter(Int))
ERROR: UndefVarError: `do_work` not defined
Stacktrace:
 [1] main(arg::Float64, fn::var"#3#4"{Int64}) @ Main .\REPL[1]:3
 [2] top-level scope @ REPL[2]:1

julia> do_work(x) = x
       @btime main($2.0, f) setup=(f=curried_converter(Int))
  30.519 ns (1 allocation: 16 bytes)
2

julia> exit()
PS C:\Users\unime> julia
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.9.0-beta4 (2023-02-07)
 _/ |\ __'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |

julia> using BenchmarkTools
       function main(arg, fn)
           res = do_work(arg)
           fn(res)
       end
       function curried_converter(::Type{T}) where T
           return x -> convert(T, x)
       end;

julia> do_work(x) = x
       @btime main($2.0, f) setup=(f=curried_converter(Int))
  4.900 ns (0 allocations: 0 bytes)
2

```

Probably not coincidentally, `Base.Fix1` and my [PartialFuns](https://github.com/uniment/PartialFuns.jl) `Fix1` suffer from this too, as does the OP’s type-parameterized abstract typctor.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [February 27, 2023, 10:21am UTC](https://discourse.julialang.org/t/using-parametric-abstract-type-instead-of-function/95227/7 "2023-02-27T10:21:11Z")

</div>

There do seems to be some bugs like this around. Sometimes including the code with Revise.jl will “fix” them.

But I’m not sure if its the fact the error was thrown, or just that it was compiled unspecialised to make a runtime function call and that’s what it’s still doing after you define `do_work`.

Your second version is only compiled after `do_work` is defined so will just inline it.

Anyway, that’s just a guess, probably actually investigating it further will give better answers. (e.g. with `@code_lowered` and `@code_native` or `Cthulhu.@descend`)
