6

Is there builtin or idiomatic way in Haskell to "unpack" the elements of a list and treat them as individual arguments to a function?

For example, if I have f :: a -> b -> c -> d -> e is there something compact like

f (unlist x)

accomplishes

let x = [1,2,3,4] in f (x!!0) (x!!1) (x!!2) (x!!3) 

or at least a less "shouty" (too many !! repetitions) way to unpack a list of known length in general (so that it can be used as arguments to a function, in this case).


Essentially what I'm looking for is something like what Sequence@@ does in Mathematica:

f[Sequence@@{1, 2, 3, 4}]
orome
  • 45,163
  • 57
  • 202
  • 418
  • 2
    I don't think that would type-check in Haskell's type system. Maybe with variations like `unlist1`, `unlist2`, `unlist3`, etc. In that case, `unlist3 f [a, b, c] = f a b c` and use like `unlist3 f x` or using infix to looks like a function "call". – Mephy Sep 13 '15 at 18:58
  • 3
    There is [a trick](http://okmij.org/ftp/Haskell/polyvariadic.html#polyvar-fn) to persuade the type system to let you write polyvariadic functions. It is relatively non-trivial and heavyweight, and so you won't see it often. The best known use case is [`Text.Printf`](http://hackage.haskell.org/package/base-4.8.1.0/docs/Text-Printf.html). – duplode Sep 13 '15 at 19:12

1 Answers1

6

It wouldn't be particularly useful in Haskell type system:

  1. As Mephy points out, you'd need a separate function for each list length, and it would fail at runtime when passed a list with wrong length;
  2. All arguments would have to have the same type.

Given this, use of tuples makes more sense than lists, as it avoids both problems; the standard library includes uncurry which does this for functions of 2 arguments, and you could define uncurry3, etc. by analogy:

uncurry3                 :: (a -> b -> c -> d) -> ((a, b, c) -> d)
uncurry3 f (a, b, c)     =  f a b c
duplode
  • 33,731
  • 7
  • 79
  • 150
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Is there a way to fix my naive approach so that it looks less like I'm shouting (there are a lot of !!!!!!!s in it!)? – orome Sep 13 '15 at 19:25
  • 2
    That's fine as far as it goes, but in general, try to avoid `!!` unless it's necessary. E.g. use pattern matching instead of `!!` to access elements at small known indices: `let [a, b, c, d] = [1,2,3,4] in f a b c d`. – Alexey Romanov Sep 13 '15 at 19:25
  • 2
    @raxacoricofallapatorius ... and to access elements at *large* known indices in most cases you should switch to a different data structure, as `(!!)`, beyond being partial, is also *O(n)* in the index. – duplode Sep 13 '15 at 19:30
  • @AlexeyRomanov: I like that approach but it doesn't work for me. If I replace `readsPrec _ s = [(f (x!!0) (x!!1) (x!!2) (x!!3), "")] where x = words s` (which works perfectly; `f` does validation) with `readsPrec _ s = [(f c w s r, "")] where [c, w, s, r] = words s` execution just hangs. Not sure what I'm missing there. – orome Sep 13 '15 at 19:42
  • 1
    @raxacoricofallapatorius, you reused the variable name `s`... – Reid Barton Sep 13 '15 at 19:47
  • @ReidBarton: Ah. Ooops! – orome Sep 13 '15 at 19:48
  • @raxacoricofallapatorius Note also that `[a, b, c, d]` pattern verifies that you have exactly 4 elements; use `(a : b : c : d : _)` otherwise. – Alexey Romanov Sep 13 '15 at 20:56