Search code examples
haskellpattern-synonyms

Using pattern synonyms to abstract implementation of text type


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.


Solution

  • 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