Haskell basic - pattern matching vs list tail

81 views Asked by At

I'm doing Set7.hs from haskell.mooc.fi/ Exercises7

-- Ex 5: reverse a NonEmpty list.
--
-- PS. The Data.List.NonEmpty type has been imported for you

-- below doesn't work
-- reverseNonEmpty :: NonEmpty a -> NonEmpty a
-- reverseNonEmpty  (h:|tail) = head reversed :| (tail reversed)
--   where 
--     reversed = reverse (h:tail)

-- below works
reverseNonEmpty :: NonEmpty a -> NonEmpty a
reverseNonEmpty (h :| tail) = head reversed :| revTail
  where
    reversed = reverse (h : tail)
    -- revTail = (tail reversed) - this doesn't work, but WHY???
    (_:revTail) = reversed

error message is:

Set7.hs:152:15: error:
    • Couldn't match expected type ‘[a] -> t’ with actual type ‘[a]’
    • The function ‘tail’ is applied to one argument,
      but its type ‘[a]’ has none
      In the expression: tail reversed
      In an equation for ‘revTail’: revTail = tail reversed
    • Relevant bindings include
        revTail :: t (bound at Set7.hs:152:5)
        reversed :: [a] (bound at Set7.hs:151:5)
        tail :: [a] (bound at Set7.hs:149:23)
        h :: a (bound at Set7.hs:149:18)
        reverseNonEmpty :: NonEmpty a -> NonEmpty a
          (bound at Set7.hs:149:1)
    |
152 |     revTail = tail reversed

I don't understand error message, what's wrong with tail reversed?

Why version with pattern matching (_:revTail) = reversed work?

3

There are 3 answers

0
Ben On BEST ANSWER

reverseNonEmpty (h :| tail) = ...

You've bound the name tail yourself, to refer to the list part of the NonEmpty. So this shadows the normal tail function, and there's no way to refer to that inside the body of reverseNonEmpty.

This is why the error message is telling you that tail has type [a] and can't be applied to an argument.

0
chi On

To avoid these issues I strongly recommend you compile your code with warnings enabled. You can use the GHC flag -Wall for that.

With warnings on, GHC spots the issue:

<source>:6:23: warning: [GHC-63397] [-Wname-shadowing]
    This binding for `tail' shadows the existing binding
      imported from `Prelude' at <source>:1:1
      (and originally defined in `GHC.List')
  |
6 | reverseNonEmpty (h :| tail) = head reversed :| revTail
  |                       ^^^^

GHC here warns that there are two things in scope and both are named tail. One is the library function, the other is the local variable bound through pattern matching.

0
willeM_ Van Onsem On

As others already said. Please don't use variable names with the same name as already existing ones in an outer scope. You assigned tail to the tail of the input, so that will not work.

I however think that the intend was to write the reverse through recursion. We can recurse with:

reverseNonEmpty :: NonEmpty a -> NonEmpty a
reverseNonEmpty (h :| t) = go t [h]
  where
    go [] ys = …
    go (x : xs) ys = …

where I leave implementing the parts as an exercise.