13

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

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

4 回答 4

6
{-# LANGUAGE TemplateHaskell #-}

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

 

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

;)

于 2012-05-30T02:41:41.753 回答
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 中通过newtype为每个不同mn您实例化的定义一个或数据类型来模拟这一点,并传递相应地f添加和删除构造函数的适当包装的函数。但显然,这根本不好玩。

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

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

即使在 System F-omega 中。问题是该swap函数比bi允许的多态性更强,并且与 with 不同x2,结果中没有忘记其他多态维度,因此存在技巧不起作用。似乎您需要一种多态性来允许该多态性(以便参数bi可以在不同数量的类型上是多态的)。

于 2012-05-30T06:58:05.510 回答
3

即使使用 ConstraintKinds,我认为障碍将是量化从参数到结果的“类型函数”。您想要的是f映射a -> bandc -> 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)

但鉴于您正试图量化或多或少的任意类型函数,我不确定您是否可以做到这一点。

于 2012-05-27T07:33:47.963 回答
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)

它非常笨重,但尽可能通用。

于 2012-05-27T20:19:06.710 回答