Stream: beginners

Topic: Associated type restriction


view this post on Zulip Jonathan (Feb 17 2026 at 22:50):

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].{...}

view this post on Zulip Jonathan (Feb 17 2026 at 22:58):

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)

view this post on Zulip Luke Boswell (Feb 17 2026 at 23:06):

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 where clause below)

AssocList(key, value) ::  {
    elements : List((key, value))
} where [key.is_eq: key, key -> Bool].{...}

I feel like that where clause should be valid

view this post on Zulip Luke Boswell (Feb 17 2026 at 23:07):

Oh yeah, maybe it goes on the constructors

view this post on Zulip Jonathan (Feb 17 2026 at 23:07):

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 │ }

view this post on Zulip Jonathan (Feb 17 2026 at 23:08):

Oh yeah, maybe it goes on the constructors

I guess also on the .empty() too would stop that edge case :man_facepalming:

view this post on Zulip Luke Boswell (Feb 17 2026 at 23:08):

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)) } ...

view this post on Zulip Jonathan (Feb 17 2026 at 23:12):

Is HasEq a type alias for "something that implements is_eq"? That's pretty cool

view this post on Zulip Luke Boswell (Feb 17 2026 at 23:13):

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

view this post on Zulip Jonathan (Feb 17 2026 at 23:16):

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?

view this post on Zulip Jonathan (Feb 17 2026 at 23:19):

I'm not sure we ever finished the conversation.

Is that not valid syntax right now? Currently afk (but will testsoon!)

view this post on Zulip Jared Ramirez (Feb 17 2026 at 23:30):

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

view this post on Zulip Jared Ramirez (Feb 17 2026 at 23:30):

there’s another thread from a while ago that has a fuller explanation, i can try to find it later

view this post on Zulip Jonathan (Feb 17 2026 at 23:55):

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).

view this post on Zulip Jonathan (Feb 17 2026 at 23:57):

Side note: I saw about the record builder suffix notation, eg

{ a: DoesNotImplEq }.MyType

Doesn't that allow bypassing smart constructor style type restrictions?

view this post on Zulip Luke Boswell (Feb 18 2026 at 00:05):

Thank you @Jared Ramirez

view this post on Zulip Jonathan (Feb 18 2026 at 00:07):

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.

view this post on Zulip Jared Ramirez (Feb 18 2026 at 01:53):

previous discussion here: #ideas > where clause changes @ 💬

view this post on Zulip Jared Ramirez (Feb 18 2026 at 01:54):

(old where syntax, but you get the idea)

view this post on Zulip Jared Ramirez (Feb 18 2026 at 01:55):

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)

view this post on Zulip Jonathan (Feb 18 2026 at 08:46):

Jared Ramirez said:

previous discussion here: #ideas > where clause changes @ 💬

Fair enough - thank you!

view this post on Zulip Jonathan (Feb 18 2026 at 10:25):

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 where clauses 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).

view this post on Zulip Jonathan (Feb 18 2026 at 10:27):

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