Using pattern synonyms to abstract implementation of text type

59 views Asked by At

Lets say I have:

newtype Animal = Animal Text

And I want to be able to pattern match on it like so:

f :: Animal -> (Bool, Text)
f = \case
  NonHuman s -> (False, s)
  Human -> (True, "human")

Basically, I want to interface with Animal as if it was defined like

data Animal = Human | NonHuman Text

Even though internally it's just a newtype of Text.

Note that I want the NonHuman pattern match to not match Animal "human".

I don't care too much about NonHuman and Human being valid in an expression context, indeed NonHuman should not be valid in an expression context (at least outside the module it's defined) as NonHuman "human" is bad.

I just want to abstract the implementation of this type from it's pattern matching syntax.

1

There are 1 answers

3
Daniel Wagner On BEST ANSWER

I guess you'd do something like this:

{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE OverloadedStrings #-}

parse :: Text -> Maybe Text
parse s = case "human" == s of
    True -> Nothing
    False -> Just s

{-# Complete Human, NonHuman #-}
pattern Human <- Animal "human"
-- OR
pattern Human <- (parse -> Nothing)
pattern NonHuman s <- (parse -> Just s)

If you did care about making them valid in expression context, the following example shows the two other pattern synonym syntactic forms that would make this possible:

pattern Human = Animal "human"
pattern NonHuman s <- (parse -> Just s) where
    NonHuman "human" = error "whoops"
    NonHuman s = Animal s