在Haskell中,很容易编写作用于或返回元组的函数,例如:前奏函数splitAt:
splitAt :: Int -> [a] -> ([a], [a])
但有没有简单,方便的方法来编写作用于 cotuples 事物的函数?例如。返回Int 或 Double的函数。举一个具体的例子,假设我想写一个函数
MyDivision :: Int -> Int -> (Int + Double)
其中+是我的cotupling符号,所以如果除法产生整数,MyDivision x y将x / y作为Int返回,如果除法不产生整数,则返回为Double。
到目前为止,似乎我有两个选择,要么声明一个新的数据类型
data IntOrDouble = AnInt Int | ADouble Double
或使用
Either Int Double
第一个替代方案需要大量输入和思考名称,而当你有更大的cotuples并且类型看起来像
时,第二个选择会很快变得混乱Either (Either a (Either b c)) (Either (Either d f) g)
现在,如果我有一个cotuple类型,请说
a + b + c + d
我希望能够形成功能
f :: (a + b + c + d) -> e
g :: (a + b + c + d) -> (e + f + g + h)
只提供功能
f1 :: a -> e, f2 :: b -> e, f3 :: c -> e, f4 :: d -> e
g1 :: a -> e, g2 :: b -> f, g3 :: c -> g, g4 :: d -> h
并设置
f = f1 + f2 + f3 + f4
g = g1 <+> g2 <+> g3 <+> g4
或类似的东西。
这可能吗?
答案 0 :(得分:13)
好的co-tuples恰当地称为副产品,只有Either
。
所以,让我们继续做一些像
这样的事情{-# LANGUAGE TypeOperators #-}
type (+) = Either
顺便说一句,这是一种联想。现在我们有类似
的语法foo :: Int + Bool + Char
foo = Right 'c'
现在,你似乎想要的东西实际上与Either
的教会代表非常相似。我们可以使用either
组合器
(+) :: (a -> c) -> (b -> c) -> (a + b) -> c
l + r = either l r
(<+>) :: (a -> c) -> (b -> d) -> (a + b) -> (c + d)
l <+> r = either (Left . l) (Right . r)
infixl 4 <+>, +
一个有趣的挑战是创建一个通用的inject
函数,它采用类似Proxy k
的内容,其中k
是类型级别的自然数字的一些表示,并返回一个很好的嵌套混乱Either
给你。
更新
我觉得无聊,这是通用inj
data Nat = S Nat | Z
type NatRep (n :: Nat) = Proxy n
type family Tuplish (l :: Nat) (r :: Nat) t
type instance Tuplish Z Z t = t
type instance Tuplish (S n) Z t = (Tuplish n Z ()) + t
type instance Tuplish l (S n) t = (Tuplish l n t) + ()
predP :: Proxy (S n) -> Proxy n
predP = reproxy
class Inject (l :: Nat) (r :: Nat) v where
inj :: NatRep l -> NatRep r -> v -> Tuplish l r v
instance Inject Z Z v where
inj _ _ = id
instance Inject (S n) Z v where
inj _ _ v = Right v
instance Inject n m v => Inject n (S m) v where
inj l r v = Left (inj l (predP r) v)
答案 1 :(得分:6)
我将您的+
重命名为>+<
,将<+>
重命名为>*<
,但您可以执行以下操作:
type a + b = Either a b
(>+<) :: (a -> c) -> (b -> c) -> a + b -> c
(>+<) = either
(>*<) :: (a -> e) -> (b -> f) -> a + b -> e + f
(f >*< _) (Left a) = Left (f a)
(_ >*< g) (Right b) = Right (g b)
我试图将操作员命名为更能说明他们的操作。
以下是实施>*<
的另一种方式:
import Control.Arrow ((+++))
(>*<) :: (a -> e) -> (b -> f) -> a + b -> e + f
(>*<) = (+++)
作为旁注:&#34; Tuples&#34;通常被称为产品类型,这就是所谓的副产品类型(或总和类型)。对于某些类型Either
和Either A B
,最基本的副产品类型为A
,所有其他产品类型与B
同构。