# \[ANN\] EnforcedTypeSignatureCallables.jl

**URL:** <https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280>\
**Category:** Package Announcements\
**Tags:** package, announcement, function, type-stability, functors\
**Created:** [July 20, 2024, 6:16pm UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280 "2024-07-20T18:16:57Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [July 20, 2024, 6:16pm UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/1 "2024-07-20T18:16:57Z")

</div>

The `EnforcedTypeSignatureCallables` package is being registered: [New package: EnforcedTypeSignatureCallables v1.0.0 by JuliaRegistrator · Pull Request #111416 · JuliaRegistries/General · GitHub](https://github.com/JuliaRegistries/General/pull/111416)

Once it’s registered, this link will point to its page on JuliaHub: EnforcedTypeSignatureCallables.jl

The Git repo is on Gitlab:

> **[Neven Sajko / EnforcedTypeSignatureCallables.jl · GitLab](https://gitlab.com/nsajko/EnforcedTypeSignatureCallables.jl)**
>
> A Julia package providing functionality for annotating arbitrary callables with type signature data. Might help with bad inference. Provides somewhat of a restricted way...

The package was motivated by the frequent requests by Julia newbies for expressing the type signatures for “functions” in the type system. This doesn’t really make sense as a problem in Julia, but this simple implementation is perhaps close to being a solution.

The package may also be useful for ensuring type stability/helping the Julia compiler’s type inference.

## Related

The FunctionWrappers.jl package offers superficially similar functionality, however its goals are actually completely different. Unlike this package, FunctionWrappers doesn’t have a type parameter for the type of the wrapped callable. Thus the same FunctionWrappers type may wrap multiple functions, assuming the type signature is the same.

Also see this recently opened PR to Julia, aiming to make FunctionWrappers obsolete: [RFC: Introduce `TypedCallable` by topolarity · Pull Request #55111 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/55111)

## Request for comments

I’m not sure about the naming yet. Specifically, the name of the exported type is `TypedCallable`, the same as in the linked Julia PR. It’d probably be good if anyone can think of a better name before EnforcedTypeSignatureCallables is registered.

---

<div class="post-metadata">

**Author:** ![ForceBru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/forcebru/32/21389_2.png) [@ForceBru](https://discourse.julialang.org/u/ForceBru)\
**Post date:** [July 20, 2024, 7:11pm UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/2 "2024-07-20T19:11:55Z")

</div>

> [@nsajko](#):
>
> frequent requests by Julia newbies for expressing the type signatures for “functions” in the type system. This doesn’t really make sense as a problem in Julia

I think this actually is a problem, it makes perfect sense, and it’s not specific to newbies. Simply rephrase the problem in terms of methods. Let the syntax

```
function my_map(fn::Function{T, U}, data::AbstractVector{T})::Vector{U} where {T,U}

```

mean: “`fn` is any function that has a method that accepts a single `T` and returns a `U`”. The usual “most concrete type first” rules apply: if `eltype(data)===Float64`, then `fn(::Real)::U` will be applied, not the more general `fn(::T)::U`, etc. Given some function like `sin`, find its most concrete method that satisfies the `Function{T, U}` constraint. There can be many functions that satisfy this criterion: one can call `my_map(sin, [1,2,3])`, `my_map(sum, [1,2,3])`, `my_map(x -> "hello", [1,2,3])` and so on. No wrapper types required.

Julia’s documentation [says](https://docs.julialang.org/en/v1/manual/methods/):

> …choose which of a function’s methods to call based on the number of arguments given, and on the types of all of the function’s arguments. Using all of a function’s arguments to choose which method should be invoked … is known as [multiple dispatch](https://en.wikipedia.org/wiki/Multiple_dispatch).  
> …  
> The most specific method definition matching the number and types of the arguments will be executed when the function is applied.

Why not use multiple dispatch when passing a function as an argument? “Simply” pass the most specific method that match the parameter’s type as the argument. This approach seems very natural to me, very much in line with dynamic dispatch.

I think it’s strange that Julia doesn’t have _syntax_ for function signatures (would be “method signatures” in Julia world, I guess), whereas other languages that have meaningful type annotations (i.e. C, Rust, Haskell, but not Python) do. Would such syntax not be useful? Is the idea of placing constraints like `Function{T, U}` on _methods_ (not individual functions) wrong?

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [July 20, 2024, 8:08pm UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/3 "2024-07-20T20:08:50Z")

</div>

> [@ForceBru](#):
>
> Simply rephrase the problem

Your messsage is interesting, but TBH it seems off-topic here. Could you move it to the Julia issue tracker on Github, as a feature request? I’m not able to change Julia’s dispatch behavior from a mere user package.

---

<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:** [July 20, 2024, 8:38pm UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/4 "2024-07-20T20:38:07Z")

</div>

> <https://gitlab.com/nsajko/EnforcedTypeSignatureCallables.jl/-/blob/main/src/EnforcedTypeSignatureCallables.jl?ref_type=heads#L33>

Why is this cascade of asserts required?

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [July 20, 2024, 9:22pm UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/5 "2024-07-20T21:22:53Z")

</div>

> [@mrufsvold](#):
>
> Why is this cascade of asserts required?

It’s not 😅. I included it “just in case”, however after checking with `Core.Compiler.return_type` it seems that none of the four type asserts in the cascade is useful. Deleting these four lines.

Julia is able to optimize out unnecessary type assertions, though, so I think they’re usually harmless.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 20, 2024, 11:50pm UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/6 "2024-07-20T23:50:43Z")

</div>

> [@ForceBru](#):
>
> Why not use multiple dispatch when passing a function as an argument?

This technically already happens, just not in the way you think it can.

First, we have to throw out the `U` part because a specific call signature with a unique piece of compiled code may not have a fixed return type, so it’s not a useful part; you could make `U` abstract and leave it entirely up to the compiler, but that’s not controllable or stable. Second, 1 method (and method signature) can have an infinite number of call signatures (and thus compiled _specializations_) at once e.g. `foo(x::Real)` can be compiled for `foo(::Int)` and `foo(::Float64)`. Type stability and control makes _call signatures_ (like this very package!) better to specify, so you wouldn’t in fact want to dispatch on methods in any sense. Even in your example, `T=Float64`, and you make it clear no method `fn(::Float64)` exists, only `fn(::Real)`. I see what you want, but the type system does not allow `Blah{Float64}` and `Blah{Real}` to share an instance. It also doesn’t allow multiple supertypes; `T=Int` in the example calls in fact, so `sin`, `sum`, etc need to subtype `Function{Int}` as well.

The good news is you don’t need any of that stuff. The first hint is that when a function call is dispatched, the types of the callable _and_ the arguments are taken into account, so the types of functions like `sin` and `sum` only have to care about the callable. That’s how we can write methods for callable instances and vary positional arguments in multimethods, yet let the compiler infer specific compiled code so often.

So if we can already dispatch on all parts of a call, why the concept of `FunctionWrapper`s in the first place? Sometimes we can’t narrow down a call site to 1 specialization because one position, often the callable, has to change types at runtime, and that normally needs possibly costly dynamic dispatch and ruins type inference downstream. So FunctionWrappers.jl wrapped specializations with input/output type conversions, putting that information in a concrete type (and parameters) so we can lump many callables together. EnforcedTypeSignatureCallables won’t be able to elide the dynamic dispatch part because most different functions will be represented by an abstract type parameter, but it does provide more inferrability and it’s not subject to many of FunctionWrappers’ constraints and dynamism shortcoming ([example](https://discourse.julialang.org/t/no-allocs-after-restart/117273/14)).

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 20, 2024, 11:59pm UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/7 "2024-07-20T23:59:52Z")

</div>

How feasible is it to incorporate keyword argument types from a call site into `TypedCallable`? Currently the call looks like:

```julia
function (tc::TypedCallable{A,R})(args::Vararg{Any,N}; kwargs...) where {A,R,N}
    args = args::A
    r = (tc.f)(args...; kwargs...)
    r::R
end

```

asserting that input types A result in output type R. But `A` is only asserted for positional arguments, while the return type will also vary with keyword arguments:

```julia
julia> foo(;a) = zero(a)
foo (generic function with 1 method)

julia> foo(;a=1), foo(;a=1.0)
(0, 0.0)

julia> Base.specializations(only(methods(var"#foo#1"))) # skipping some steps to find this symbol
Base.MethodSpecializations(svec(MethodInstance for var"#foo#1"(::Int64, ::typeof(foo)), MethodInstance for var"#foo#1"(::Float64, ::typeof(foo)), nothing, nothing, nothing, nothing, nothing))

```

If brevity is a concern, this could be a separate type for calls with keyword arguments, keep `TypedCallable`’s structure for the typical positional-only calls.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [July 21, 2024, 6:26am UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/8 "2024-07-21T06:26:56Z")

</div>

> [@Benny](#):
>
> How feasible is it to incorporate keyword argument types from a call site into `TypedCallable`?

Possibly it’s doable, but certainly not trivial, which is why I gave up on it right away. Maybe make a PR?

Keyword arguments seem problematic, because they can come in any order. A call like `f(; a = 1, b = true)` will have a different type of named tuple for the kwargs than `f(; b = true, a = 1)`.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 21, 2024, 6:39am UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/9 "2024-07-21T06:39:17Z")

</div>

Exactly my concern over feasibility. Making a type depend on keyword argument order is not nonsensical because a target call site will have those keywords in a particular order, but that categorizes and restricts the callable in a fairly unnecessary way. The keyword argumented calls’ underlying calls sort the keywords into positional arguments, so they don’t appear to be specializing over arbitrary keyword order either.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [July 21, 2024, 5:14pm UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/10 "2024-07-21T17:14:25Z")

</div>

> [@nsajko](#):
>
> ## Request for comments
> 
> I’m not sure about the naming yet. Specifically, the name of the exported type is `TypedCallable`, the same as in the linked Julia PR. It’d probably be good if anyone can think of a better name before EnforcedTypeSignatureCallables is registered.

I’ve played around with things like this a few times, and I prefer to call them something like MethodWrappers or CallableMethods since they (ideally) would statically specify a single method of a function and make it invokeable.

Functions in Julia have no specific signature, only methods have signatures, so this concept is more related to methods than functions in my mind.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [September 5, 2025, 12:59pm UTC](https://discourse.julialang.org/t/ann-enforcedtypesignaturecallables-jl/117280/11 "2025-09-05T12:59:06Z")

</div>

Breaking release v3 is now registered and available for installation. The code is now completely rewritten. The design is simplified and leverages functionality already provided with Julia as much as possible: `typeassert`, function composition (`∘`) and `Base.Fix2`. This means that the package now provides a way to create functions that are guaranteed to return a value of the given type, if they return **and** it is possible to dispatch on such functions even without depending on EnforcedTypeSignatureCallables.jl. Basically I’m proposing a standard the ecosystem can converge on for specifying generic functions with type signatures.

The repo is now also moved to the Github JuliaFunctional org:

- [GitHub - JuliaFunctional/EnforcedTypeSignatureCallables.jl: Julia package providing functionality for annotating arbitrary callables with type signature data.](https://github.com/JuliaFunctional/EnforcedTypeSignatureCallables.jl)
