This question (from 5 years ago) asks 'Why are all recursive pattern synonyms rejected?' and its example is still rejected. The User Guide says "Pattern synonyms cannot be defined recursively."
I have a recursive pattern synonym that's accepted (GHC 8.10.2). I can call it, and it loops -- no surprise. So why does it compile?/Is there a plausible use-case for something like this?
Code based on an example in Section 2.3 'Polymorphic pattern synonyms' of the 2016 paper.
data JoinList a = JNil | Unit a | JoinList a `JoinTree` JoinList a deriving (Eq, Read, Show)
I'm trying to define a pattern synonym Nil
. Obviously JNil
is a match. But also JNil ``JoinTree`` JNil
, and any arbitrary nestings of JoinTree
providing all the leafs are JNil
.
pattern Nil :: JoinList a
pattern Nil <- JNil
where Nil = Nil `JoinTree` Nil -- huh?
wot = Nil `JoinTree` Nil -- these all compile
wotwot Nil = ()
wotwotwot = wotwot Nil
wotwotwotwot = wotwot wot
Trying to call wot
loops, no surprise. Trying to call wotwotwot
complains Non-exhaustive patterns in function wotwot
.
Full disclosure: what I was playing with/I know this doesn't work [see also below] is:
pattern Nil = JNil -- base case matcher, and builder
where Nil <- Nil `JoinTree` Nil -- recursion on the matcher, not the builder
-- but can't put `<-` equations after where
The following rejected Recursive pattern synonym definition
-- which is what I expect
pattern NilRec <- NilRec `JoinTree` NilRec
where NilRec = JNil
And anyway that wouldn't work: it needs to match NilRec = JNil
first as the base case.
To answer @Noughtmare's q "how would you suggest ..." comment to his answer, I'd like to write the decl per above 'what I was playing with' (yes I know that's currently illegal syntax); and get that desugarred to a matcher with two cases, tried sequentially:
case arg of
{ JNil -> ...
; Nil `JoinTree` Nil -> ...
}
Note each of those cases has outermost a data constructor from type JoinList
. So the recursive use of Nil
is guarded/intermediated, as would be any approach via a ViewPattern. (Which is what the q 5 years ago was asking.) Put it another way: I think the compiler could generate a ViewPattern from the pattern ... = ... where ... <- ...
.
pattern ... <- ... where ... = ...
notation defines the matcher (with the arrow) and the builder (with the equality sign) separately, it is not a base case recursive case split as you seem to suggest.