Haskell中的临时cotuples

Cal*_*lle 8 haskell

在Haskell中,很容易编写作用于或返回元组的函数,例如prelude函数splitAt:

splitAt :: Int -> [a] -> ([a], [a]) 
Run Code Online (Sandbox Code Playgroud)

但有写作用于或导致功能的不容易,便捷,方式cotuples的东西呢?例如,返回Int Double 的函数.举一个具体的例子,假设我想编写一个函数

MyDivision :: Int -> Int -> (Int + Double)
Run Code Online (Sandbox Code Playgroud)

其中+是我的cotupling符号,因此如果除法产生整数,MyDivision xy将x/y返回为Int,如果除法不产生整数,则返回Double.

到目前为止,似乎我有两个选择,要么声明一个新的数据类型

data IntOrDouble = AnInt Int | ADouble Double
Run Code Online (Sandbox Code Playgroud)

或使用

Either Int Double
Run Code Online (Sandbox Code Playgroud)

第一种选择需要大量输入和思考名称,第二种选择很快就会变得混乱,当你有更大的cotuples并让类型看起来像

Either (Either a (Either b c)) (Either (Either d f) g)
Run Code Online (Sandbox Code Playgroud)

现在,如果我有一个cotuple类型,说

a + b + c + d
Run Code Online (Sandbox Code Playgroud)

我希望能够形成功能

f :: (a + b + c + d) -> e
g :: (a + b + c + d) -> (e + f + g + h)
Run Code Online (Sandbox Code Playgroud)

只提供功能

f1 :: a -> e, f2 :: b -> e, f3 :: c -> e, f4 :: d -> e
g1 :: a -> e, g2 :: b -> f, g3 :: c -> g, g4 :: d -> h
Run Code Online (Sandbox Code Playgroud)

和设置

f = f1 + f2 + f3 + f4
g = g1 <+> g2 <+> g3 <+> g4
Run Code Online (Sandbox Code Playgroud)

或类似的东西.

这可能吗?

Dan*_*zer 13

好的co-tuples恰当地称为副产品Either.

所以,让我们继续做类似的事情吧

{-# LANGUAGE TypeOperators #-}

type (+) = Either
Run Code Online (Sandbox Code Playgroud)

顺便说一句,这是联系在一起的.现在我们有相当的语法

foo :: Int + Bool + Char
foo = Right 'c'
Run Code Online (Sandbox Code Playgroud)

现在,你似乎想要的东西实际上非常类似于教会代表的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 <+>, +
Run Code Online (Sandbox Code Playgroud)

一个有趣的挑战是创建一个通用的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)
Run Code Online (Sandbox Code Playgroud)


Dav*_*vid 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)
Run Code Online (Sandbox Code Playgroud)

我试图将操作员命名为更能说明他们的操作.

这是另一种实现方式>*<:

import Control.Arrow ((+++))

(>*<) :: (a -> e) -> (b -> f) -> a + b -> e + f
(>*<) = (+++)
Run Code Online (Sandbox Code Playgroud)

作为旁注:"元组"通常被称为产品类型,这就是所谓的副产品类型(或总和类型).最基本的副产品类型是Either和所有其他副产品类型Either A B对于某些类型A和同构B.

  • 当你提到`+++`时:另一个是'Arrow`中的`|||`. (2认同)