Continuing my crusade against MonadFail
being a constraint in my monad, I'm now faced with the following:
data Thing :: Bool -> Type where
TrueThing :: Thing 'True
FalseThing :: Thing 'False
myFunc :: Monad m => m ()
myFunc = do
TrueThing <- return TrueThing
return ()
Which won't compile because, you guessed it:
• Could not deduce (MonadFail m)
arising from a do statement
with the failable pattern ‘TrueThing’
Indeed, if I read whatever1 documentation I could find on the subject, it's not quite clear how the matching should work on a GADT. I would obviously have thought this case fell under the “matches unconditionally” umbrella. But I'll concede it doesn't qualify as a “single constructor data
type”2. Even though avoiding that sort of ambiguity was the point of using a GADT in the first place.
Sure ~lazyPattern
works, but that's just band-aid. I've desugared the match:
myFunc' :: Monad m => m ()
myFunc' = let f :: _
f TrueThing = return ()
in return TrueThing >>= f
And now, while the error message isn't that explicit (Couldn't match expected type ‘p’ with actual type ‘m0 ()
), the type wildcard is more telling:
• Found type wildcard ‘_’ standing for ‘Thing a -> p’
…and guides me towards a “solution”:
myFunc' :: forall m. Monad m => m ()
myFunc' = let f :: Thing 'True -> m ()
f TrueThing = return ()
in return TrueThing >>= f
Well, that works. Can I sugar it back? f
doesn't occur in the initial expression, but there are two spots I can annotate with a type: the monadic expression and the pattern. I've tried both separately as well as together, and it's all “could not deduce MonadFail
”.
I suppose my expectations were too high, but I'd like to understand why. Is it some type inference limitation? A GADT one? Am I not using the right machinery in the first place? How could this pattern fail at all?
(1) It's hard to find an authority on the subject. The GHC manual mentions MonadFail, but links to the HaskellPrime wiki, which seems to be an older revision of the same page on the Haskell wiki
(2) Replacing Bool
with ()
works too. But that's too much simplification with regard to my actual problem.
x <- return y
in ado
block is redundant, as you can simply remove that line replace anyx
that occurs later in the block withy
. – Felicityreturn y
is a simplification of some other expression that is known to have typem (Thing 'True)
. – Glacialx <- foo
), then pattern match usingcase ... of ...
, perhaps in alet
binding . This will at least warn you when you add a constructor if you enable-Wincomplete-patterns
. There appears to be an open issue about it (and a related example with pattern synonyms) – Formalin