函数的Haskell类型推断

时间:2015-08-19 05:24:29

标签: haskell functor monomorphism-restriction

最近我一直在玩Haskell,特别是整个仿函数概念。我越是深入研究它,我得到的时刻越多,它肯定会让我的多巴胺受体发挥得相当多。

我遇到的问题如下。这是有效的代码,它提升了函数,然后将其首先应用于IO值,然后应用于List。

replicator1 =
  fmap (replicate 3)

replicator2 =
  fmap (replicate 3)

main = do
  replicated <- replicator1 getLine
  print (replicator2 replicated)

以更简洁的方式编写它是非常诱人的,即:

replicator =
  fmap (replicate 3)

main = do
  replicated <- replicator getLine
  print (replicator replicated)

我的一部分说它在概念上是正确的,因为replicator应该适用于IO和List实例,但作为强类型语言Haskell不允许我这样做。我想我非常明白为什么会这样。

问题是:有什么方法可以让我更接近后一种变体?或者和前者住在一起好吗?

谢谢!

1 个答案:

答案 0 :(得分:19)

您的代码实际上很好,除非您遇到dreaded monomorphism restriction,这使得Haskell不会推断 replicator最常用的类型。

基本上,受限制,Haskell不会推断多态类型的绑定看起来不像函数。这意味着 []选择IOreplicate这样的具体函子,如果您尝试在两个不同的上下文中使用它,则会导致错误。< / p>

您可以通过三种方式使代码工作:

  • 关闭单态限制:在模块顶部添加{-# LANGUAGE NoMonomorphismRestriction #-}

  • replicator 看起来就像一个函数:

    replicator x = fmap (replicate 3) x
    
  • 在代码中添加显式类型签名

    replicator :: Functor f => f a -> f [a]
    replicator = fmap (replicate 3)
    

第三种选择是最惯用的。良好的Haskell样式涉及向所有顶级标识符添加显式类型签名。但是,知道其他两个选项以了解正在发生的事情以及能够在Haskell中编写快速和脏的一次性脚本而不必担心类型签名是很有用的。