We can add a type annotation to a definition by writing it above:
foo : Foo
foo = bar
However when using backpassing (typically with Result.try) this does not work (the compiler emits a "duplicate name" error, which makes sense when desugaring the backpassing – the first foo would be in the scope where bar is called and the second foo is the argument of bar and would shadow it, which is not allowed):
foo : Foo
foo <- bar |> Result.try
I have worked around this so far by writing:
foo <- bar |> Result.try
checkFoo : Foo
checkFoo = foo
This lets me ensure that I made bar return what I think it should return, but adds an "unused definition" warning.
Is there a better way? Maybe something like expect type foo == Foo (but I don't want a runtime check).
Maybe foo : Foo should not count as "definition" of foo?
I wonder if thd shadowing proposal would be helpful for this use case? Maybe that would work as it would be the same foo.
:thinking:
Do you normally type lambda parameters?
Like when writing a quick List.map function
I do sometimes in Haskell. Not very often but sometimes.
To clarify why I ask, anything that works with backpassing probably should also work with generic lambdas passed to something like List.map
If we enable:
foo: Foo
foo <- bar |> Result.try
...
We also by default enable
foo: Foo
bar |> Result.try \foo ->
...
Yes exactly.
In Haskell you can put the type inline with parentheses. In Roc that might look like this
bar |> Result.try \(foo: Foo) ->
Or
(foo: Foo) <- bar |> Result.try
If our arguments are comma delimited, could we do the same without parens?
Maybe it would be good to have inline type annotations for smaller functions or lambdas.
Last updated: Nov 09 2025 at 12:14 UTC