Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matching multiple data type constructors at once

Tags:

haskell

Lets say we have this type declaration:

data D a = A a | B a | C a | D a | E a | F a

and want to define a function over it which divides the data constructors in 2 sets. It would be nice to write something like that:

g x | x `is` [A,B,C] = 1
    | x `is` [D,E,F] = 2

instead of matching on each constructor separately.

Is there any way to achieve this? I looked at uniplate but couldn't find a way to do it.

like image 781
Daniel Avatar asked Jul 18 '10 11:07

Daniel


2 Answers

If you often need to match for the same set of constructors, a helper function could be the simplest solution. For example:

getAbc :: D a -> Maybe a
getAbc (A v) = Just v
getAbc (B v) = Just v
getAbc (C v) = Just v
getAbc _     = Nothing

With such a helper function, the definition of g can be simplified like this:

g x = g_ (getAbc x)
  where
    g_ (Just v) = 1
    g_ Nothing  = 2

Or, using the maybe function:

g = maybe 2 (\v -> 1) . getAbc
like image 51
sth Avatar answered Oct 22 '22 01:10

sth


Edit: If all constructors have the same type of fields, you could abuse Functor:

{-# LANGUAGE DeriveFunctor #-}

data D a = A a | B a | C a | D a | E a | F a
    deriving (Eq, Functor)

isCons :: (Eq (f Int), Functor f) => f a -> (Int -> f Int) -> Bool
isCons k s = fmap (const 42) k == s 42

is :: (Eq (f Int), Functor f) => f a -> [Int -> f Int] -> Bool
is k l = any (isCons k) l

g :: D a -> Int
g x | x `is` [A,B,C] = 1
    | x `is` [D,E,F] = 2

You could try

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Data

data D a = A a | B a | C a | D a | E a | F a
        deriving (Typeable, Data)

g :: Data a => D a -> Int
g x | y `elem` ["A","B","C"] = 1
    | y `elem` ["D","E","F"] = 2
    where y = showConstr (toConstr x)
like image 31
kennytm Avatar answered Oct 22 '22 01:10

kennytm