9

I'm trying to convert several projects to classy-prelude at the moment. While most behaviour seems quite straightforward to me, the (head . head) gives mysterious errors on a simple 2D list.

Consider the following GHCi session:

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

Let's try this with ghci -XNoImplicitPrelude and classy-prelude:

> import ClassyPrelude
ClassyPrelude> (head . head) [[1,2],[3,4]]

<interactive>:10:1:
    Couldn't match type `MinLen (Succ nat1) mono1' with `[[t0]]'
    Expected type: [[t0]] -> Element mono0
      Actual type: MinLen (Succ nat1) mono1 -> Element mono0
    The function `head . head' is applied to one argument,
    but its type `MinLen (Succ nat1) mono1 -> Element mono0'
    has only one
    In the expression: (head . head) [[1, 2], [3, 4]]
    In an equation for `it': it = (head . head) [[1, 2], [3, 4]]

I assume GHC simply can't resolve the types for multidimensional lists correctly. Is there any way I can help it without resorting to (Prelude.head . Prelude.head)?

Yuuri
  • 1,858
  • 1
  • 16
  • 26
Uli Köhler
  • 13,012
  • 16
  • 70
  • 120
  • 1
    `MinLen (Succ nat1) mono1` => I think you need a non-empty list type there – Mauricio Scheffer Feb 25 '14 at 22:59
  • @MauricioScheffer Where exactly do you think I need a non-empty list? `[[1,2],[3,4]]` is non-empty in both dimensions, and GHC *should* be able to derive an Int type from it (it can do it for `Prelude.head`) – Uli Köhler Feb 25 '14 at 23:01
  • 4
    @UliKöhler I think MauricioScheffer is right. Those lists are non-empty, but their *type* is not a "non-empty list" type. – Daniel Wagner Feb 25 '14 at 23:52
  • 5
    @DanielWagner Do you mean that while `Prelude.head`, being a [Partial function](http://www.haskell.org/haskellwiki/Partial_functions), fails with `error` on non-empty lists, `classy-prelude` tried to solve this problem by defining non-empty lists as a different types? – Uli Köhler Feb 25 '14 at 23:57
  • 3
    I think that's exactly correct. – J. Abrahamson Feb 26 '14 at 00:42

1 Answers1

11

As already mentioned in the comments, classy prelude's head function only works on traversables which are guarranted to have at least one element by the type-system, so that it doesn't have to be partial. Because all your lists have at minimum one element, you can just use the non-empty list type:

head . head $ mlcons (mlcons 1 $ mlcons 2 $ toMinLenZero []) $ mlcons (mlcons 3 $ mlcons 4 $ toMinLenZero []) $ toMinLenZero [] :: Int
-- 1

(The functions starting with ml are all from the MinLen module of mono-traversable, which is reexported by classy-prelude)

If you just want the behaviour of the Prelude.head function, you can use unsafeHead again from the mono-traversable package and exported by default:

unsafeHead . unsafeHead [[1,2],[3,4]]
-- 1

There is also headMay in that module, which can be used if you like to handle failure differently and not crash the whole program.

bennofs
  • 11,873
  • 1
  • 38
  • 62
  • 2
    Great answer, I'd just throw in `headEx` as well. The difference between that and `unsafeHead` is that `unsafeHead` may cause a segfault with certain datatypes (`Vector` and `ByteString` in particular), whereas `headEx` is guaranteed to throw an exception on an empty container. – Michael Snoyman Feb 26 '14 at 11:25