【问题标题】:Is there a canonical haskell type for "One or Both"?“一个或两个”是否有规范的haskell类型?
【发布时间】:2013-10-16 20:41:01
【问题描述】:

我发现自己需要一个可能包含AB 的数据结构,并且肯定是其中之一。如果我要为这个东西破解一个通用数据类型,它可能看起来像:

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 中是否有一种规范的方法来处理一个或两个结构?

【问题讨论】:

  • 为什么止步于两种类型?三四, ...? :-)
  • 如果您想要“零、一或两者”,您可以选择 1 + A + B + A*B = (1 + A) * (1 + B)(Maybe A, Maybe B)

标签: haskell


【解决方案1】:

Data.These

这对于表示两个值的组合很有用,其中 如果任一输入是,则定义组合。代数上,类型 These A B 代表 (A + B + AB),这不容易考虑 sums and products — 像 Either A (B, Maybe A) 这样的类型不清楚并且 使用起来很尴尬。

【讨论】:

  • 不是很规范,但绝对是比自己滚动更好的选择,尽管这个包确实带有许多对 OP 可能有用也可能没用的依赖项和附加功能
  • 我不确定这是否真的能在所有情况下都胜过你自己。听起来 OP 经常使用它,但如果这是一件小事,我不想引入对 profunctors、mtl、semigroups 等的依赖
  • 对谈论以冠词命名的专有名词有多困难感到好笑,我写了Acme.Whose,它的明显优势是只引入strictbifunctors。请不要使用它。
  • 正是我正在寻找的那种东西。谢谢!
【解决方案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)

    您可以通过将(Maybe A, Maybe B) 包装在newtype 中并使用智能构造函数删除(Nothing,Nothing) 来执行A + B + A*B = (1+A)*(1+B)-1

    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")
    

    【讨论】:

    • 虽然代数分解方法很好,但我看不出这实际上比简单地滚动您自己的总和类型有什么优势。使用newtype 基本上是同构的,现在模式匹配已经不复存在了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-15
    • 1970-01-01
    • 2020-07-14
    • 2015-09-22
    • 2010-09-22
    • 1970-01-01
    • 2017-04-10
    相关资源
    最近更新 更多