Haskell bifab=(fa,fb)的一般变体

Haskell bifab=(fa,fb)的一般变体,haskell,types,higher-rank-types,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 ?? 在秩n类型的例子中,总是有一些更简单的东西,比如 g :: (forall a. a -> a

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

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
?? 在秩n类型的例子中,总是有一些更简单的东西,比如

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

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

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

这是您将要得到的最接近的结果,我认为:

{-# 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)
它很笨重,但尽可能的普通

{-# LANGUAGE TemplateHaskell #-}

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


)()

是的,但不是在哈斯克尔。但高阶多态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

甚至在F-ω系统中。问题是
swap
函数比
bi
允许的多态性更大,并且与
x2
不同,结果中没有忘记其他多态维度,因此存在技巧不起作用。似乎您需要一种多态性来允许这种类型(因此,
bi
的参数可以在不同的类型上多态)。

我不这么认为。您必须对所有输入类型进行量化,这需要对类型类约束进行抽象。@LouisWasserman,有一些新东西(ConstraintTypes)允许对约束进行抽象。好吧,我明白了,但我认为您不能像必须的那样对结果类型进行量化。如果它的格式是
a->a
,那么你可以做
bi:(cxta,cxtb)=>(对于所有x.cxtx=>x->x)->a->b->(a,b)
,但是我认为你不能从每个输入自动获取“类型函数”到它的结果类型。这段代码中有几个错误。首先,
Fn
的一个实例有三个参数,而
Fn(加a)
的情况并非如此。其次,
Plus
的类型是
*
,因此
Plus a
无效。最后,您需要
FlexibleInstances
,因为类型变量
b
Snd
实例中出现两次。
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
swap (x,y) = (y,x)
x4 = bi swap (3, "hi") (True, 3.1)