# Semantics of :: in return type vs. argument type annotations

**URL:** https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838
**Category:** General Usage
**Created:** [February 6, 2024, 7:29pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838 "2024-02-06T19:29:14Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![mnemnion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mnemnion/32/206596_2.png) [@mnemnion](https://discourse.julialang.org/u/mnemnion)
#### Post date: [February 6, 2024, 7:29pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/1 "2024-02-06T19:29:14Z")

</div>

> [@How can we maintain easy generics in the face of demand for a “safe” language?](https://discourse.julialang.org/t/how-can-we-maintain-easy-generics-in-the-face-of-demand-for-a-safe-language/109734/18):
>
> Ah, right, I forgot that return type annotations are `convert` statements, not type checks, making them much less useful.

That utterly baffled me when I found out. A return annotation has the same appearance as an argument annotation, but a subtly different semantic.

I still use them, but I don’t like having to think about whether I should. I’d like Julia 2.0 to change `(a,b)::Type` to have the same semantics as `(a::Type)` and make `(a,b)::Type()` the converting version. Or something like that, now we’re conflating constructors and `convert`, but that’s closer imho.

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [February 6, 2024, 7:43pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/2 "2024-02-06T19:43:20Z")

</div>

> [@mnemnion](#):
>
> A return annotation has the same appearance as an argument annotation, but a subtly different semantic

It’s more similar to a local-variable declaration. If you declare `local x::T`, then assignments `x = y` call `convert(T, y)`.

(This was the motivation for the behavior, in fact, as discussed in [add `function f()::T` return type declaration syntax by JeffBezanson · Pull Request #16432 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/16432))

---

<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 6, 2024, 7:45pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/3 "2024-02-06T19:45:29Z")

</div>

> [@mnemnion](#):
>
> That utterly baffled me when I found out. A return annotation has the same appearance as an argument annotation, but a subtly different semantic.

`::` just declares a type in several contexts where it needs to do something different. I’d say callable/argument annotations actually stand out because the types are constrained by dispatch, not `convert` or `typeassert`. If we’re speculating about 2.0, I expect the latter would be simplified if it’s decided to ([Require constructors and `convert` to return objects of stated type? · Issue #42372 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/42372)), or error, hence builtin `typeassert`s.

---

<div class="post-metadata">

### Author: ![mnemnion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mnemnion/32/206596_2.png) [@mnemnion](https://discourse.julialang.org/u/mnemnion)
#### Post date: [February 6, 2024, 8:07pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/4 "2024-02-06T20:07:52Z")

</div>

> [@Benny](#):
>
> `::` just declares a type in several contexts where it needs to do something different.

It doesn’t absolutely have to do something different though, in fact it easily couldn’t. Struct fields and variable assertions convert, that’s good, function arguments don’t convert, also good, function return annotations behave like the former, not the latter. Two things there: I find it confusing, and it isn’t what I want.

Confusing:

```julia
julia> f(a::Int) = a
f (generic function with 1 method)

julia> f(0x01)
MethodError: no method matching f(::UInt8)

julia> f(a::Integer) = a
f (generic function with 2 methods)

julia> f(0x01)
0x01

julia> g(a::Integer)::Int = a
g (generic function with 1 method)

julia> g(0x01)
1

```

All those type declarations are on the same line, but they do different things, and I want `g` to throw an error, or I would have said `::Integer`, or made it `g(a::Number) = Int(a)`, something like that. There’s no easy way to get that behavior, which causes this kind of problem:

```julia
julia> h(a::Number)::UInt8 = a
h (generic function with 1 method)

julia> h(1)
0x01

julia> h(256)
InexactError: trunc(UInt8, 256)

```

Which is a big part of what type annotations are supposed to prevent. In a real method `a` would be coming from somewhere else and I would expect it to be a `UInt8` already, and it only blows up when the value can’t be converted. So yeah, I’d like `h(a)::UInt8()` for the converting kind, that way I know it can blow up at runtime. Technically the other one blows up at runtime as well, Julia being dynamic and all, but if the type signature is wrong it will blow up the first time, rather than at some random future point when the value is too large.

---

<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 6, 2024, 8:53pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/5 "2024-02-06T20:53:16Z")

</div>

> [@mnemnion](#):
>
> Struct fields and variable assertions convert

and `setindex!`

If you make a return type annotation that doesn’t `convert`, only `typeassert` like right-hand expressions’ annotations (which makes sense because you can’t assign to a return value like you can with a variable, field, or index), then I’d rather there not be 2 nearly identical return annotation syntaxes. `UInt8()` isn’t even syntactically consistent with constructor calls. Instead you could just write a `convert` call at the end, and it’s not much more to write than a return type annotation.

> [@mnemnion](#):
>
> `g(a::Integer)::Int = a`

> [@mnemnion](#):
>
> `h(a::Number)::UInt8 = a`

> [@mnemnion](#):
>
> In a real method `a` would be coming from somewhere else and I would expect it to be a `UInt8` already

I’m guessing that return type annotations were chosen to also `convert` because it was already expected that more proper type annotations like `h(a::UInt8)::UInt8 = a` would be written as part of type stability principles, so it ends up as a nice header-level shorthand for an ending `convert` step. It still prevents impossible `convert`s like in `h(256)`. Even if return type annotations don’t `convert`, it’d be strange to have had the foresight to restrict the return type but not the input type.

---

<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 6, 2024, 8:53pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/6 "2024-02-06T20:53:27Z")

</div>

> [@mnemnion](#):
>
> I want `g` to throw an error, or I would have said `::Integer`, or made it `g(a::Number) = Int(a)`, something like that. There’s no easy way to get that behavior, which causes this kind of problem:

Yes, there’s a way to do that — use the `::` operator in a “type-assert” context:

```julia-repl
julia> h(a::Number) = return a::UInt8
h (generic function with 1 method)

julia> h(123)
ERROR: TypeError: in typeassert, expected UInt8, got a value of type Int64

```

Yes, there are different behaviors in different contexts — and it is documented in `::`'s docstring (although it looks like that’s not making it into the online manual right now).

```julia-repl
help?> ::
search: ::

  ::

  The :: operator either asserts that a value has the given type, or
  declares that a local variable or function return always has the given
  type.

  Given expression::T, expression is first evaluated. If the result is
  of type T, the value is simply returned. Otherwise, a TypeError is
  thrown.

  In local scope, the syntax local x::T or x::T = expression declares
  that local variable x always has type T. When a value is assigned to
  the variable, it will be converted to type T by calling convert.

  In a method declaration, the syntax function f(x)::T causes any value
  returned by the method to be converted to type T.

  See the manual section on Type Declarations.

```

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 6, 2024, 9:15pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/7 "2024-02-06T21:15:57Z")

</div>

It is nice for a syntax to do just one job rather than depending on context.

In Rust,

```rust
fn f(x: i64) -> f64 {
    x
}

```

fails with

```julia
1 | fn f(x: i64) -> f64 {
  | --- expected `f64` because of return type
2 | x
  | ^ expected `f64`, found `i64`
  |
help: you can convert an `i64` to an `f64`, producing the floating point representation of the integer, rounded if necessary
  |
2 | x as f64
  | ++++++

```

and you have to do

```rust
fn f(x: i64) -> f64 {
    x as f64
}

```

`x as T` is maybe a little verbose for Julia, but maybe there’s another syntax that could distinguish `typeassert` from `convert` — or just use functions and macros.

---

<div class="post-metadata">

### Author: ![rdavis120](https://avatars.discourse-cdn.com/v4/letter/r/b5a626/32.png) [@rdavis120](https://discourse.julialang.org/u/rdavis120)
#### Post date: [February 6, 2024, 9:56pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/8 "2024-02-06T21:56:57Z")

</div>

> [@mbauman](#):
>
> Yes, there’s a way to do that — use the `::` operator in a “type-assert” context:

Thanks for explaining this. The type assert also works a lot better with JET.jl

---

<div class="post-metadata">

### Author: ![mnemnion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mnemnion/32/206596_2.png) [@mnemnion](https://discourse.julialang.org/u/mnemnion)
#### Post date: [February 6, 2024, 10:16pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/9 "2024-02-06T22:16:58Z")

</div>

> [@Benny](#):
>
> If you make a return type annotation that doesn’t `convert`, only `typeassert` like right-hand expressions’ annotations (which makes sense because you can’t assign to a return value like you can with a variable, field, or index), then I’d rather there not be 2 nearly identical return annotation syntaxes.

In the abstract, I’m fine with just changing the semantics in 2.0 land. I think having an after-the-parens syntax which means what `(...)::Type` used to mean might make it easier to convert code, but then again, maybe most code out there is using it for assertion/restriction, and the implicit conversion is just along for the ride. Certainly true in my case.

That said `(...)::Type()` is kinda ugly (pure aesthetics, ymmv) but I think it’s pretty clear, and I kiiiinda see the argument for having the ability to specify a conversion as part of the signature.

> [@mbauman](#):
>
> Yes, there’s a way to do that — use the `::` operator in a “type-assert” context:

Sure, and if there are a bunch of returns, I can just scatter those all over the function, instead of putting it in the signature where it belongs.

Or, what I’d prefer: the in and out parts of the function signature behave consistently, and people who want conversion can ask for it. It’s not an uncommon request, so make it part of the function signature also, since “give me one of these” doesn’t always want to carry “only if it already is” as the semantics. I’m not in love with `(...)::UInt8()` but it’s suggestive of the difference at least.

> [@mbauman](#):
>
> Yes, there are different behaviors in different contexts — and it is documented in `::`'s docstring (although it looks like that’s not making it into the online manual right now).

A surprising semantic which is (again, just my opinion) a worse choice than the alternative, being nonetheless documented, is at most mitigating. I’d rather write Julia than complain about it, but this is the one thing I would emphatically endorse changing.

The question is whether a return signature should be L-value coded, or R-value coded, and I think R but Julia does L.

```julia
f(a)::UInt8 = a 

 # should this mean:

u::UInt8 = f(a) 

# or should it mean 

u = f(a)::UInt8

# ???

```

Right now it means the former, and I think the latter is much more consistent. Notice how `f(a)::UInt8;` and `f(a)::UInt8 = a` mean the same thing now? Which is the same thing you get in the rest of the signature, whether calling or defining it.

To me it isn’t about `::` having a single meaning, which wouldn’t be great, I think the L value behavior is just fine. It’s specifically about what is most useful and consistent in a function signature. The other one.

---

<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 6, 2024, 10:49pm UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/10 "2024-02-06T22:49:47Z")

</div>

> [@mbauman](#):
>
> `return a::UInt8`

You’d need to do this for possibly multiple `return` points. The return type annotation provides a behavior without forcing you to refactor into 1 `return` point.

> [@mnemnion](#):
>
> To me it isn’t about `::` having a single meaning, which wouldn’t be great, I think the L value behavior is just fine. It’s specifically about what is most useful and consistent in a function signature. The other one.

This is subjective and it’s been decided the other way for v1, but I would lean towards agreeing. I don’t really mind inserting multiple explicit `convert` calls, I’d appreciate the clarity, and right now I can’t opt out of the `convert`.

---

<div class="post-metadata">

### Author: ![mnemnion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mnemnion/32/206596_2.png) [@mnemnion](https://discourse.julialang.org/u/mnemnion)
#### Post date: [February 7, 2024, 12:07am UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/11 "2024-02-07T00:07:31Z")

</div>

> [@Benny](#):
>
> This is subjective and it’s been decided the other way for v1, but I would lean towards agreeing. I don’t really mind inserting multiple explicit `convert` calls, I’d appreciate the clarity, and right now I can’t opt out of the `convert`.

All of this is definitely 2.0 kvetching, and most of the time the difference isn’t important, so I just add the return annotation and carry on with my day.

As you say here:

> [@Benny](#):
>
> If we’re speculating about 2.0, I expect the latter would be simplified if it’s decided to ([Require constructors and `convert` to return objects of stated type? · Issue #42372 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/42372)), or error, hence builtin `typeassert`s.

Imposing a requirement that type constructors must always return their type is a good fit with changing the semantics from `convert` to `typeassert`, because the user can replace all `return a` with `return Type(a)` and nothing unexpected can happen. At that point, `f(a)::Type()` is fairly clearly saying “transform all `return a` into `return Type(a)`”. I still don’t like how that looks, maybe `f(a) as {Type}`? I’m not in love with that either, but the other one is really busy as lambda syntax: `(a,b)::T() -> a * b`! Don’t love it. `(a,b) as {T} -> a * b`?

The `::Type()` form does indicate that an implicit function is being called on the return value. I think it’s suggestive that there’s no clear way to represent a return `typeassert` if `f(a)::Type` means conversion (as it does now), though.

Edit: another point in favor of the type-call syntax is one can mix and match in Tuple types: `f(a)::Tuple{Int,UInt8()}`, the first return must be a literal `Int`, the second one goes through `UInt8(retval)`. `Tuple{A,B}()` converts both. As much as I dislike how it looks, it _reads_ perfectly clearly to me.

The distinctive thing about function returns is that they can come from a number of different places. Compare to parameter, field, and variable definitions, and R-value assertions: every other use of `::` applies to one item of syntax. So it’s a good candidate for having a way to express both intentions.

---

<div class="post-metadata">

### Author: ![sadish-d](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sadish-d/32/48058_2.png) [@sadish-d](https://discourse.julialang.org/u/sadish-d)
#### Post date: [February 7, 2024, 3:45am UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/12 "2024-02-07T03:45:20Z")

</div>

I wish `::` meant `typeassert` and only that. To have to remember that it may silently `convert` things adds cognitive load. I don’t think I have ever wanted things implicitly converted (though implicit type promotion is sensible, otherwise you couldn’t do `1 + 1.`).

I was initially excited that I could write functions with the signature

```julia
function foo(x::Integer)::Float64
	answer = pi * x

	return answer
end

```

But since I learned that it applies conversion, I do:

```julia
function foo(x::Integer)
	answer = pi * x

	return answer::Float64
end

```

So its more digging around to see what type a function returns.

The implicit conversion makes it hard to find bugs. Say I end up writing the following:

```julia
_1st(x::Vector)::Float64 = first(x)

```

Now if `::` were just a `typeassert`, I would probably realize after some testing that I meant to write:

```julia
_1st(x::Vector{<:Number})::Float64 = first(x)

```

Instead, I go on blissfully doing things like `_1st(['a', 'b'])` without errors.

---

<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 7, 2024, 4:17am UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/13 "2024-02-07T04:17:57Z")

</div>

> [@mnemnion](#):
>
> because the user can replace all `return a` with `return Type(a)` and nothing unexpected can happen

Not necessarily. `T(x)` and `convert(T, x)` could only be interchangeable if `T` is primitive or if conversion is exactly equivalent to constructing a struct `T`’s fields from `x`. For a counterexample, `Ref(Ref(1))` versus `convert(Ref, Ref(1))`.

> [@mnemnion](#):
>
> f(a)::Tuple{Int,UInt8()}

That is even more horrific to me because that’s not a valid type. I find it pretty important especially to beginners that different things are not written in visually similar but non-interchangeable ways for different contexts. The human brain likes to connect and extrapolate, so either keep things the same or very different.

> [@sadish-d](#):
>
> I wish `::` meant `typeassert` and only that.

Even for left hand annotations? That could be helpful, actually. Just throws an error in your face if you didn’t intend for a conversion.

Taking this all together, it could be justifiable that there be a different annotation syntax for additional conversion, could even make it an infix operator to replace `convert` calls.

> [@mnemnion](#):
>
> I’m not in love with that either, but the other one is really busy as lambda syntax: `(a,b)::T() -> a * b`! Don’t love it. `(a,b) as {T} -> a * b`?

Funny you mention that because I’m not a fan of how there’s multiple function syntaxes (named `function`, named `=`, nameless `function`, anonymous `->`. It’d be more okay if they had the same features, but parsing difficulties means they actually [don’t](https://discourse.julialang.org/t/why-does-julia-have-multiple-ways-to-define-functions/102424/10). If `function` and `end` were too long, I’d rather they’d just be shortened. Rust does `fn` which seems clear enough, but I wouldn’t know what could replace `end`; my first instinct is a simple spaced period but seems weird. Anyway, if `->` syntax is removed, then `->` could actually be a drop-in replacement for `::` meaning “assert but also convert first”.

---

<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 7, 2024, 5:14am UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/14 "2024-02-07T05:14:41Z")

</div>

It’s worth noting that `::` is pure syntax and doesn’t have a functional form (as most operators in the language do). It’s also worth noting that it has _six_ distinct contexts in which it is used:

- Assert that an expression `isa` particular type
- Declare the type:
  - of a local or global variable
  - of a struct field
  - of a method’s return value
  - of the positional arguments of a method
  - of the keyword arguments of a method

Perhaps this is overly pedantic, but this discussion isn’t so much about the behavior of `::` itself, but rather how things with a declared type should behave **after** you’ve defined them.

For example, the `::`-declared method arguments (even kwargs!) don’t do conversion. It gets into tree-falling-in-forest-with-no-one-to-hear-land if you ask if the positional args even do type assertions.

For structs, it’s the default behavior that defines `setproperty!` as convert-and-assert — a behavior that can be overridden.

Would it be better if typed variables and method-returns just asserted? Maybe, it’d be a different choice. Is the syntax `::T()` available? No, it’d be quite confused (even as a breaking change) as it currently means calling a function to see what type to use. Would it be better to have _two_ flavors of declared types, one that does both convert-and-assert and one that only asserts? Definitely not.

---

<div class="post-metadata">

### Author: ![sadish-d](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sadish-d/32/48058_2.png) [@sadish-d](https://discourse.julialang.org/u/sadish-d)
#### Post date: [February 7, 2024, 5:41am UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/15 "2024-02-07T05:41:11Z")

</div>

Maybe the confusion does in fact come from the word “declare”. I’ve seen the word used in the documentation too. What does it mean to “declare” a type when the only options available are asserting or converting?

So when I “declare” `x::Int = 0`, it’s a conversion. But when I “declare” the type of a function argument in `f(x::Int)`, it is not a conversion but an assertion.

To me, it would be clearer if `::` just meant assert (yes, in the left hand side as well Benny). If `x::Float64 = 0` gives me an error, okay, I’ll go back and add a dot. But if someone is comfortable with it meaning convert, maybe it’s not too strange to them to expect `f(x::Int)` to actually do something like `f(Int(x::Any))`.

---

<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 7, 2024, 6:14am UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/16 "2024-02-07T06:14:31Z")

</div>

> [@sadish-d](#):
>
> But when I “declare” the type of a function argument in `f(x::Int)`, it is not a conversion but an assertion.

If that is a call, yes. If that is a method definition, then no, dispatch will guarantee `x isa Int`, no `convert` or `typeassert` needed. To me, `::` is very consistently guaranteeing something will have a type if the program continues. Removing `convert` still won’t make `::` do so the same way everywhere.

---

<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 7, 2024, 6:45am UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/17 "2024-02-07T06:45:49Z")

</div>

> [@sadish-d](#):
>
> Maybe the confusion does in fact come from the word “declare”

Oh that’s interesting — and I think it gets to the crux of the matter. Type declarations are neither converts nor asserts in and of themselves. Asserting and converting are actions. Declarations are statements of fact about something, more like a label than an action.

> [@sadish-d](#):
>
> So when I “declare” `x::Int = 0`, it’s a conversion.

Not really. It’s two steps: first you declare that `x` will only ever be of type `Int`, and then you immediately perform an assignment. It’s this:

```julia
local x::Int
x = typeassert(convert(Int, 0), Int)

```

You might do it all in the same line or the assignment might be miles away. But the behavior is really in how Julia assigns to a typed variable, and not in the declaration itself.

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 7, 2024, 7:28am UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/18 "2024-02-07T07:28:05Z")

</div>

There is another potential source of confusion, which is that `=` returns the value on the right, pre-conversion:

```julia
julia> x::Float64 = 1
1

julia> x
1.0

```

---

<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 7, 2024, 8:16am UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/19 "2024-02-07T08:16:53Z")

</div>

Giving an assignment expression the value of the left hand variables would be ambiguous now:

```julia
julia> x::Float32=0; y::Float64=0;

julia> x = y = 1
1

julia> x, y
(1.0f0, 1.0)

```

That said, if we did away with `::` conversions then the right hand value would match all the variables’ values, so it doesn’t matter which side we choose.

---

<div class="post-metadata">

### Author: ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)
#### Post date: [February 7, 2024, 8:21am UTC](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838/20 "2024-02-07T08:21:59Z")

</div>

> [@Benny](#):
>
> Giving an assignment expression the value of the left hand variables would be ambiguous now:

Couldn’t you just say control flows right to left so the value is the leftmost?

[Next page](https://discourse.julialang.org/t/semantics-of-in-return-type-vs-argument-type-annotations/109838.md?page=2)
