8

Lately I've been playing around with Haskell, and specifically the whole functors concept. The more I dive into it, the more a-ha moments I'm getting, and it certainly tickles my dopamine receptors quite a bit.

The problem I'm stuck with is the following. Here's the code that works, it lifts the function and then applies it first to the IO value, and then to a List.

replicator1 =
  fmap (replicate 3)

replicator2 =
  fmap (replicate 3)

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

It is very tempting to write it in a more concise manner, i.e.:

replicator =
  fmap (replicate 3)

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

Part of me says it's conceptually right, since replicator should be applyable both to IO and to List instances, but being a strongly typed language Haskell doesn't allow me to do so. I think I pretty much understand why is this happening.

The question is: is there any way I can get any closer to the latter variant? Or is it fine to live with the former?

Thank you!

4

1 回答 1

19

您的代码实际上很好,除了您遇到了可怕的单态性限制,这使 Haskell无法推断replicator.

本质上,由于受到限制,Haskell 不会为看起来不像函数的绑定推断多态类型。这意味着它必须选择一个具体的仿函数,例如[]or IOreplicate如果您尝试在两种不同的上下文中使用它,则会导致错误。

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

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

  • 看起来replicator 一个函数:

    replicator x = fmap (replicate 3) x
    
  • 向您的代码添加显式类型签名

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

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

于 2015-08-19T05:34:52.307 回答