bi fab的通用变体=(fa,fb)

aem*_*xdp 13 haskell types higher-rank-types

是否有任何类型安全的方法来编写函数

bi f a b = (f a, f b)
Run Code Online (Sandbox Code Playgroud)

这样就可以像这样使用它:

x1 :: (Integer, Char)
x1 = bi head [2,3] "45"

x2 :: (Integer, Char)
x2 = bi fst (2,'3') ('4',5)

x3 :: (Integer, Double)
x3 = bi (1+) 2 3.45
Run Code Online (Sandbox Code Playgroud)

?在rank-n-types例子中总有一些更简单的东西

g :: (forall a. a -> a) -> a -> a -> (a, a)
g f a b = (f a, f b)
Run Code Online (Sandbox Code Playgroud)

Fun*_*lad 6

{-# LANGUAGE TemplateHaskell #-}

bi f = [| \a b -> ($f a, $f b)|]
Run Code Online (Sandbox Code Playgroud)

 

ghci> :set -XTemplateHaskell 
ghci> $(bi [|head|]) [2,3] "45" 
(2,'4')
Run Code Online (Sandbox Code Playgroud)

;)


And*_*erg 4

是的,尽管不是在 Haskell 中。但高阶多态 lambda 演算(又名系统 F-omega)更为通用:

bi : forall m n a b. (forall a. m a -> n a) -> m a -> m b -> (n a, n b)
bi {m} {n} {a} {b} f x y = (f {a} x, f {b} y)

x1 : (Integer, Char)
x1 = bi {\a. List a} {\a. a} {Integer} {Char} head [2,3] "45"

x2 : (Integer, Char)
x2 = bi {\a . exists b. (a, b)} {\a. a} {Integer} {Char} (\{a}. \p. unpack<b,x>=p in fst {a} {b} x) (pack<Char, (2,'3')>) (pack<Integer, ('4',5)>)

x3 : (Integer, Double)
x3 = bi {\a. a} {\a. a} {Integer} {Double} (1+) 2 3.45
Run Code Online (Sandbox Code Playgroud)

在这里,我f {T}为显式类型应用程序编写并假设分别类型化的库。类似于\a. a类型级别的 lambda。这个x2例子更加复杂,因为它还需要存在类型来本地“忘记”参数中的其他多态性。

实际上,您可以在 Haskell 中模拟这一点,方法是newtype为实例化的每个不同的 or 定义一个 or 数据类型mn并传递适当包装的函数f来相应地添加和删除构造函数。但显然,这一点也不好玩。

编辑:我应该指出,这仍然不是一个完全通用的解决方案。例如,我不知道你如何输入

swap (x,y) = (y,x)
x4 = bi swap (3, "hi") (True, 3.1)
Run Code Online (Sandbox Code Playgroud)

即使在 F-omega 系统中也是如此。问题在于该swap函数比 allowed 具有更多的多态性bi,并且与 with 不同x2,结果中不会忘记另一个多态维度,因此存在主义技巧不起作用。看来您需要一种多态性来允许这种情况(以便参数可以bi在不同数量的类型上具有多态性)。