2014-08-31 59 views
6

我想编写一个Haskell函数,它返回一个附加到自身计数时间的列表(如Python中的lst * count)。Pointfree版本不能编译,但有意义的版本呢?

我第一次尝试是:

self_append_n :: Int -> [a] -> [a] 
self_append_n = concat . replicate 

我的理由是,replicate接受计数和价值,并产生值的列表。当该值本身是一个列表时,剩下的就是将这些列表连接在一起。然而,这给出了一个令人困惑的错误:

Couldn't match type `[a0]' with `[a] -> [a]' 
Expected type: [[a0]] -> [a] -> [a] 
    Actual type: [[a0]] -> [a0] 
In the first argument of `(.)', namely `concat' 
In the expression: concat . replicate 
In an equation for `self_append_n': 
    self_append_n = concat . replicate 

然后我写了一个贴题版本:

self_append_n a b = concat $ replicate a b 

和它的作品!

为什么免费版本无法编译,但添加点使其工作?

回答

11

这可能有助于明确parenthesise签名:

selfAppend :: Int -> ([a]   -> [a]) 
replicate :: Int -> ([a]->[[a]]) 
concat  ::   [[a]]  -> [a] 

如果您尝试撰写concat . replicate,你最终给concat部分appied结果replicate,即[a] -> [[a]]。这不符合[[a]]

您需要做的是在交付结果之前首先通过两个参数到replicate。 IMO做到这一点最好的办法就是 “半pointfree”:

selfAppend n = concat . replicate n 

的可读性替代将是

selfAppend' = curry $ concat . uncurry replicate 
selfAppend'' = (concat.) . replicate 

或者与臭名昭著操作

(.:) :: (c->d) -> (a->b->c) -> a->b->d 
(.:) = (.).(.) 
-- `≡ fmap fmap fmap`, also a popular implementation... 

可以简单的写

selfAppend''' = concat .: replicate 
+4

我明白了。组合失败,因为复制的实际返回类型不是列表,而是函数(它本身返回一个列表)。 – 2014-08-31 21:05:18

+1

是的。不幸的是,Haskell的_curry everything_哲学并不总是一种好处(尽管通常是这样)。 – leftaroundabout 2014-08-31 21:09:09