2013-11-26 41 views
2

我正在学习Haskell和我试图实现我自己的replicate功能,下面是我的工作的结果是:通过列表理解来实现复制功能

replicate' :: Enum a => a -> b -> [b] 
replicate' a b = [b | _ <- [1..a]] 

但在加载脚本到ghci中我得到msg:

proginhaskell.hs:152:29: 
Could not deduce (Num a) arising from the literal `1' 
from the context (Enum a) 
    bound by the type signature for 
      replicate' :: Enum a => a -> b -> [b] 
    at proginhaskell.hs:152:1-34 
Possible fix: 
    add (Num a) to the context of 
    the type signature for replicate' :: Enum a => a -> b -> [b] 
In the expression: 1 
In the expression: [1 .. a] 
In a stmt of a list comprehension: _ <- [1 .. a] 

我想问一下如何修复这个问题。这是否是正确的方法来实现复制功能?

回答

5

问题是,您正在使用列表表达式[1..a],这意味着我们必须能够将1视为与a相同的类型。该类型的1

1 :: Num a => a -- Numbers are actually polymorphic! 

所以GHC抱怨你没有说aNum实例。所以你可以在a上加上Num来约束你的约束。

replicate' :: (Enum a, Num a) => a -> b -> [b] 
replicate' a b = [b | _ <- [1..a]] 

本代码有多好,我可能会用take写出来,但都是精细的实现。如果你很好奇它是如何切实执行(尽管更严格的replicate :: Int -> a -> [a]

replicate n x = take n (repeat x) 
+0

“至于这段代码有多好,我可能会用明确的递归来写它,以避免生成一个无用的列表,但都是很好的实现。” - 我相信GHC能够优化这种情况,并且摆脱'[1..a]'生成的列表。参见[快捷融合](http://www.haskell.org/haskellwiki/Short_cut_fusion)。 –

+0

@PedroRodrigues我很好奇,分析了核心,看起来两者都被编译成相同的代码,最后我更喜欢前奏'take'。我相应地编辑了我的答案 – jozefg

2

从这个代码片段[1..a]编译inferes是a应该有相同类型的1。由于1Num,因此a应该是Num类型。所以,而不是Enum a你应该有(Enum a, Num a)作为你的上下文。