Соответствие нескольким конструкторам типа данных сразу

Позволяет говорят, что у нас есть это описание типа:

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

и хочу определить функцию по нему, которая делит конструкторов данных на 2 набора. Было бы хорошо записать что-то как этот:

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

вместо того, чтобы соответствовать на каждом конструкторе отдельно.

Там какой-либо путь состоит в том, чтобы достигнуть этого? Я посмотрел на uniplate, но не мог найти способ сделать это.

9
задан Daniel Velkov 18 July 2010 в 00:04
поделиться

5 ответов

Я бы хотел, чтобы в паттернах Haskell был способ указать "OR" двух паттернов, подобно | в OCaml:

(* ocaml code *)
let g x = match x with
            A v | B v | C v -> 1
          | C v | D v | E v -> 2
0
ответ дан 3 November 2019 в 00:58
поделиться

Если вам часто требуется сопоставление одного и того же набора конструкторов, вспомогательная функция может быть самым простым решением. Например:

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

С такой вспомогательной функцией определение g можно упростить следующим образом:

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

Или, используя функцию возможно :

g = maybe 2 (\v -> 1) . getAbc
5
ответ дан 3 November 2019 в 00:58
поделиться

Это немного похоже на взлом, но как насчет этого, используя Data.Data и тип «заполнитель»?

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Data 

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


matchCons :: (Data a) => D a -> [D X] -> Bool
matchCons x ys = any ((== xc) . toConstr) ys
    where xc = toConstr x

g :: (Data a) => D a -> Int
g x | matchCons x [A X, B X, C X] = 1
    | matchCons x [D X X, E X X, F X X] = 2

Обратите внимание, что это позволяет избежать проблема сигнатуры типа / арности другого конструктора. Вероятно, есть и более чистый способ сделать что-то подобное.

0
ответ дан 3 November 2019 в 00:58
поделиться

Я попытался обобщить ответ @KennyTM с помощью:

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

class AutoBind a where
    bindSome :: forall b . (a -> b) -> b

instance AutoBind Bool where bindSome f = f False
instance Num a => AutoBind a where bindSome f = f 0

class AutoConst a b | a -> b where {- bind until target type -}
    bindAll :: a -> b

instance AutoBind a => AutoConst (a -> b) b where bindAll = bindSome
instance (AutoBind a, AutoConst b c) => AutoConst (a -> b) c where bindAll = bindAll . bindSome

isCons :: (Eq (f a), AutoBind a, AutoConst b (f a), Functor f) => f a -> b -> Bool
isCons x y = fmap (bindSome const) x == bindAll y

Но по какой-то причине он не работает для конструктора C

2
ответ дан 3 November 2019 в 00:58
поделиться

Изменить: если все конструкторы имеют одинаковый тип полей, вы можете злоупотребить Функтором:

{-# 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

Вы можете попробовать

{-# 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)
4
ответ дан 3 November 2019 в 00:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: