Hi all,
I couldn't find the syntax in the tutorial or in the compiler repo, or the right terms to search here: is there a way to add a restriction/where clause to type parameters of types? E.g. an association list needs comparable keys? Or is there a way to define this in roc's way to avoid the problem?
(Invalid where clause below)
AssocList(key, value) :: {
elements : List((key, value))
} where [key.is_eq: key, key -> Bool].{...}
I suppose, being opaque, you could just put the restriction on the constructor (and every other func)? Although then there's nothing stopping me from doing
x : AssocList(MyTypeWithNoEq, Str)
x = AssocList.empty()
(Of course, why I would do that I don't know)
Jonathan said:
Hi all,
I couldn't find the syntax in the tutorial or in the compiler repo, or the right terms to search here: is there a way to add a restriction/where clause to type parameters of types? E.g. an association list needs comparable keys? Or is there a way to define this in roc's way to avoid the problem?(Invalid
whereclause below)AssocList(key, value) :: { elements : List((key, value)) } where [key.is_eq: key, key -> Bool].{...}
I feel like that where clause should be valid
Oh yeah, maybe it goes on the constructors
This is what I'm getting on the current nightly
You cannot define a where clause inside a type declaration.
You're attempting do this here:
┌─ ./AssocList.roc:1:1
│
1 │ AssocList(key, value) :: {
2 │ elements : List((key, value))
3 │ }where [key.is_eq: key, key -> Bool].{
4 │ }
Oh yeah, maybe it goes on the constructors
I guess also on the .empty() too would stop that edge case :man_facepalming:
I think we talked about a way to alias a collection of where clauses into like an interface.
Maybe it would be something like this then?
AssocList(key, value) :: { elements : List((HasEq(key), value)) } ...
Is HasEq a type alias for "something that implements is_eq"? That's pretty cool
I think it would be something like but I don't know what the syntax would look like. I'm not sure we ever finished the conversation.
HasEq(a) : _ where a.is_eq: a, a -> Bool
That's quite an elegant solution tbh. So essentially you can put restrictions on aliases and record field annotations, and that bubbles up to the opaque type parameters?
I'm not sure we ever finished the conversation.
Is that not valid syntax right now? Currently afk (but will testsoon!)
this is actually an anti pattern! putting constraints on the type level end up being hard to work with. you’ll want to put only the constraints necessary on each individual function
there’s another thread from a while ago that has a fuller explanation, i can try to find it later
Even if it's core to the existence of the type? I would love to read that if you could find it thank you (off soon - no pressure).
Side note: I saw about the record builder suffix notation, eg
{ a: DoesNotImplEq }.MyType
Doesn't that allow bypassing smart constructor style type restrictions?
Thank you @Jared Ramirez
Bonus question: are there any cases where you would consider type level constraints? For instance, if I am a function that accepts Container(a), and there are certain type restricted functions of Container that I don't necessarily use yet, it would be a breaking change in the future if I started using them.
previous discussion here:
(old where syntax, but you get the idea)
tldr; is if you put the constraint on the type, then you have to include that constraint in a bunch of type signatures everywhere, even if the function question doesn't need it (eg in size : Dict(k, v), k would need k.hash, even though size doesn't do any hashing)
Jared Ramirez said:
previous discussion here:
Fair enough - thank you!
Luke Boswell said:
HasEq(a) : _ where a.is_eq: a, a -> Bool
So is this a syntax that will not make it in to roc? I see it's currently frozen at least:
Jared Ramirez said:
Based on this convo, i'm planning on disallowing
whereclauses in type declarations for now in my next PR.
Putting type constraints on a type may be an antipattern, but here it looks like it allows you to define something interface-y that you can tag a type variable with as a shorthand for all the constituent required functions in the where. Not sure how you'd compose though (e.g., HasEq and HasCmp).
I also don't have a valid use case yet outside of type-foo so maybe don't mind me here :laughing:
Last updated: Feb 20 2026 at 12:27 UTC