3

I'm just starting out with Haskell, so I'm trying to wrap my head around the "Haskell way of thinking." Is there a reason to use pattern matching to solve Problem 1 here basically by unwrapping the whole list and calling the function recursively, instead of just retrieving the last element directly like myLast lst = lst !! ((length lst) - 1)? It seems almost brute-force, but I assume it's just my lack of familiarity here.

Jess
  • 1,515
  • 3
  • 23
  • 32
  • 5
    I’d argue the “Haskell way of thinking” would actually take fault with the question, since `myLast` is clearly partial—what does `myLast []` mean? Therefore, the result type should be widened to `Maybe a` or the argument type should be narrowed to `NonEmpty a`, but perhaps that isn’t a very helpful response to your question. – Alexis King Oct 07 '18 at 02:29
  • When dealing with list type, [head](http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:head), [tail](http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:tail), [init](http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:init) and [last](http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:last) are your friends yet as mentioned, lists are not as efficient as arrays in some other languages. There are better data structs in Haskell. Have a look at https://stackoverflow.com/questions/9611904/haskell-lists-arrays-vectors-sequences – Redu Oct 07 '18 at 07:37

1 Answers1

9

A few things I can think of:

  • (!!) and length are ultimately implemented using recursion over the structure of the list. That being so, it can be a worthwhile learning exercise to implement those basic functions using explicit recursion.

  • Keep in mind that, under the hood, the retrieval of the last element is not direct. Since we are dealing with linked lists, length has to go through all elements of the lists, and (!!) has to go through all elements up to the desired index. That being so, lst !! (length lst - 1) runs through the whole list twice, rather than once. (This is one of the reasons why, as a rule of thumb, length is better avoided unless you actually need to know the number of elements in and of itself, and not just as a proxy to something else.)

  • Pattern matching is a neat way of stating facts about the structure of data types. If, while consuming a list recursively, you match a [x] pattern (or, equivalently, x : [] -- an element consed to the empty list), you know that x is the last element. In a way, matching [x] involves one less level of indirection than accessing the list element at index length lst - 1, as it only deals with the structure of the list, without requiring an indexing scheme to be bolted on the top of it.

With all that said, there is something fundamentally right about your feeling that explicit recursion feels "almost brute-force". In time, you'll find out about folds, mapping functions, and other ways to capture and abstract common recursive patterns, making it possible to write in a more fluent manner.

duplode
  • 33,731
  • 7
  • 79
  • 150
  • 2
    Do note that `(!!)` is also a partial function. Avoiding `(!!)` is generally good form -- so you can explicitly handle the related edge cases. – Aearnus Oct 07 '18 at 06:06