bi f a b =(f a,f b)的通用变体

时间:2012-05-27 06:34:06

标签: haskell types higher-rank-types

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

bi f a b = (f a, f b)

这样可以像这样使用它:

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

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

g :: (forall a. a -> a) -> a -> a -> (a, a)
g f a b = (f a, f b)

4 个答案:

答案 0 :(得分:6)

{-# LANGUAGE TemplateHaskell #-}

bi f = [| \a b -> ($f a, $f b)|]

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

答案 1 :(得分:4)

是的,虽然不在Haskell中。但是更高阶的多态性lambda演算(又名System 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

在这里,我为显式类型应用程序编写f {T}并假设分别键入了一个库。像\a. a这样的东西是类型级的lambda。 x2示例更复杂,因为它还需要存在类型来在本地“忘记”参数中的另一部分多态性。

您可以在Haskell中通过为您实例化的每个不同的newtypem定义n或数据类型来实际模拟这个,并传递添加的适当包装的函数f并相应地删除构造函数。但显然,这根本不好玩。

编辑:我应该指出,这仍然不是完全通用解决方案。例如,我看不出你如何键入

swap (x,y) = (y,x)
x4 = bi swap (3, "hi") (True, 3.1)

甚至在System F-omega中。问题是swap函数比bi允许更多态,并且与x2不同,结果中不会忘记其他多态维度,因此存在性技巧不起作用。看起来你需要类型多态来允许那个(因此bi的参数可以在不同类型的数量上变成多态)。

答案 2 :(得分:3)

即使使用ConstraintKinds,我认为障碍将是从结果的参数中量化“类型函数”。您想要的f来映射a -> bc -> d,以及a -> b -> (c, d),但我认为没有办法以充分的普遍性量化这种关系。

但是,有些特殊情况可能是可行的:

(forall x . cxt x => x -> f x) -> a -> b -> (f a, f b)
 -- e.g. return

(forall x . cxt x => f x -> x) -> f a -> f b -> (a, b)
 -- e.g. snd
(forall x . cxt x => x -> x) -> a -> b -> (a, b)
 -- e.g. (+1)

但是考虑到你试图量化或多或少的任意类型函数,我不确定你能做到这一点。

答案 3 :(得分:2)

我认为这差不多就像你想要的那样:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
module Data.Function.Bi (bi, Fn(..))

bi :: (Fn i a a', Fn i b b') => i -> a -> b -> (a', b')
bi i a b = (fn i a, fn i b)

class Fn i x x' | i x -> x' where
      fn :: i -> x -> x'

像这样使用它:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, RankNTypes,
             FlexibleInstances, UndecidableInstances #-}
import Data.Function.Bi

data Snd = Snd

instance Fn Snd (a, b) b where
         fn Snd = snd

myExpr1 :: (Int, String)
myExpr1 = bi Snd (1, 2) ("a", "b")
-- myExpr == (2, "b")

data Plus = Plus (forall a. (Num a) => a)

instance (Num a) => Fn Plus a a where
         fn (Plus n) = (+n)

myExpr2 :: (Int, Double)
myExpr2 = bi (Plus 1) (1, 2) (1.3, 5.7)
-- myExpr2 == (3, 6.7)

它非常笨重,但尽可能一般。