0

So I'm writing a line to get the second to last element of a list. Initially my code was

mySLast x = last.take ((length x) - 1) x

Which worked up until the last function. Realized the my take business is already included in Haskell as init so I rewrote as

mySLast = last.init 

This still doesn't work. I find this puzzling because init::[a]->[a] and last::[a]->a so they definitely should be composable morphisms in the Hask Category.

I tried asking Haskell what it thinks the type is and it said

ghci> :t last.init
last.init :: [c] -> c
ghci> last.init [3,2,4,1]

<interactive>:45:6:
    Couldn't match expected type ‘a -> [c]’
                with actual type ‘[Integer]’
     Relevant bindings include
       it :: a -> c (bound at <interactive>:45:1)
    Possible cause: ‘init’ is applied to too many arguments
     In the second argument of ‘(.)’, namely ‘init [3, 2, 4, 1]’
     In the expression: last . init [3, 2, 4, 1]

Even though

ghci> init [3,2,4,1]
[3,2,4]
ghci> last [3,2,4]
4

So I must be misunderstanding something about composing functions in Haskell. Any insight would be greatly appreciated!

dfeuer
  • 48,079
  • 5
  • 63
  • 167
Tshimanga
  • 845
  • 6
  • 16

3 Answers3

10

Function application binds more tightly than (.) so

last.init [3,2,4,1]

is being parsed as

last . (init [3,2,4,1])

you can use

(last . init) [3,2,4,1]

or

last . init $ [3,2,4,1]
Lee
  • 142,018
  • 20
  • 234
  • 287
1

You forgot the $ between init and your list, e.g.

last . init $ [3,2,4,1]
            ↑ See here 
Rizier123
  • 58,877
  • 16
  • 101
  • 156
1

An alternative solution to this problem that only evaluates the (spine of the) list up to the needed element, rather than using length (which will evaluate the entire spine before walking back to the needed element):

takeTail n xs = go (drop n xs) xs
  where
    go (_:ys) (_:xs) = go ys xs 
    go []     xs     = xs  -- Stop and return xs when ys runs out

> head . takeTail 2 $ [1,2,3,4]
3
Rein Henrichs
  • 15,437
  • 1
  • 45
  • 55
  • The approach the user is trying to take has this property as well. This example can be improved by giving an explicit type signature `go :: [a] -> [b] -> [b]`, clarifying that the first argument is used only for its length. – dfeuer Oct 01 '15 at 19:20
  • @dfeuer Not if they're calculating the length. – Rein Henrichs Oct 01 '15 at 21:51
  • Yes; their "rough draft" would indeed be inefficient. The version they typed with insufficient parentheses would be fine, although not as general as yours. – dfeuer Oct 01 '15 at 21:56