I have arrived at this approach of making handling of nested Either
's more elegant; it uses pattern synonyms to allow case
'ing directly on the inner Either values.
type (:+:) = Either
class a :<: b where
inj :: a -> b
pick :: b -> Maybe a
instance a :<: (a :+: b) where
inj = Left
pick = either Just (const Nothing)
instance b :<: (a :+: b) where
inj = Right
pick = either (const Nothing) Just
instance {-# OVERLAPPABLE #-} a :<: c => a :<: (c :+: b) where
inj = Left . inj
pick = either pick (const Nothing)
pattern Member :: x :<: c => x -> c
pattern Member {thing} <-
(pick -> Just thing)
where
Member = inj
type Stuff = Int :+: String :+: Char :+: Text
mkStuff :: Stuff
mkStuff = Member @Int 42
useStuff :: Stuff -> String
useStuff = \case
Member s -> s
Member (n :: Int) -> "The number: " <> show n
Member (c :: Char) -> [c]
Member (t :: Text) -> toString t
GHC however complains of non-exhaustive patterns:
Foo.hs:(110,12)-(114,34): warning: [-Wincomplete-patterns]
Pattern match(es) are non-exhaustive
In a case alternative: Patterns not matched: _
|
110 | useStuff = \case
| ^^^^^...
But how would one write a COMPLETE
pragma for this polymorphic pattern synonym?
I don’t know how it works, but using {-# COMPLETE Member :: Either #-}
will suppress this warning entirely, and will not complain even if there are in fact non-exhaustive patterns. Obviously that’s not ideal.