Hey ya'll, I'm trying to implement a recursive type, and seem to have hit a brick wall. The error message for recursive types says:
Recursion in aliases is only allowed if recursion happens
behind a tagged union, at least one variant of which is not
recursive.
I believe my recursive type meets these requirements - The recursion is within a tag union, and there is a variant in the union which is terminating. Is the problem because the tag union in my recursive type is wrapped in a record (or List), but must actually be a pure tag union?
I believe this should be adequate to show my type structure:
Expression : {
subexpression: Subexpression,
expression: [
Expression Expression, # recursive
NoExpression,
]
}
Subexpression : List [
Match Match,
Group Group, # recursive
AnchorWordBoundary,
AnchorNonWordBoundary,
]
Group: {
modifier: [Capturing, NonCapturing]
expression: Expression, # recursive
quantifier: Quantifier,
}
For context, I am trying to express the syntax tree for the following grammar:
Expression ::= Subexpression ("|" Expression)?
Subexpression ::= SubexpressionItem+
SubexpressionItem
::= Match
| Group
| Anchor
Group ::= "(" GroupNonCapturingModifier? Expression ")" Quantifier?
Any suggestions on how I could massage this grammar into workable Roc types?
In the current roc compiler, recursive types must be tags and they must be defined in one definition.
I don't think we ever fixed the one definition restriction
Ah... so probably the fact that its not one definition is what is catching me up here...
Yeah
And I think you can use as
to name subexpression in the single definition if necessary
Hmm.. so these are part of a greater type called Regex
. I tried to inline everything inside Regex, and use as
, but as
seems to throw an error.
Seems like as
doesn't work in this context.
I think I'm going to have to go back to the drawing board on how to implement this AST. :thinking::sweat_smile:
I swear we have a way to name inline....thought it was wrapping in parens and using as.....hmmm
Hmmm.. yeah, I can't seem to find a way to get the as keyword to work inline.
Brendan Hansknecht said:
I don't think we ever fixed the one definition restriction
Does this imply it is a goal to permit recursion across multiple definitions? :fingers_crossed::smiley:
I don't know what the new plan is/how it works. I think the new plan is only recursion through nominal types.
But yeah, should be multiple definition
yeah that's the plan!
introduce nominal ("custom") tag unions, allow them to be recursive (including mutually recursive if desired) and disallow recursion in structural tag unions
I forget where we talked about why that change was necessary, but it was important :sweat_smile:
This sounds promising! Will be great to have more flexible recursion support.
Couple of questions - by definition, a nominal tag union would be a closed union, correct? And furthermore, since it is nominal, not structural, then even if NominalA was a subset of NominalB, NominalA could not be given to something which needed NominalB, correct?
they're closed and so can't be unioned together
they're exactly like algebraic data types in other languages (or enums in Rust)
just using the name "custom tag union" since that fits our terminology :big_smile:
Okay, that's what I figured.
This now tops the list of features I'm looking forward to in Roc!
Last updated: Jul 05 2025 at 12:14 UTC