Say we have Result.withDefault : Result ok err, ok -> ok and we use this in a module
foo = Result.withDefault
What should the type of foo be. Today it is actually
foo : Result ok *, ok -> ok
But the natural result of some changes I'm making turns that into
foo : Result ok err, ok -> ok
And I actually like that better I think
So, should we import type variable names?
we'd need to actively do work to get rid of those names btw
the times when this would come up would be in the repl, in the editor when asking the name of the type, or in an error message, yeah?
exactly
right now in the repl this would show the err because technically that function is not imported
but in the general case the name would be dropped today
however, with some changes https://github.com/rtfeldman/roc/pull/2703/files that will make moving types between modules much faster,
this changes
I think because we send a rigid over now, instead of a flex
and rigids must have a name
and you can't just pick * because the rigid might occur elsewhere in the signature
I agree I think we should send the name over especially if it comes naturally with the current impl
Semantically it is no different, but saying err instead of * seems a lot more expressive
This is a little off topic, but I expected the first code example to crash the REPL because I'm used to Roc expecting my functions to be applied.
I expected this:
foo = Result.withDefault
to have to be rewritten as this:
foo = \x, y -> Result.withDefault x y
However, the first code example works fine! I was pleasantly surprised that g = f and g f both work as expected:
» f = \x -> x * 2
… g = f
… g
<function> : Num a -> Num a
» f = \x -> x * 2
… g = \x -> x 3
… g f
6 : Num *
» f = \x -> x * 2
… 3 |> f
6 : Num *
»
Is this new behavior? Have I been too cautious with my function references? I've felt the need to do a lot of anonymous \x -> f x functions, especially in pipelines... but I'd love to not have to!
I don't think there's anything new about this behaviour. In your example, you mention something about functions having to be applied, but the foo function is not applied so it's no different from the Result.withDefault inside it.
But something comes to mind that might be relevant. If you want to partially apply a function (i.e. give it one argument now and another later) then you do need to wrap it in another function.
y = 123
foo = \x -> Result.withDefault x y
foo (Ok 5)
But if you're providing all arguments at once, there's no need to do that.
In closely-related languages like Haskell/Elm/Ocaml/F#, you wouldn't need the wrapping function because they have automatic currying. But Roc doesn't have that.
Yeah, that makes sense, since Roc doesn't have partial application or currying. However, I thought that extended to non-application (or whatever the phrase would be for partial application with zero parameters applied).
On second thought, I've used a lot of non-applied functions in pipelines... but sometimes I'd need to wrap them in a lambda for whatever reason... maybe those were just more-complex functions. Okay!
Ah OK that must be it then. The idea that functions can be used as values is pretty core in functional languages, so it would be a bug if it didn't work somewhere.
Hmm, wow I swear this didn't use to work... I have a bad memory, though.
» f = \x -> x * 2
… g = \x -> x * 3
… h = \x, y -> x 4 + y 5
… h f g
23 : Num *
»
Maybe it wasn't working (at some point in the past) due to some bug, but then I just assumed it wasn't supposed to work.
Hooray! This is super clean and practical :smiley:
Last updated: Jun 16 2026 at 16:19 UTC