# Proposal: Adding Optional Static Interface/Traits Checking to Julia

**URL:** <https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846>\
**Category:** Internals & Design\
**Tags:** question\
**Created:** [February 13, 2025, 12:09am UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846 "2025-02-13T00:09:30Z")\
**Posts on this page:** 13\
**Page:** 2

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [February 13, 2025, 5:29pm UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/21 "2025-02-13T17:29:38Z")

</div>

in some cases it can add some `convert` calls and type assertions right? although yeah even when nonzero, performance impact should be negligible

---

<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:** [February 13, 2025, 5:32pm UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/22 "2025-02-13T17:32:25Z")

</div>

> [@pdeffebach](#):
>
> “Duck typing” does not lead to a run-time performance loss in Julia.

Well, code with duck-typing (or multiple dispatch, which is what Solmantos was claiming to be more expensive) can have runtime overhead, but that has other causes, not duck-typing or multiple dispatch. Simple example:

```julia
module DuckTypingSlow
function foo(x::Base.RefValue{Any})
    return x[] + 2
end
end

```

---

<div class="post-metadata">

**Author:** ![Solmantos](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/solmantos/32/215204_2.png) [@Solmantos](https://discourse.julialang.org/u/Solmantos)\
**Post date:** [February 13, 2025, 5:32pm UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/23 "2025-02-13T17:32:38Z")

</div>

> [@pdeffebach](#):
>
> performance

The performance argument is exactly my point - duck typing in Julia has no performance penalty.

There is, however, a fundamental conceptual difference between duck typing and multiple dispatch, which my simple example was trying to illustrate. The holy traits pattern adds complexity without performance benefits over duck typing. But the discussion seems stuck on pedantic implementation details and meaningless defensive quips

---

<div class="post-metadata">

**Author:** ![Solmantos](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/solmantos/32/215204_2.png) [@Solmantos](https://discourse.julialang.org/u/Solmantos)\
**Post date:** [February 13, 2025, 5:33pm UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/24 "2025-02-13T17:33:56Z")

</div>

> [@Benny](#):
>
> You haven’t demonstrated anything as rigorous as Mypy for Julia here. Also worth pointing out that Mypy started development in 2012, 18 years after Python v1 released. Python’s support for type hints came in 2014, a full 2 decades after. People didn’t exactly wait for this to start serious investment

This has already been addressed:

> [@Solmantos](#):
>
> Poor developer experience kills languages in industry. Julia’s limited industry adoption despite its excellent technical merits is evidence of this. The bar isn’t “better than MATLAB” anymore - we’re competing with languages that have excellent tooling, clear interfaces, and strong static analysis support

Industry has evolved in the last 20 years. We are not designing languages in the 90s.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [February 13, 2025, 5:34pm UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/25 "2025-02-13T17:34:45Z")

</div>

Welcome to the forums, @Solmantos! There is indeed recent thought on this matter — have you seen and contrasted your own thoughts against Keno’s [roadmap for interfaces](https://hackmd.io/BbEw0_B4Q8uDSS34LOvpCw) that was linked above? The most likely way to affect change here is to work in the context of existing efforts.

---

<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:** [February 13, 2025, 5:56pm UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/26 "2025-02-13T17:56:38Z")

</div>

> [@Solmantos](#):
>
> There is, however, a fundamental conceptual difference between duck typing and multiple dispatch, which my simple example was trying to illustrate. The holy traits pattern adds complexity without performance benefits over duck typing.

The same misconceptions are here, and it’s seriously affecting the soundness of your proposal. I’m evidently failing to help you clear those up, so I’ll leave this discussion here. I’m confident that after you study Julia and the other efforts toward formalizing traits or interfaces, you’ll understand that the design issues brought up here weren’t “pedantic implementation details,” but important steps to figuring out something we all want. I’m sure they’ll welcome your participation and contributions then.

---

<div class="post-metadata">

**Author:** ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)\
**Post date:** [February 13, 2025, 6:32pm UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/27 "2025-02-13T18:32:48Z")

</div>

> [@Solmantos](#):
>
> There is, however, a fundamental conceptual difference between duck typing and multiple dispatch, which my simple example was trying to illustrate.

Could you elaborate on this? In my understanding, they are almost completely separate, although there is an interplay of the lack of duck typing or specialization with the multiple dispatch system, i.e. specialized methods can be written leveraging multiple dispatch. That’s the multiple dispatch at the semantic (front-end) level that you may be thinking of primarily. But the interesting thing about Julia is that multiple dispatch (static or dynamic) exists in the background regardless of whether you have specialized methods or duck typed methods. Outside of specific exceptions, specialized method instances will be compiled for each combination of argument types, and even inlined if they can be statically inferred in the callee (again, there are exceptions to this). What is typically associated with performance penalty is runtime dispatch, but this penalty is also not universally bad if it doesn’t occur many times over in hot loops where it swamps actual computation time.

---

<div class="post-metadata">

**Author:** ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)\
**Post date:** [February 13, 2025, 6:49pm UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/28 "2025-02-13T18:49:02Z")

</div>

> [@Solmantos](#):
>
> Coming from Python’s experience with type hints and interfaces, I think we’re letting perfect be the enemy of good here. Are interfaces a perfect solution to behavioral contracts? No. But they’re a proven system that would add zero runtime overhead and wouldn’t restrict Julia’s multiple dispatch in any way.

I do agree both that this is an important issue and that we shouldn’t strive for the perfect solution right away and end up with no solution.

That said, I do believe that it was somewhat easier to tack such a system onto Python where types had a much smaller importance in the language itself than in Julia.

So regarding multiple dispatch, I agree with @Benny that we need to see examples to understand the interplay of different types of contracts with idiomatic Julia code leveraging multiple dispatch both implicitly and explicitly.

---

<div class="post-metadata">

**Author:** ![grahamstark](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/grahamstark/32/5200_2.png) [@grahamstark](https://discourse.julialang.org/u/grahamstark)\
**Post date:** [February 13, 2025, 11:06pm UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/29 "2025-02-13T23:06:28Z")

</div>

Should there not be some more formal forum for proposals like this, the way Python has [Pep](https://peps.python.org/)?

---

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [February 14, 2025, 12:32am UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/30 "2025-02-14T00:32:39Z")

</div>

I think technically the most “correct” place for big feature request threads is supposed to be [Github Discussions](https://github.com/JuliaLang/julia/discussions) although I forget who told me that

although empirically it seems like many big proposals start as HackMD docs (e.g. [1](https://hackmd.io/NnLXBeoyRymWgPtHYlW7-A?view#New-Builtin-functions) [2](https://hackmd.io/@tecosaur/SyMYFLhG1g) [3](https://hackmd.io/@jakobnissen/SksGljkfkl) [4](https://hackmd.io/BbEw0_B4Q8uDSS34LOvpCw)) that get linked here or in the Slack or Zulip communities and then discussion continues on the doc until it becomes refined enough to graduate to a PR, at which point discussion happens on the PR.

---

<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:** [February 14, 2025, 3:08am UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/31 "2025-02-14T03:08:58Z")

</div>

> [@grahamstark](#):
>
> Should there not be some more formal forum for proposals like this, the way Python has [Pep](https://peps.python.org/)?

Julia had _Juleps_ already, but they fell into disuse. This was a new attempt, but likewise didn’t seem to get any traction, I think:

> <https://github.com/JuliaLang/julia/issues/55334>
>
> Many programming languages have very formal processes for implementing language …enhancements (such as the following)
> 1. https://github.com/JuliaLang/Juleps
> 2. https://openjdk.org/jeps/1
> 3. https://peps.python.org/pep-0001/
> 4. https://isocpp.org/std/submit-a-proposal
> 
> Coming from a Julia perspective of "just make a PR", these all are incredibly formal (especially for a relatively small language with a tightly knit culture), but especially since Julia as a language has matured fairly significantly over the past few years, we do likely want to increase the amount of process and effort for adding new features to Base (both to improve the quality of features that get added and to discourage adding to Base by default).
> 
> It's worth noting that Julia already has a process for this https://github.com/JuliaLang/Juleps, but it is very optional (at time of writing there only appear to be 9 entries), likely due to the added inconvenience of requiring a pull request to a separate repository. I think the idea and use of the Julep process for the changes that have used it has worked well, but I think the organization has meant that we do not use them as frequently as we should.
> 
> As such, I think we should likely adopt a process less formal than that of other languages, but more formal than what we have now. As a rough draft of one (stealing lots here from python's PEP1 since it seems pretty good overall).
> 
> \> Each Pull request introducing new functionality should include (either directly or by link to some external document such as a Julep, github issue, or HackMD document):
> \> 1. Abstract – a brief description of the technical issue being addressed.
> \> 2. Motivation – It should clearly explain why the existing language specification is inadequate to address the problem that the JEP solves. This can include collecting documented support for the JEP from important projects in the Julia ecosystem.
> \> 3. Rationale – The rationale fleshes out the specification by describing why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale should provide evidence of consensus within the community and discuss important objections or concerns raised during discussion.
> \> 4. Specification – The technical specification should describe the syntax and semantics of any new language feature.
> \> 5. Backwards Compatibility – All JEPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The JEP must explain how the author proposes to deal with these incompatibilities.
> \> 6. Security Implications – If there are security concerns in relation to the JEP, those concerns should be explicitly written out to make sure reviewers of the JEP are aware of them.
> \> 7. Reference Implementation – If possible, the code should be implemented in a (possibly unregistered) package. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of “rough consensus and running code” is still useful when it comes to resolving many discussions of API details. The final implementation must include test code, documentation, and news entries as appropriate.
> \> 8. Rejected Ideas – Throughout the discussion of a JEP, various ideas will be proposed which are not accepted. Those rejected ideas should be recorded along with the reasoning as to why they were rejected. This both helps record the thought process behind the final version of the JEP as well as preventing people from bringing up the same rejected idea again in subsequent discussions. In a way this section can be thought of as a breakout section of the Rationale section that is focused specifically on why certain ideas were not ultimately pursued.
> \> 9. Open Issues – While a JEP is in draft, ideas can come up which warrant further discussion. Those ideas should be recorded so people know that they are being thought about but do not have a concrete resolution. This helps make sure all issues required for the JEP to be ready for consideration are complete and reduces people duplicating prior discussion.
> 
> This is just a draft, but when finalized, I think it should go into the contributing guidelines and possibly be part of a template for feature PRs

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [February 17, 2025, 6:55am UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/32 "2025-02-17T06:55:46Z")

</div>

This is seemingly parenthetical to the original discussion (sorry for derailing it somewhat), but I feel that the term “multiple dispatch” is often misused. This may be my misunderstanding, and I am happy to be corrected.

Multiple dispatch in julia means that methods are specialized for multiple argument types. If only one argument is involved in a call, this is no different from single dispatch, and `f(x)` in Julia is equivalent to `x.f()` in python. What makes julia differ from python’s dispatch is when we have `f(x, y)`, which is different from python’s `x.f(y)`, as python usually can’t specialize on `y` automatically, whereas julia does specialize on both the argument types.

The example presented above:

```julia
module MultipleDispatch
function foo(x::Int32)::Int32
    return x + 2
end

function foo(x::Int64)::Int64
    return x + 3
end

function foo(x::Float64)::Float64
    return x + 4
end
end

```

has different methods associated with each type, but this is still using single-dispatch on the only argument. From what I understand, the duck-typing example is where a generic method is provided to define the behavior for an abstract type, and julia automatically compiles specialized method instances for specific concrete types. If I understand correctly, multiple dispatch is an extension of this to have method instances specialize on more than one argument.

Perhaps the term is being used differently than what I might be familiar with, so it might help if this is clarified.

In any case, this isn’t the topic of discussion, and I agree that interfaces and static checking will be a great step. Looks like the devs are already thinking about this.

---

<div class="post-metadata">

**Author:** ![abraemer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraemer/32/51403_2.png) [@abraemer](https://discourse.julialang.org/u/abraemer)\
**Post date:** [February 17, 2025, 12:09pm UTC](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846/33 "2025-02-17T12:09:03Z")

</div>

Sorry to continue the derailing but let me try to clarify the terminology and concepts involved a bit more. I think that this explanation might also be helpful for @Solmantos to understand these concepts better.

Generally speaking, calling a function in Julia involves 2 orthogonal concepts:

- Multiple _dispatch_. This describes the process by which Julia figures out _which method_ of the function it should call.
- Function _specialization_. This is the process in which Julia creates an _instance of a method_ for a set of concrete parameter types (which it then compiles).

Unfortunately these 2 concepts are somewhat mixed up most of the times and even [the manual](https://docs.julialang.org/en/v1/manual/methods/#man-method-specializations) isn’t very clear about the terminology.

Let me walk you through the whole process with an example to clarify the terms and different steps.

1. We define a function. This step is often times not done explicitly but I mention it here for conceptual reasons.

```julia
function foo end

```

1. We then add methods to this function by further definitions:

```julia
function foo(a::Number)
    print("This is a number: $a")
end
function foo(str::String)
    print("Got a string: $str")
end

```

1. Now we have 1 function with 2 methods. To verify you can do:

```julia-repl
julia> methods(foo)
# 2 methods for generic function "foo" from Main:
 [1] foo(str::String)
     @ REPL[2]:1
 [2] foo(a::Number)
     @ REPL[1]:1

```

1. Let us now call this function with an Int: `foo(5)`. What Julia does in the background is to first run the dispatch to figure out which method is the most specific one. This is where multiple dispatch plays a role in general. In this case it it is `foo(::Number)`. You can check with:

```julia-repl
julia> @which foo(5)
foo(a::Number)
     @ Main REPL[1]:1

```

1. Now Julia will call this method. Generally, when calling a method Julia _specializes_ the method for the concrete argument types (here: `Int`) which results in a `MethodInstance` which is then executed. You can see this by inspecting the `Method`:

```julia-repl
julia> foo(5) # run once to create the MethodInstance for Int
This is a number: 5

julia> typeof(@which foo(5))
Method

julia> (@which foo(5)).specializations
MethodInstance for foo(::Int64)

```

1. (Extra): In my example, if you run that function again with the Float64 `2.0` then this _dispatches_ to the same _method_ but, due to specialization, Julia will create and compile a new `MethodInstance`:

```julia-repl
julia> foo(2.0)
This is a number: 2.0
julia> (@which foo(5)).specializations # looks a bit different now, but just contains 2 MethodInstances
svec(MethodInstance for foo(::Int64), MethodInstance for foo(::Float64), nothing, nothing, nothing, nothing, nothing)

```

Julias performance now stems from 2 facts:

- If the types are known at compile time, multiple dispatch happens at compile time
- Due to specialization, every `MethodInstance` receives only concrete types and thus can be compiled in the most efficient way.

[Previous page](https://discourse.julialang.org/t/proposal-adding-optional-static-interface-traits-checking-to-julia/125846.md?page=1)
