Haskell中的“真正”泛型函数

时间:2011-08-19 11:47:14

标签: haskell existential-type

假设我有一个复合数据类型 -

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

这有什么特别的原因吗?我如何解决这个问题,即定义一个可以应用于所有f的函数(M o),即使o在同一个表达式中变化?我猜测存在类型在这里发挥作用,但我无法弄清楚如何。

3 个答案:

答案 0 :(得分:15)

问题是编译器无法正确推断p的类型。你必须给它一个类型签名:

p :: (forall o. M o -> M o) -> a

这是排名2类型,因此您需要语言扩展名:

{-# LANGUAGE Rank2Types #-}

{-# LANGUAGE RankNTypes #-}

在此处详细了解:http://www.haskell.org/haskellwiki/Rank-N_types

答案 1 :(得分:5)

  

这有什么特别的原因吗?

确实,有一个。用一句话来说:如果取消HM系统对lambda参数必须是单态的限制,那么类型推断将无法正常工作。

Simon Peyton Jones设计了一种类型检查器,它扩展了HM并允许更高级别的多态性。但在这种情况下,需要显式类型注释。见Sjoerds答案。

答案 2 :(得分:2)

函数f范围内的p f与顶级函数f不同。它是在调用p时作为参数给出的任何函数。由于您未给出p类型签名,因此编译器必须根据您使用它的方式推断f。您的首次使用意味着f :: M () -> M (),即p :: (M () -> M ()) -> a。正如其他人所说,你必须使用pforall一个明确的签名来做你想做的事。