I find myself needing a data structure that contains maybe an A
, maybe a B
, and definitely one of them. If I were to hack up a general data type for this thing, it would probably look like:
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
Does this data structure have a name? Is there a canonical way to handle one-or-both structures in Haskell?
Data.These
This can be useful to represent combinations of two values, where the combination is defined if either input is. Algebraically, the type
These A B
represents (A + B + AB), which doesn't factor easily into sums and products — a type likeEither A (B, Maybe A)
is unclear and awkward to use.
Data.These has been mentioned, and is probably the best choice, but if I were to roll my own, I'd do it as:
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
If you wanted "Zero, One, or Both" you'd have 1 + A + B + A*B = (1 + A) * (1 + B)
or (Maybe A, Maybe B)
.
You could do A + B + A*B = (1+A)*(1+B)-1
by wrapping (Maybe A, Maybe B)
in a newtype
and using smart constructors to remove the (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
The combining operators are fun:
λ 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")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With