For basic Tuples with 2 elements we have fst and snd:
let t2 = (2, 3)
fst t2 // = 2
snd t2 // = 3
Easy. Now with 3 elements
let t3 = (2, 3, 4)
how do we access the 3rd element? msdn has an answer (http://msdn.microsoft.com/en-us/library/dd233200.aspx):
let third (_, _, c) = c
Still easy. But actually deceptive, because we cannot use fst and snd on such triples:
fst t3
error FS0001: Type mismatch. Expecting a int * int but given a int * int * int
The tuples have differing lengths of 2 and 3
Hence the challenging question: How can we provide function fst for tuples, triples and quadruples without sacrifying runtime performance?
Approaches that do not work:
A) simply adding fst(,,_)
let fst (a,_,_) = a // hides fst (a,b), so tuples dont work anymore
B) Any Reflection tricks, since performance is affected or tricky function composition that leads to additional calls at the jitted code level.
C) extension methods (did not lead me anywhere, maybe someone else has better ideas)
type Tuple<'T1, 'T2, 'T3> with
member o.fst(a,b,c) = a
static member fst(a,b,c) = a
besides having other signature (t3.fst()) I could not even get extensions to work with Tuples, although they are classes (not structs).
D)
let fst = function
| (a,b) -> a
| (a,b,c) -> a // FS0001: Type mismatch
The question is meant as it is formulated, not for workarounds. Actually I believe the answer is that it is not possible to provide common functions that operate on Tuples.
Using this technique of method overload combined with an inline function has no penalty at all at runtime, since the overload resolution occurs at compile time and the overloaded function is inlined at the call site.
There are more complex ways to organise these kind of generic tuple functions in libraries, @MauricioScheffer already posted some links showing more experiments making use of basically the same technique.
Here's a very short standalone code fragment to overload the function fst
to make it work with other tuple sizes:
type Fst = Fst with
static member ($) (Fst, (x1,_)) = x1
static member ($) (Fst, (x1,_,_)) = x1
static member ($) (Fst, (x1,_,_,_)) = x1
// more overloads
let inline fst x = Fst $ x
As others already mentioned, there are (more or less elaborate and ugly) ways to do this, but I think the important thing that should be said here is that, if you're using F# well, you don't need this.
If you need a data structure that can have two, three or four elements, then you should probably use a list instead of using tuples (in other languages, tuples are sometimes used for this, but not in F#).
If you're working with tuples in F#, then you'll be typically working with a small number of elements (two or three) and then it is more readable to decompose them using pattern matching. Compare the following:
// Pattern matching makes you name things, so this is quite clear
// (we don't need the third element, so we're ignoring it)
let width, height, _ = tuple
width * height
// If you had fst3 and snd3, then you could write it using just
// a single line, but the code becomes hard to follow
(fst3 tuple) * (snd3 tuple)
So, I think you simply shouldn't be doing this. Now, if you were using more complex data structures (with larger number of elements), then it's better to use something like a record. If your first element is always a "name" and you want to have a function for accessing the name, you can use interfaces (or discriminated unions and pattern matching).
type INamed =
abstract Name : string
type Person =
{ SomeName : string }
interface INamed with
member this.Name = this.SomeName
type FancyPerson =
{ SomeName : string; Title : string }
interface INamed with
member this.Name = this.SomeName
The right approach depends on what you're doing. But in any case, there is most likely a better way of expressing what you need.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With