Haskell中的"真正"泛型函数

Anu*_*ain 14 haskell existential-type

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

data M o = M (String,o)
Run Code Online (Sandbox Code Playgroud)

现在,我可以定义一个适用于ALL的函数,M而不管它是什么o.例如 -

f :: M o -> M o
f (M (s,o)) = M (s++"!", o)
Run Code Online (Sandbox Code Playgroud)

然而,f并不像我希望的那样普遍.特别是,f在表达式中使用修复了类型,o所以我不能再使用f不同类型的o.例如,以下不是类型检查 -

p f = undefined where
  m1 = M ("1", ())
  m2 = M ("2", True)
  m1' = f m1
  m2' = f m2
Run Code Online (Sandbox Code Playgroud)

它产生错误 - 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
Run Code Online (Sandbox Code Playgroud)

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

Sjo*_*her 15

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

p :: (forall o. M o -> M o) -> a
Run Code Online (Sandbox Code Playgroud)

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

{-# LANGUAGE Rank2Types #-}
Run Code Online (Sandbox Code Playgroud)

要么

{-# LANGUAGE RankNTypes #-}
Run Code Online (Sandbox Code Playgroud)

在这里阅读更多相关信息:http://www.haskell.org/haskellwiki/Rank-N_types


Ing*_*ngo 5

这有什么特别的原因吗?

的确,有一个.用一句话来说:如果解除了HM系统的限制,那么类型推断将无法正常工作,因为lambda参数必须是单态的.

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