Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 将多态函数应用于两种不同类型的输入_Haskell_Types_Parametric Polymorphism - Fatal编程技术网

Haskell 将多态函数应用于两种不同类型的输入

Haskell 将多态函数应用于两种不同类型的输入,haskell,types,parametric-polymorphism,Haskell,Types,Parametric Polymorphism,考虑这一功能: doToBoth f x y = (f x, f y) 在简单的情况下,它可以正常工作: doToBoth (2 *) 10 15 == (20, 30) doToBoth head [1,2] [3,4,5] == (1, 3) 然后我试了这些: doToBoth head [1,10,100] "apple" doToBoth pred 2 'b' 我希望它们都会导致(1,'a'),但它们只会导致类型错误。问题在于推断的doToBoth类型不够多态: doToBoth

考虑这一功能:

doToBoth f x y = (f x, f y)
在简单的情况下,它可以正常工作:

doToBoth (2 *) 10 15 == (20, 30)
doToBoth head [1,2] [3,4,5] == (1, 3)
然后我试了这些:

doToBoth head [1,10,100] "apple"
doToBoth pred 2 'b'
我希望它们都会导致
(1,'a')
,但它们只会导致类型错误。问题在于推断的
doToBoth
类型不够多态:

doToBoth :: (a1 -> a2) -> a1 -> a1 -> (a2, a2)
看到这一点,我尝试添加一个显式类型签名来修复它:

doToBoth :: (t ~ (i1 -> o1), t ~ (i2 -> o2)) => t -> i1 -> i2 -> (o1, o2)
此类型签名已被接受,但没有解决问题,通过检查
:t doToBoth
发生的情况,发现其最终的类型与原始推断的类型相同:

doToBoth :: (i2 -> o2) -> i2 -> i2 -> (o2, o2)

编写类型签名的正确方法是什么,以使此函数按我希望的方式工作?

接受多态参数会使函数排名2多态。GHC有,但只有当您能够量化参数必须支持的类型(使用类型构造函数或类)时,才能使用它。例如,对于列表,您可以编写

{-# LANGUAGE Rank2Types, UnicodeSyntax #-}
doToBoth_listelem :: (∀ x . [x] -> x) -> [a] -> [b] -> (a,b)
doToBoth_listelem f x y = (f x, f y)
>doToBoth_enum pred 2'b'
(1,'a')

我认为,将其编写为自动适用于该论点可能需要的任何此类约束是不可能的。通过一些聪明的类型族和约束类型,可能可以近似地实现这一点,但我怀疑它最终是否实际可用。

根据您描述的解决方案,似乎对于每种不同类型的
f
,我都需要不同版本的
doToBoth
,这不是很方便。我真正想要的是你在最后一段中所说的不可能或至少不切实际。我将把这个问题保留几天,以防其他人能想到一种方法来解决,或者找到一个答案来解释Haskell的类型系统中缺少了什么使它变得不可能。@JosephSible我找不到一个通用的方法来解决这个问题,除非我利用Haskell中没有的交叉点类型(IIRC,交叉点类型的推理是不可判定的,类型检查太昂贵了)。我想你需要一些东西,比如
doBoth::((a->a')&&(b->b')->a->b->(a',b')
,Haskell没有。我想为所有c.(ca,cb)=>(对于所有x.cx=>x)->a->b->(a,b),但那不起作用。我找到了一种方法,可以用
ConstraintKinds
来做,如果你显式地传递字典,但这不值得麻烦:给定
both::forall a b c.Dict(c a)->Dict(c b)->(forall x.Dict(c x)->x->a->b->(a,b)
你调用它作为
两者(Dict::Dict(Enum Int))(Dict::Dict(Enum Char))(\Dict->pred)2'b'
。可能是更聪明的东西的起点。@JonPurdy看起来只有当
f
是自同态时它才会起作用,而
head
不是。@JosephSible:对,这是答案中的第二个例子(
doToBoth\u Enum
)^如果这不能完全回答您的问题,您可以让我知道。在这里获得满足您特定问题的内容应该是一个小小的修改,但如果您愿意,我可以提供有关该修改的更多详细信息。@DavidYoung您在那里的答案看起来几乎完美。将其转换为使用visible t非常简单键入应用程序而不是代理,并添加新的类/实例以支持
head
功能(如果需要,我可以在答案中编辑其中一个或两个)。唯一的缺点是它在
length
函数上不起作用,而是在
length.toList
函数上起作用。我尝试了一些时间,用各种新的GHC扩展来解决这个问题,因为你在2015年写了这个问题,但没有成功。
> doToBoth_listelem head [1,10,100] "apple"
(1,'a')
doToBoth_enum :: (Enum a, Enum b)
      => (∀ x . Enum x => x -> x) -> a -> b -> (a,b)
doToBoth_enum f x y = (f x, f y)
> doToBoth_enum pred 2 'b'
(1,'a')