F #: How to provide fst for tuples, triples, and fours without sacrificing runtime performance

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 the answer ( http://msdn.microsoft.com/en-us/library/dd233200.aspx ):

let third (_, _, c) = c

Still easy. But it’s actually misleading, because we cannot use fst and snd in 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 tricky question: How can we provide the fst function for tuples, triples, and fours without sacrificing performance at runtime?

Approaches that do not work:

A) just adding fst (, _)

let fst (a,_,_) = a // hides fst (a,b), so tuples dont work anymore

B) , , jitted-.

C) ( , , - )

type Tuple<'T1, 'T2, 'T3> with
    member o.fst(a,b,c) = a
    static member fst(a,b,c) = a

(t3.fst()), Tuples, ().

D)

let fst = function 
          | (a,b) -> a
          | (a,b,c) -> a // FS0001: Type mismatch

, . , , , .

+4
3

, , .

, @MauricioScheffer , , .

fst, :

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
+9

, ( ) , , , , , F #, .

, , , , , ( , F #).

F #, ( ), . :

// 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)

, , . , ( ), - . "", , ( ).

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

, . , , , .

+5

. , , F # , Tuple <... > . , , Tuple<T1, T2> Tuple<T1, T2, T3>? : , . .

So, you can use the library above, or you can try to flip your own, which includes relationships. Probably the best way to do this, but this is straightforward:

type XTuple1<'a>(a: 'a) =
    member public this.fst = a
    abstract member strContents: unit->string
    default this.strContents() = this.fst.ToString()
    override this.ToString() = "(" + this.strContents() + ")"
type XTuple2<'a, 'b>(a:'a, b:'b) =
    inherit XTuple1<'a>(a)
    member public this.snd = b
    override this.strContents() = base.strContents() + ", " + b.ToString()
type XTuple3<'a, 'b, 'c>(a:'a, b:'b, c:'c) =
    inherit XTuple2<'a, 'b>(a, b)
    member public this.thrd = c
    override this.strContents() = base.strContents() + ", " + c.ToString()

let inline xfst<'a> (xt:XTuple1<'a>) = xt.fst
let inline xsnd<'a,'b> (xt:XTuple2<'a,'b>) = xt.snd
let inline xthrd<'a,'b,'c> (xt:XTuple3<'a,'b,'c>) = xt.thrd

[<EntryPoint>]
let main argv = 
    let t3 = XTuple3(1, 'a', "word")
    let a = xfst t3 // strongly typed: a is an int
    let b = xsnd t3
    let c = xthrd t3
    printfn "%s" (t3.ToString()) // -> "(1, a, word)"
    0 // return an integer exit code
+1
source

All Articles