10

I've been learning Haskell for a few weeks, and I have a question about the use of the underscore (_) as a function parameter. I think my question will be better asked with a specific example. Let's say I want to define a function that extracts the element of a list based on the provided index—yes, I realize (!!) is already pre-defined. The two ways that I can define the function (I'm sure there are more) are the following:

Version 1

indexedElement             :: [a] -> Int -> a
indexedElement xs n | n < 0 = error "Index can not be negative."
indexedElement [] n         = error "Index must be smaller than the length of the list."
indexedElement (x:xs) 0     = x
indexedElement (x:xs) n     = indexedElement xs (n - 1)

Version 2

indexedElement            :: [a] -> Int -> a
indexedElement _ n | n < 0 = error "Index can not be negative."
indexedElement [] _        = error "Index must be smaller than the length of the list."
indexedElement (x:_) 0     = x
indexedElement (_:xs) n    = indexedElement xs (n - 1)

The two versions are obviously very similar. The only difference between the two is the use of an explicit variable or an underscore. To me _ means that literally anything can be written there while an explicit variable like n makes it more obvious that the argument must be an integer. For that reason, I prefer Version 1; but the GHC source code for (!!) is written like Version 2. Is there a functional advantage of the second version? If not, would "hardcore" Haskell programmers take issue with Version 1? I understand the importance of having a consistent way of writing code, so I try to follow the "unwritten rules" for programming in a particular language. This is an example where I much prefer the first version though, and I don't think it makes the code any more difficult to read. I don't know if it's due to my background in pure math or what, but I'd like to hear what you more-seasoned-Haskell vets think.

philomathic_life
  • 514
  • 3
  • 18
  • 6
    I'm not sure about performance differences (you could try outputting the core of both versions to see if there are any differences), but most haskellers would prefer the second version since it reduces the number of names that need to be bound. The `_` means that any value can go there (so long as its the right type, which is given by the type signature. While it may feel more natural to use `n` to indicate an integer even when it's not in use, keep in mind that not everyone will have that bias. – bheklilr Nov 24 '15 at 20:09
  • 5
    You can compromise by naming the variable `_n`, which turns off the warnings when using `-Wall`, indicates that the variable is not used and names it at the same time. – luqui Nov 24 '15 at 21:53
  • @bheklilr Okay, I appreciate your input. I will train myself to get more comfortable with `_`. Thanks. – philomathic_life Nov 24 '15 at 22:17
  • @luqui I appreciate the suggestion to avoid any warnings that would come up with my preferred method. Honestly, I never even knew about `-Wall`. – philomathic_life Nov 24 '15 at 22:19
  • 1
    @basketballfan22, luqui's approach has also become idiomatic in recent times, and I like it a lot. – dfeuer Nov 25 '15 at 01:36
  • @dfeuer Does any prominent library use that the feature mentioned by Luke ? – Sibi Nov 25 '15 at 17:22
  • 1
    @Sibi, I've definitely seen it a good bit, so it's almost certainly used by some, most likely including `base`, but I couldn't point to anything in particular off the top of my head. – dfeuer Nov 26 '15 at 03:58
  • 2
    @bheklilr, there certainly won't be any performance differences. Alpha conversion only affects anything in a language with too much reflection or with an insane compiler. – dfeuer Nov 26 '15 at 04:02
  • 1
    Another thing to think about is multiple parameters. You can write: `thing _ value _ = value` but `thing n value n` is an error. – psycotica0 Nov 27 '15 at 02:50

1 Answers1

23

Is there a functional advantage of the second version?

I don't think they have any operational difference. But I find the second version more readable. _ indicates that it isn't used at all. So while reading the code, I can just ignore it and just concentrate on the other parameters. Whereas in the first version, I will be thinking that n is defined but maybe the author forgot to use it ? Or maybe the argument isn't required. The second version just avoids this kind of mental overload. But this is just my opinion. :)

In fact, if you enable the warning flag (-Wall) and compile your code, it will throw warning for your first version:

[1 of 1] Compiling Main             ( code.hs, code.o )

code.hs:2:16: Warning: Defined but not used: ‘xs’

code.hs:3:19: Warning: Defined but not used: ‘n’

code.hs:4:19: Warning: Defined but not used: ‘xs’

code.hs:5:17: Warning: Defined but not used: ‘x’

code.hs:8:17: Warning: Defined but not used: ‘xs’
Sibi
  • 47,472
  • 16
  • 95
  • 163
  • Thanks a lot. As I mentioned in my post, I haven't been programming in Haskell for very long; consequently, I never even knew about `-Wall`. How does one turn it on? Do I simply type `-Wall` at the very beginning of my source code; and if so, should it precede any imports? – philomathic_life Nov 24 '15 at 22:21
  • 1
    @basketballfan22: It's a command-line option to the compiler. If you're compiling by hand with the `ghc` command, you supply it there. If you're using a build tool like Cabal or Stack, the documentation for them will say how to configure your project's GHC options. – Luis Casillas Nov 24 '15 at 22:38
  • @LuisCasillas: Ah! That's awesome. Thank you for the help. – philomathic_life Nov 24 '15 at 22:45
  • 1
    @basketballfan22, another option is to put `{-# OPTIONS_GHC -Wall #-}` in your `.hs` file somewhere above the `module` line. – dfeuer Nov 27 '15 at 18:39