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: Jul 06 2025 at 12:14 UTC