是否有任何类型安全的方法来编写函数
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)
答案 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中通过为您实例化的每个不同的newtype
或m
定义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 -> 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)
但是考虑到你试图量化或多或少的任意类型函数,我不确定你能做到这一点。
答案 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)
它非常笨重,但尽可能一般。