# On type annotations

**URL:** https://discourse.julialang.org/t/on-type-annotations/116305
**Category:** New to Julia
**Created:** [June 27, 2024, 1:13pm UTC](https://discourse.julialang.org/t/on-type-annotations/116305 "2024-06-27T13:13:24Z")
**Posts on this page:** 20
**Page:** 4

<div class="post-metadata">

### Author: ![Jorge\_Vieyra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jorge_vieyra/32/6527_2.png) [@Jorge\_Vieyra](https://discourse.julialang.org/u/Jorge_Vieyra)
#### Post date: [July 2, 2024, 9:18am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/61 "2024-07-02T09:18:44Z")

</div>

But we want them to be supported by other packages inside the company. And also, we want to use functionality provided by other open source packages.

---

<div class="post-metadata">

### Author: ![Matthijs\_1971](https://avatars.discourse-cdn.com/v4/letter/m/bb73d2/32.png) [@Matthijs\_1971](https://discourse.julialang.org/u/Matthijs_1971)
#### Post date: [July 2, 2024, 9:19am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/62 "2024-07-02T09:19:52Z")

</div>

Thank you all for sharing your expertise. I think everything has been said about this topic now. On my side an evaluation of the input will be made and we will rethink the coding standard.

---

<div class="post-metadata">

### Author: ![Jorge\_Vieyra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jorge_vieyra/32/6527_2.png) [@Jorge\_Vieyra](https://discourse.julialang.org/u/Jorge_Vieyra)
#### Post date: [July 2, 2024, 9:19am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/63 "2024-07-02T09:19:59Z")

</div>

No, `Union`s are _not_ the solution for everything. Abstract interfaces are for the most part and _sometimes_ unions.

---

<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 2, 2024, 9:20am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/64 "2024-07-02T09:20:24Z")

</div>

This style guide, at its best, seems to really be an attempt to retrofit static typing on top of Julia. In any case perhaps it’d be good to take a step back and analyze the requirements here.

What is the actual goal here? Is it the compile-time safety allowed by static typing? Or is it just that the relevant programmers are familiar with statically-typed languages and can’t be bothered with learning Julia properly?

Honestly, I think it’s the latter, in which case it’d be better to either invest in learning Julia, writing good test-suites, etc.; or switch to C++ or something.

If it’s the former:

1. To get the safety benefits of static typing (error before run time), what is required is a _sound_ static analyzer. A sound type checker of a Turing-complete language necessarily rejects a lot of valid programs, effectively relinquishing a lot of expressive/descriptive power: [Chapter 1 — Program Analysis](https://aviatesk.github.io/posts/introduction-to-static-analysis/chapter1/)
2. A lot of programming languages that are commonly considered as “statically typed” don’t actually have _sound_ type checkers, so they don’t actually provide the advertised safety (they may error at run time when a programmer would expect a compile-time error).
3. A useful sound static type checker for Julia would perhaps be a research project. The next best thing is JET.jl, by @aviatesk.

I’m not able to give a good suggestion here, but I think a workflow where each method has to be either:

1. cleared by JET.jl for the declared argument types, or
2. somehow marked as “unsafe” (in the Rust sense)

… would be better than requiring this coding standard.

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [July 2, 2024, 9:23am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/66 "2024-07-02T09:23:37Z")

</div>

Your example shows literal construction, which is unrelated to the input arguments. The problem arises when assignment assertions depend on input arguments. And with union inputs those can’t be hard-coded.

So

```julia
function foo(x::Float64) 
    y::Float64 = x + 1
   ... 
end

```

Is possible, but this fails with unions. Do you only need assignment assertions with literals? Why not with the above?

---

<div class="post-metadata">

### Author: ![Jorge\_Vieyra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jorge_vieyra/32/6527_2.png) [@Jorge\_Vieyra](https://discourse.julialang.org/u/Jorge_Vieyra)
#### Post date: [July 2, 2024, 9:32am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/67 "2024-07-02T09:32:55Z")

</div>

@nsajko This is a very insightful comment. Thanks!  
The only problem with `JET.jl` is that it doesn’t report exactly what companies like TIOBE expect for their checkers. Also, it is not a paragon of user-friendliness

---

<div class="post-metadata">

### Author: ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)
#### Post date: [July 2, 2024, 10:25am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/68 "2024-07-02T10:25:09Z")

</div>

> [@Matthijs\_1971](#):
>
> Because after the annotation, the type of `vec` can not change any more. That improves maintainability and readability of the code.

My current sense is that while the above is the intention, the proposed solution of a left hand type assertion, binding the variable to the type, also has the effect of inviting conversion when it is not desired.

In a prior discussion, a `@returnassert` macro was developed to just assert the return type of a function, without any implicit conversion.

> [@Semantics of :: in return type vs. argument type annotations](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/56):
>
> An issue with return statements is that they are not always needed, and I don’t see how a macro could easily determine all possible return values of a function when not all of them are associated with an explicit return. For a macro-based approach, I’d rather use an approach in which a function definition like this: @returnassert function foo(x, y) :: Float64 if (rand() \> 0.5) return x + y end # no explicit return here x - y end is transformed into something like this…

> [@Matthijs\_1971](#):
>
> ```julia
> other::OtherStruct = calculate_other_struct(value, obj, vec)
> 
> ```

The above line invites implicit conversion. A macro that rewrites all left-hand assertions to be as follows or lowers the left-hand assertions and then removes the conversions might be a more appropriate tool for static typing.

```julia
    other = calculate_other_struct(value, obj, vec)
    other::OtherStruct

```

Here we ensure both that

1. `other` does not change type
2. The right hand side also does not change type.

What I imagine with the large number of structs is there actually may be a need to `convert` between the structs and thus unintentional conversion could be a real issue for these structs, not just numeric types.

In summary, my thought here is that we need another tool here other than the left-hand variable assertion or the function return type assertion. In the near term, this may mean a specialized macro, like the referenced `@returnassert` to accomplish this goal without the implicit conversion side effects. In the long term, we may need a different kind of type assertion or assignment syntax.

Somewhat missing from the discussion are the use of type parameters to both enforce expectations and provide flexibility.

---

<div class="post-metadata">

### Author: ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)
#### Post date: [July 2, 2024, 11:47am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/69 "2024-07-02T11:47:36Z")

</div>

> [@DNF](#):
>
> Your example shows literal construction, which is unrelated to the input arguments. The problem arises when assignment assertions depend on input arguments. And with union inputs those can’t be hard-coded.

For what it’s worth you can do things like

```julia
function foo(x::Union{Float32, Float64})
    T = typeof(x)
    y::T = x + 1
end

```

although it would have to be `T::DataType = typeof(x)` to follow the proposed standard.

---

<div class="post-metadata">

### Author: ![kevbonham](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kevbonham/32/216165_2.png) [@kevbonham](https://discourse.julialang.org/u/kevbonham)
#### Post date: [July 2, 2024, 12:43pm UTC](https://discourse.julialang.org/t/on-type-annotations/116305/70 "2024-07-02T12:43:45Z")

</div>

> [@stevengj](#):
>
> Why is your `error` message better?

Well, to you’re right in this case, I’ve just replicated the Method error, but one can talk about the interface, reference the docs, or provide a suggestion about the method that needs to be defined. When I first started developing in Julia, I hit `MethodError`s all of the time and they felt inscrutable.

Writing these reminders to myself were invaluable.

---

<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 2, 2024, 7:54pm UTC](https://discourse.julialang.org/t/on-type-annotations/116305/71 "2024-07-02T19:54:00Z")

</div>

> [@Matthijs\_1971](#):
>
> Yes, this discussion is drifting out of control with all this focus on numerical types and too little on the myriad of structs we have defined in our company.

> [@Matthijs\_1971](#):
>
> The difference is that structs defined in our company won’t be supported by third party packages mentioned earlier in the discussion.

Makes no difference to the topic of type annotations and dispatch, and the base numerical types only served as accessible examples. Obviously nobody outside your company knows the structs you’re talking about.

> [@nsajko](#):
>
> I’m not able to give a good suggestion here, but I think a workflow where each method has to be either:
> 
> 1. cleared by [JET.jl](https://juliahub.com/ui/Packages/General/JET) for the declared argument types

Can sorta pull this off in one go for a main function but doesn’t cover anything else done interactively.

---

<div class="post-metadata">

### Author: ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)
#### Post date: [July 2, 2024, 10:03pm UTC](https://discourse.julialang.org/t/on-type-annotations/116305/72 "2024-07-02T22:03:42Z")

</div>

> [@mkitti](#):
>
> As many have noted above, these are not merely annotations but also have consequences that could be harmful in several ways including negatively impacting performance, reducing reusability, and throwing errors you did not intend to throw.

I do not intend to derail the discussion, and I feel that this intervention is relevant to the OP as well. This being said…

I understand the usability compromise, but I am pretty unease that simply being explicit about types can end up in:

- negatively impacted performance
- unexpected errors

As long as there is no violation of the business logic of the said code, it seems like - at least for sanity’s sake - type annotation _should_ do no harm (I am not challenging your claims - I understand that annotating stuff exhaustively actually harms).

In a way, it feels like “prohibited type annotation” and less “optional type annotation” 🙂

---

<div class="post-metadata">

### Author: ![kevbonham](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kevbonham/32/216165_2.png) [@kevbonham](https://discourse.julialang.org/u/kevbonham)
#### Post date: [July 2, 2024, 10:34pm UTC](https://discourse.julialang.org/t/on-type-annotations/116305/73 "2024-07-02T22:34:40Z")

</div>

> [@algunion](#):
>
> As long as there is no violation of the business logic of the said code, it seems like - at least for sanity’s sake - type annotation _should_ do no harm

I’m glad you said it - I have this feeling too, though lack the confidence in my understanding to say so.

I think two things are true in this thread

1. There are many good reasons _not_ to do tons of type annotation - I think the general advice we give users on this point is right, particularly for open source libraries that expect to work with a lot of the ecosystem. The thread is filled with great examples of this point.
2. If the company sees benefit to this style for their internal code, and are aware of the limitations, by all means do what works for you, and thanks for using Julia in production! 😄

---

<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 2, 2024, 10:36pm UTC](https://discourse.julialang.org/t/on-type-annotations/116305/74 "2024-07-02T22:36:12Z")

</div>

> [@algunion](#):
>
> simply being explicit about types

As extensively discussed, putting a type annotation on a method return type or variable is not “simply being explicit about types”.

I’ll agree though, that type annotations calling `convert` wasn’t a good design. Hopefully something to consider for Julia v2.

---

<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 2, 2024, 11:07pm UTC](https://discourse.julialang.org/t/on-type-annotations/116305/75 "2024-07-02T23:07:59Z")

</div>

> [@nsajko](#):
>
> I’ll agree though, that type annotations calling `convert` wasn’t a good design.

I’m on the fence about this for fields and array elements because implicit conversion there in several languages is about as routine as promotion in base operations, but it really does seem like people expect and want an error, ideally before happening upon it at runtime, at annotated variables and return types, even in the cases where `convert` is implemented. I know the argument that conversion by annotated return type is more convenient than doing it at every exit point, but incorporating the conversion into the method is a very rarely used feature that I actually wouldn’t mind writing out explicitly.

---

<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 2, 2024, 11:10pm UTC](https://discourse.julialang.org/t/on-type-annotations/116305/76 "2024-07-02T23:10:24Z")

</div>

I think even `new` calls `convert`. It’s way too difficult to avoid `convert`.

---

<div class="post-metadata">

### Author: ![stephancb](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephancb/32/14243_2.png) [@stephancb](https://discourse.julialang.org/u/stephancb)
#### Post date: [July 3, 2024, 8:16am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/77 "2024-07-03T08:16:05Z")

</div>

Here is a small experiment (by a physicist):

```julia
function distance_to_target_nota(t) # no type annotations
    c = 299792458e0 # speed of light
    # ... lot of code ...
    # developer does not realize that c is already used
    # remaining comments are "real" for this experimental nonsense function
    #
    foo1(t, coeff) = (t-1)*coeff # effect of warp thruster
    foo2(t, coeff) = (t-1)*coeff*1.1 # warp thruster 2 is slightly better
    dx = 0
    if t>1 # if time too large then warp is used:
        c = 3000 # efficiency coefficient as specified
        dx = foo1(t, c) + foo2(t, c)
    end
    # ... more code ...
    
    return c*t-dx
end

```

has a programming error (reuses variable c) and returns a by five orders of magnitude wrong result for `t>1`. The type annotated version

```julia
function distance_to_target_withta(t::Float64)::Float64 # with type annotations
    c::Float64 = 299792458 # speed of light
    # ... lot of code ...
    # developer does not realize that c is already used
    # remaining comments are "real" for this experimencal nonsense function
    #
    foo1(t::Float64, coeff::Float64)::Float64 = (t-1)*coeff # effect of warp thruster
    foo2(t::Float64, coeff::Float64)::Float64 = (t-1)*coeff*1.1 # warp thruster 2 is slightly better
    dx::Float64 = 0
    if t>1 # if time is too large then warp is used:
        c::Float64 = 3000 # efficiency coefficient as specified
        dx = foo1(t, c) + foo2(t, c)
    end
    # ... more code ...
    
    return c*t-dx
end

```

does not compile and so catches this particular error.

The OP cites “readability and maintainability” as justification for using type annotations everywhere in Julia. This is perhaps debatable, but, I think, they are a tool to prevent some of the common programming errors (similar as in other statically typed languages). I agree that the type annotations are not ideal for this (being too much cluttering etc.). A discussion of other available tools, `@returnassert` , `@inferred` from `Test.jl`, …, and other code analysis tools would be useful, perhaps in another or forked thread.

---

<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 3, 2024, 8:43am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/78 "2024-07-03T08:43:38Z")

</div>

> [@stephancb](#):
>
> does not compile

Not a method compilation error, the syntax error is thrown upon evaluating the method. This is just repurposing a type declaration limitation to error at accidental reassignment in a long stretch of code instead of using conventional programming practices like making a new `local` variables in a new local scope (`let`), splitting a large method into calls of smaller methods, and descriptively disambiguating names. It doesn’t work for something as simple as accidentally reassigned arguments:

```julia
julia> function energy(mass, c::Float64=299792458.0)
         if mass < 0 # sleep deprived code incoming
           c::Int=-1 # inferred ::Union{Float64, Int64}
           mass = c*mass
         end
         mass*c^2
       end
energy (generic function with 2 methods)

julia> energy(2)
1.7975103574736352e17

julia> energy(-2)
2

```

---

<div class="post-metadata">

### Author: ![Matthijs\_1971](https://avatars.discourse-cdn.com/v4/letter/m/bb73d2/32.png) [@Matthijs\_1971](https://discourse.julialang.org/u/Matthijs_1971)
#### Post date: [July 3, 2024, 9:18am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/79 "2024-07-03T09:18:24Z")

</div>

```julia
#!/usr/bin/env julia

function energy(c::Float64)::Float64
    c::String="bbb"
    println(typeof(c))
    return 5.0
end

energy(5.0)

```

Indeed, this just runs without any problems. I did not expect this. I wonder if this is documented and desired Julia behavior or a bug in the language that can be fixed now it is discovered.

---

<div class="post-metadata">

### Author: ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)
#### Post date: [July 3, 2024, 9:22am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/80 "2024-07-03T09:22:32Z")

</div>

> [@Matthijs\_1971](#):
>
> I did not expect this.

Why not? Looks perfectly fine in my eyes.

---

<div class="post-metadata">

### Author: ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)
#### Post date: [July 3, 2024, 9:56am UTC](https://discourse.julialang.org/t/on-type-annotations/116305/81 "2024-07-03T09:56:34Z")

</div>

The argument type annotation is not a local variable type assertion.

You cannot do a new `local` declaration.

```julia
julia> function energy(c::Float64)
           local c::String
       end
ERROR: syntax: local variable name "c" conflicts with an argument

```

[Previous page](https://discourse.julialang.org/t/on-type-annotations/116305.md?page=3)

[Next page](https://discourse.julialang.org/t/on-type-annotations/116305.md?page=5)
