Haskell 中的“真正”通用函数
Posted
技术标签:
【中文标题】Haskell 中的“真正”通用函数【英文标题】:A "truly" generic function in Haskell 【发布时间】:2011-08-19 11:47:14 【问题描述】:假设我有一个复合数据类型 -
data M o = M (String,o)
现在,我可以定义一个适用于所有M
的函数,而与o
无关。比如——
f :: M o -> M o
f (M (s,o)) = M (s++"!", o)
但是,f
并不像我希望的那样普遍。特别是,在表达式中使用f
修复了o
的类型,因此我不能再次将f
与不同类型的o
一起使用。例如以下内容不进行类型检查 -
p f = undefined where
m1 = M ("1", ())
m2 = M ("2", True)
m1' = f m1
m2' = f m2
它会产生错误 - Couldn't match expected type 'Bool' with actual type '()'
令人惊讶的是,如果我不提供 f 作为参数,而是简单地使用 f 的全局定义,那么它可以编译并正常工作!即编译 -
p = undefined where
m1 = M ("1", ())
m2 = M ("2", True)
m1' = f m1
m2' = f m2
这有什么特别的原因吗?我该如何解决这个问题,即定义一个可以应用于所有(M o)
的函数f
,即使o
在同一个表达式中变化?我猜存在类型在这里发挥作用,但我就是不知道如何。
【问题讨论】:
为什么要让 M 成为一个元组?多么丑陋。 默认情况下有些东西是“forall”,有些东西不是。你可能会看到这些文字:en.wikibooks.org/wiki/Haskell/Existentially_quantified_types @monadic,这实际上只是一个最小的示例,它重现了我在实际代码中遇到的问题(这要复杂得多)。 【参考方案1】:问题是编译器无法正确推断 p 的类型。你必须给它一个类型签名:
p :: (forall o. M o -> M o) -> a
这是 2 级类型,因此您需要语言扩展:
-# LANGUAGE Rank2Types #-
或
-# LANGUAGE RankNTypes #-
在此处了解更多信息:http://www.haskell.org/haskellwiki/Rank-N_types
【讨论】:
谢谢!添加该类型签名有效。我认为它与 forall 关键字有关,但不知道 RankN 类型。看来我要读书了!【参考方案2】:这有什么特别的原因吗?
确实,有一个。一句话概括:如果解除 HM 系统对 lambda 参数必须是单态的限制,类型推断将无法正常工作。
Simon Peyton Jones 设计了一个类型检查器,它扩展了 HM 并允许更高等级的多态性。但在这种情况下,需要显式类型注释。请参阅 Sjoerds 答案。
【讨论】:
【参考方案3】:函数p f
范围内的f
与***函数f
不同。它是在调用p
时作为参数给出的任何函数。由于您没有给 p
类型签名,编译器必须从您使用它的方式推断出 f
是什么。您的第一次使用意味着f :: M () -> M ()
,即p :: (M () -> M ()) -> a
。正如其他人所说,您必须使用forall
给p
一个明确的签名才能执行您想做的事情。
【讨论】:
以上是关于Haskell 中的“真正”通用函数的主要内容,如果未能解决你的问题,请参考以下文章