2
split :: [a] -> Int -> ([a], [a])
split [xs] n = 
    (take n [xs], drop n [xs])

The same code works if I give the variable as xs instead of [xs], signatures are same in both cases. Using [xs] gives the error that pattern is non-exhaustive. I understand it's telling that the input I gave is not covered by my code, but not clear what is happening under the hood.

Test input: [1,2,3] 2.

duplode
  • 33,731
  • 7
  • 79
  • 150
peeyush singh
  • 1,337
  • 1
  • 12
  • 23
  • 10
    Well `[xs]` matches a list with *exactly* one element, and that element is then unified with `xs`. Somehow people ask this question every other day :( – Willem Van Onsem Feb 20 '18 at 11:29
  • 2
    In the future, please try to give your questions more specific titles. "Haskell code doesn't work, why?" says almost nothing about your problem. – duplode Feb 20 '18 at 12:39
  • 1
    @WillemVanOnsem I agree, this error is way too frequent here on SO. We might need to write a "reference" question/answer, addressing the general issue, so that we can close all such questions as duplicates. We did that for the monomorphism restriction (after a bit of discussion on meta, IIRC). – chi Feb 20 '18 at 13:48
  • 6
    I have a counter-question: why would you expect `[xs]` to mean the same thing as `xs`? – Daniel Wagner Feb 20 '18 at 16:36
  • 1
    @DanielWagner I do not expect [xs] to mean same thing as xs, however as mentioned (comments in ans) below the primary confusion came as in Haskell when declaring types you use [] for list. That plus since I do not frequently use Haskell so as default wrote the code like that. As mentioned in the question I knew how to get the code working and understood partly what the compiler was complaining but still could not get the overall picture. Thanks for answers now I know a little bit more. Maybe adding this as a reference question answer is a good idea. – peeyush singh Feb 21 '18 at 14:35
  • See also [*Pattern matching expects round braces for non empty list and not square brackets*](https://stackoverflow.com/q/49831324/2751851) for extra discussion. – duplode Apr 14 '18 at 14:58

1 Answers1

12

Somehow a lot of people think that [xs] as pattern means that you unify a list with xs. But this is incorrect, since the function signature (either derived implicitly, or stated explicitly) already will prevent you to write code where you call the function with a non-list item.

A list has two constructors:

  • the empty list []; and
  • the "cons" (h : t) with h the head (first element), and t the tail (a list with the remaining elements).

Haskell however introduces some syntactical sugar as well. For example [1] is short for (1:[]), and [1, 4, 2] for (1:(4:(2:[]))).

So that means that if you write [xs], behind the curtains you defined a pattern (xs: []) which thus means you match all lists with exactly one element, and that single element (not the entire list) is then xs.

Anyway, the solution is to use:

split xs n = (take n xs, drop n xs)

Since both take :: Int -> [a] -> [a] and drop :: Int -> [a] -> [a] have in the signature that xs is supposed to be a list, Haskell will derive automatically that n is supposed to be an Int, and xs an [a].

Note that you can use splitAt :: Int -> [a] -> ([a], [a]) as well. We can make the signature equivalent to the one you target with:

split = flip splitAt
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 2
    I actually liked your comment more since that explained me the (or my) issue very clearly. Think probably this a typical beginner problem, partly due to the fact that in a function signature [a] would mean a list while the same ([a]) in code means a list containing a single value! – peeyush singh Feb 20 '18 at 12:56
  • @peeyushsingh Yeah, the "`[]`-the-type vs `[]`-the-value" pun can confuse beginners. You have to remember that types and values live in different worlds. When you define `data Foo = Foo`, the left-hand side defines a type named `Foo` and the right-hand side defines a value which coincidentally happens also to be named `Foo` (but in a different namespace). That's just the same as what's going on with `[]` and `[]`. – Benjamin Hodgson Feb 20 '18 at 14:29
  • @peeyushsingh: You can think of the type `[a]` as syntactic sugar for the normal prefix syntax `[] a`—which you can use instead, just like other prefix type constructors such as `Maybe a`. It’s just visually nicer to write `[[Int]]` than `[] ([] Int)`, for example. – Jon Purdy Feb 21 '18 at 06:42