質問

To strip the last item from a list in GHCi I can reverse the list, take the tail and then reverse it again. For example,

reverse(tail(reverse([1,2,3,4])))

As there are quite a lot of brackets there I thought I would change it to use function composition instead. However when I try this I get the following error.

Prelude> reverse . tail. reverse [1,2,3,4]

<interactive>:2:17:
    Couldn't match expected type `a0 -> [a1]' with actual type `[a2]'
    In the return type of a call of `reverse'
    Probable cause: `reverse' is applied to too many arguments
    In the second argument of `(.)', namely `reverse [1, 2, 3, 4]'
    In the second argument of `(.)', namely
      `tail . reverse [1, 2, 3, 4]'

I think this means that it doesn't like composing reverse [1,2,3,4], so I tried putting brackets around it, but it gives me the same error.

Prelude> reverse . tail. (reverse [1,2,3,4])

<interactive>:3:18:
    Couldn't match expected type `a0 -> [a1]' with actual type `[a2]'
    In the return type of a call of `reverse'
    Probable cause: `reverse' is applied to too many arguments
    In the second argument of `(.)', namely `(reverse [1, 2, 3, 4])'
    In the second argument of `(.)', namely
      `tail . (reverse [1, 2, 3, 4])'

But if I do the following it works correctly.

Prelude> let f = reverse . tail . reverse
Prelude> f [1,2,3,4]
[1,2,3]

What is it that causes this error and why does the let binding stop this from happening?

役に立ちましたか?

解決

When you see type of . you will notice that it expects both of its operands to be functions (of suitable types so that they can be composed)

Prelude> :i (.)
(.) :: (b -> c) -> (a -> b) -> a -> c   -- Defined in `GHC.Base'
infixr 9 .

Now function application has higher precedence than any infix operator so

Prelude> reverse . tail . reverse [1,2,3,4]

becomes

Prelude> reverse . tail . (reverse [1,2,3,4])

which has wrong type when you try to apply composition. For composition to be correct you have to explicitly give composition higher precedence than function application which you can do by providing explicit parentheses.

Prelude> (reverse . tail . reverse) [1,2,3,4]

This solutions works but haskellers hate using parentheses. Here comes another operator $ which can save you in such places and make your code more readable than using parentheses. When you see the type of $

Prelude> :i ($)
($) :: (a -> b) -> a -> b       -- Defined in `GHC.Base'
infixr 0 $

You can clearly see that its precendence (0) is less than that of compostion (9) which gives you just the effect you want. You can just lower the precedence of last function application or replace function composition with $.

Prelude> reverse . tail . reverse $ [1,2,3,4]
Prelude> reverse . tail $ reverse [1,2,3,4]
Prelude> reverse $ tail $ reverse [1,2,3,4]

This is perfectly fine as first operand of $ is a function and other is just a value of correct type.

他のヒント

In the let case the functions are composed with . and then a list is given as an argument to the result of the function composition:

(reverse . tail . reverse) [1,2,3,4]

In the other case the list is given as an argument to reverse and the result of that is tried to be composed with some other functions:

reverse . tail . (reverse [1,2,3,4])

Since the reversed list returned by reverse [1,2,3,4] isn't a function, using it as an argument for . leads to a type error.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top