【问题标题】:Ad-hoc cotuples in HaskellHaskell 中的临时夫妻
【发布时间】:2014-06-26 04:31:00
【问题描述】:

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

splitAt :: Int -> [a] -> ([a], [a]) 

但是没有简单、方便的方法来编写作用于或导致子集 的函数吗?例如。一个返回 Int 或 Double 的函数。作为一个具体的例子,假设我想写一个函数

MyDivision :: Int -> Int -> (Int + Double)

其中 + 是我的耦合符号,因此如果除法结果为整数,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

或类似的东西。

这可能吗?

【问题讨论】:

  • 你知道Either Int Double吗?
  • @gspr,是的,我是。但在我看到 jozefgs 的回答之前,我认为将它用于更大的联排会有点混乱。
  • 抱歉,我现在看到你明确提到了Either。我在手机上,没看到。
  • 没关系,很高兴你对我的问题表现出兴趣。

标签: haskell


【解决方案1】:

联元组正确地称为联积,即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 是类型级别的自然数的某种表示,并为您返回大量嵌套的Eithers .

更新:

无聊了,这里是通用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)

【讨论】:

  • 我不确定您的 inj 有什么用处。据我所知,它引入了一个嵌套的 Either,左右两侧带有 ()-s,注入值的类型位于中间。
  • @AndrásKovács 然后使用&lt;+&gt; 吗?我正在努力使这个包成为一个独立的包,其中包含一些更好的组合器,包括 Proxy 用于构建 Eithers
  • Another approach to sum types 和一个实现它的library。
【解决方案2】:

我将您的 + 重命名为 &gt;+&lt; 并将您的 &lt;+&gt; 重命名为 &gt;*&lt;,但您可以这样做:

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)

我试图命名操作员以更能暗示他们的操作。

这是实现&gt;*&lt;的另一种方式:

import Control.Arrow ((+++))

(>*<) :: (a -> e) -> (b -> f) -> a + b -> e + f
(>*<) = (+++)

附带说明:“元组”通常称为产品类型,这就是所谓的联产品类型(或总和类型)。最基本的联积类型是Either,对于某些类型A 和B,所有其他联积类型都与Either A B 同构。

【讨论】:

  • 当你提到+++时:另一个是||| in Arrows。
猜你喜欢
  • 2022-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多