Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointfree version doesn't compile, but the pointful one does?

I want to write a Haskell function that returns a list appended to itself count times (like lst * count in Python).

My first attempt is:

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

My reasoning is that replicate accepts a count and a value, and produces a list of values. When the value is itself a list, all that remains is to concatenate the lists together. However, this gives a bewildering error:

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

Then I wrote a pointful version:

self_append_n a b = concat $ replicate a b

and it works!

Why does the point-free version fail to compile, but adding points makes it work?

like image 678
ridiculous_fish Avatar asked Aug 31 '14 20:08

ridiculous_fish


1 Answers

It probably helps to explicitly parenthesise the signatures:

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

If you try to compose concat . replicate, you end up giving concat the partially appied result of replicate, i.e. [a] -> [[a]]. That doesn't unify with [[a]].

What you need to do is first pass both arguments to replicate before handing on the result. IMO best way to do that is "semi-pointfree":

selfAppend n = concat . replicate n

Less readable alternatives would be

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

Or with the infamous operator

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

you can simply write

selfAppend''' = concat .: replicate
like image 95
leftaroundabout Avatar answered Sep 20 '22 01:09

leftaroundabout