13

Reading about F# today and I'm not clear on one thing:

From: http://msdn.microsoft.com/en-us/library/dd233200.aspx

you need only one element of the tuple, the wildcard character (the underscore) can be used to avoid creating a new name for a variable that you do not need

let (a, _) = (1, 2)

I can't think of a time that I've been in this situation. Why would you avoid creating a variable name?

Community
  • 1
  • 1
jcollum
  • 43,623
  • 55
  • 191
  • 321

4 Answers4

16

Because you don't need the value. I use this often. It documents the fact that a value is unused and saves naming variables unused, dummy, etc. Great feature if you ask me.

Daniel
  • 47,404
  • 11
  • 101
  • 179
  • Yeah I gathered that, but most other languages you'd just get the 1st value out of the tuple, in F# you skip the second. If I want the 8th value, it'll get really ugly. – jcollum Dec 31 '12 at 20:58
  • 11
    By the time you have 8 values in your tuple, you're probably past the point where you should consider a different data structure. – Joel Mueller Dec 31 '12 at 21:01
  • @JoelMueller ah yes that's probably what I'm missing, finding a different data structure – jcollum Dec 31 '12 at 21:33
  • 3
    @jcollum - If it makes sense to access the 8th element by index, then probably everything is the same data type, and an array or a list is better than a tuple. Conversely, if we have 8 or more elements of different data types, accessing them by index is just confusing, so in F# it would be preferred to use a record, so your values-of-disparate-types can be accessed by name. – Joel Mueller Dec 31 '12 at 21:51
  • @JoelMueller cool, that makes a lot more sense thanks for the assist – jcollum Dec 31 '12 at 22:00
11

Interesting question. There are many trade-offs involved here.

Your comparisons have been with the Ruby programming language so perhaps the first trade-off you should consider is static typing. If you use the pattern x, _, _ then F# knows you are referring to the first element of a triple of exactly three elements and will enforce this constraint at compile time. Ruby cannot. F# also checks patterns for exhaustiveness and redundancy. Again, Ruby cannot.

Your comparisons have also used only flat patterns. Consider the patterns _, (x, _) or x, None | _, Some x or [] | [_] and so on. These are not so easily translated.

Finally, I'd mention that Standard ML is a programming language related to F# and it does provide operators called #1 etc. to extract the first element of a tuple with an arbitrary number of elements (see here) so this idea was implemented and discarded decades ago. I believe this is because SML's #n notation culminates in incomprehensible error messages within the constraints of the type system. For example, a function that uses #n is not making it clear what the arity of the tuple is but functions cannot be generic over tuple arity so this must result in an error message saying that you must give more type information but many users found that confusing. With the CAML/OCaml/F# approach there is no such confusion.

J D
  • 48,105
  • 13
  • 171
  • 274
  • OT, but does SML's type system let you write general functions (along the lines of #n) over n-ary tules? Or is this a special language feature? – Chris Barrett Jan 04 '13 at 02:10
  • @ChrisBarrett Functions cannot be generic over tuple arity. – J D Jan 04 '13 at 12:35
  • Thanks for clearing that up. I read your last paragraph to mean the #n functions were generalized over arity in SML, which struck me as totally weird. – Chris Barrett Jan 04 '13 at 13:04
  • I believe they are monomorphic so they begin life with the potential to be applied to any arity of tuple but as soon as they are first used on a tuple with known arity they can only ever be used on tuples with that arity ever again. – J D Jan 21 '20 at 15:57
10

The let-binding you've given is an example of a language facility called pattern matching, which can be used to destructure many types, not just tuples. In pattern matches, underscores are the idiomatic way to express that you won't refer to a value.

Directly accessing the elements of a tuple can be more concise, but it's less general. Pattern matching allows you to look at the structure of some data and dispatch to an approprate handling case.

match x with
| (x, _, 20) -> x
| (_, y, _)  -> y

This pattern match will return the first item in x only if the third element is 20. Otherwise it returns the second element. Once you get beyond trivial cases, the underscores are an important readability aid. Compare the above with:

match x with
| (x, y, 20) -> x
| (x, y, z)  -> y

In the first code sample, it's much easier to tell which bindings you care about in the pattern.

Chris Barrett
  • 3,383
  • 17
  • 21
  • I guess I'm just criticizing the language, but why not just say `x,y = arr[0], arr[1]` if you want to the 1st and 2nd element only? I've been writing Ruby for the past couple months, F# looks like it's going out of its way to say "don't care about this value". In Ruby if you didn't care about the value you'd just never look at it. F# is saying "I know there's something there, but I don't care about it". – jcollum Dec 31 '12 at 20:23
  • Sure, but again, that's a very trivial example of pattern matching. It's still idiomatic to document which elements in a pattern you don't care about binding to. – Chris Barrett Dec 31 '12 at 20:35
  • _ is an idiom for pattern matching in many functional languages (Erlang, OCaml, of course F# to name a few). – cfeduke Dec 31 '12 at 20:35
  • 1
    In your original tuple example, you could also have gone for `let x = fst tpl` and got the same result. But things get much more useful once you get away from tuples. For functions that need to switch behaviour depending on their input, you can use pattern matches rather than if/then/else. For example, `let optTail = function (x::xs) -> Some xs | _ -> None` will return `None` if the given list doesn't have a tail. – Chris Barrett Dec 31 '12 at 20:51
  • I'd also like to point out, in Ruby: `h, _, _, _, l = [1,2,3,4,5]` is valid as well. Not that there's pattern matching to go along with it, its just a convenient shorthand. – cfeduke Dec 31 '12 at 21:17
2

Sometimes a method will return multiple values but the code you're writing is only interested in a select few (or one) of them. You can use multiple underscores to essentially ignore the values you don't need, rather than having a bunch of variables hanging around in local scope.

cfeduke
  • 23,100
  • 10
  • 61
  • 65
  • Wouldn't it be more readable to say let a = f.[3]? As in, I only care about the 4th value? New to the language, so that might not be valid code but I think you see what I mean. – jcollum Dec 31 '12 at 19:42
  • Compared to `let _, _, a = f()`? - I think I prefer the expanded version. With pattern matching there's a lot of expressiveness in this syntax. – cfeduke Dec 31 '12 at 20:38
  • 12 characters is more expressive than 7? Uhh, not to me. – jcollum Dec 31 '12 at 20:50
  • 1
    Using your suggested syntax you could never write expressive pattern matchers. You'd end up with a bunch of if-statements cascading through in an attempt to match patterns and guard against values. – cfeduke Dec 31 '12 at 21:09
  • 2
    @jcollum - This is an odd dispute, considering that `let a = f.[3]` is not valid syntax for tuples. I think the thing you might be missing when compared to Ruby is that F# is statically-typed. The reason that tuples exist as distinct from arrays is that arrays can only contain a single type (unless you box everything to object). Tuples exist as a way to store multiple un-named but strongly-typed values of different types, something that is not compatible with arrays or the array-indexing syntax. – Joel Mueller Dec 31 '12 at 21:10
  • @JoelMueller right, it's not valid and I pointed that out. It just looks like accessing the nth element of a tuple is clumsy in F#. – jcollum Dec 31 '12 at 21:31
  • @jcollum - Yes it is, but I consider that a feature, because it encourages you to choose a more-appropriate data type. Tuple syntax is convenient for a handful of values, but a bit of a pain as the number of elements goes up - which is exactly when you should start reconsidering your use of tuples in that context. – Joel Mueller Dec 31 '12 at 21:57