Подтвердить что ты не робот

Существует ли канонический тип haskell для "One or Both"?

Мне нужна структура данных, которая содержит, возможно, A, возможно, B и определенно один из них. Если бы я взломал общий тип данных для этой вещи, это, вероятно, выглядело бы так:

data OneOrBoth a b = A a | B b | AB a b

maybeA :: OneOrBoth a b -> Maybe a
maybeB :: OneOrBoth a b -> Maybe b
eitherL :: OneOrBoth a b -> Either a b -- Prefers a
eitherR :: OneOrBoth a b -> Either a b -- Prefers b
hasBoth, hasExactlyOne, hasA, hasB :: OneOrBoth a b -> Bool

Имеет ли эта структура данных имя? Существует ли канонический способ обработки одно- или двух структур в Haskell?

4b9b3361

Ответ 1

Data.These

Это может быть полезно для представления комбинаций двух значений, где комбинация определена, если любой вход. Алгебраически тип These A B представляет (A + B + AB), что не суммы и продукты - тип типа Either A (B, Maybe A) неясен и неудобно использовать.

Ответ 2

Data.These было упомянуто и, вероятно, лучший выбор, но если бы я сам откатился, я бы сделал это как:

import Control.Applicative ((<$>), (<*>))

type These a b = Either (Either a b) (a, b)

maybeA :: These a b -> Maybe a
maybeA (Left (Left a)) = Just a
maybeA (Right (a, _))  = Just a
maybeA _               = Nothing

maybeB :: These a b -> Maybe b
maybeB (Left (Right b)) = Just b
maybeB (Right (_, b))   = Just b
maybeB _                = Nothing

eitherA :: These a b -> Either a b
eitherA (Left (Left a))  = Left a
eitherA (Right (a, _))   = Left a
eitherA (Left (Right b)) = Right b

eitherB :: These a b -> Either a b
eitherB (Left (Right b)) = Right b
eitherB (Right (_, b))   = Right b
eitherB (Left (Left a))  = Left a

hasBoth, hasJustA, hasJustB, hasA, hasB :: These a b -> Bool

hasBoth (Right _) = True
hasBoth _         = False

hasJustA (Left (Left _)) = True
hasJustA _               = False

hasJustB (Left (Right _)) = True
hasJustB _                = False

hasA = (||) <$> hasBoth <*> hasJustA
hasB = (||) <$> hasBoth <*> hasJustB

Ответ 3

Если вы хотите "нуль, один или оба", у вас будет 1 + A + B + A*B = (1 + A) * (1 + B) или (Maybe A, Maybe B).

Вы можете сделать A + B + A*B = (1+A)*(1+B)-1, обернув (Maybe A, Maybe B) в newtype и используя интеллектуальные конструкторы, чтобы удалить (Nothing,Nothing):

module Some (
  Some(),
  this, that, those, some,
  oror, orro, roro, roor,
  swap
) where

import Control.Applicative ((<|>))

newtype Some a b = Some (Maybe a, Maybe b) deriving (Show, Eq)

-- smart constructors
this :: a -> Some a b
this a = Some (Just a,Nothing)

that :: b -> Some a b
that b = Some (Nothing, Just b)

those :: a -> b -> Some a b
those a b = Some (Just a, Just b)

-- catamorphism/smart deconstructor
some :: (a -> r) -> (b -> r) -> (a -> b -> r) -> Some a b -> r
some f _ _ (Some (Just a, Nothing)) = f a
some _ g _ (Some (Nothing, Just b)) = g b
some _ _ h (Some (Just a, Just b))  = h a b
some _ _ _ _ = error "this case should be unreachable due to smart constructors"

swap :: Some a b -> Some b a
swap ~(Some ~(ma,mb)) = Some (mb,ma)

-- combining operators
oror, orro, roro, roor :: Some a b -> Some a b -> Some a b

-- prefer the leftmost A and the leftmost B
oror (Some (ma,mb)) (Some (ma',mb')) = Some (ma <|> ma', mb <|> mb')
-- prefer the leftmost A and the rightmost B
orro (Some (ma,mb)) (Some (ma',mb')) = Some (ma <|> ma', mb' <|> mb)
-- prefer the rightmost A and the rightmost B
roro = flip oror
-- prefer the rightmost A and the leftmost B
roor = flip orro

Операторы объединения - забавные:

λ this "red" `oror` that "blue" `oror` those "beige" "yellow"
Some (Just "red",Just "blue")
λ this "red" `orro` that "blue" `orro` those "beige" "yellow"
Some (Just "red",Just "yellow")
λ this "red" `roor` that "blue" `roor` those "beige" "yellow"
Some (Just "beige",Just "blue")
λ this "red" `roro` that "blue" `roro` those "beige" "yellow"
Some (Just "beige",Just "yellow")