Partager via


Adventures in F#--Poking Tuples with a Stick

Jomo Fisher--In a comment on my bloglet about type inference in F# John Harrop suggested I try:

let f (a,b)=(b,a)

Which, under Reflector, gives this equivalent in C#:

public static Tuple<U, T> f<T, U>(T a, U b)

{

    return new Tuple<U, T>(b, a);

}

Thanks for the prod John, Tuples were on my (growing) list of F# features to dig into. The Tuple class is a grouping of two or more values under a single type. Tuple is actually a family of types defined in fslib.dll. There's a Tuple with two properties--Tuple<A,B>--a Tuple with three properties--Tuple<A,B,C>--and so on. The widest Tuple class has seven properties.

This limitation in Tuple width invited me to try returning a wider Tuple:

let f = (1,2,3,4,5,6,7,8)

The result is interesting to me:

[CompilationMapping(SourceLevelConstruct.Value)]

public static Tuple<int, int, int, int, int, int, Tuple<int, int>> f

{

   get

   {

       return Test.f@2;

   }

 

The extra value is handled by nesting Tuples. The compiler must then apply a little additional syntactic sugar to make this nested Tuple appear flat.

In turn, this made me wonder what would happen with a tuple that is fourteen wide. Before I try the experiment, it seem like there are two possible representations--either the fourteenth value is paired inside a third nesting level or a second Tuple is added in the sixth position of the outer Tuple for a nesting level of two.

[CompilationMapping(SourceLevelConstruct.Value)]

public static Tuple<int, int, int, int, int, int, Tuple<int, int, int, int, int, int, Tuple<int, int>>> f

{

    get

    {

        return Test.f@2;

    }

}

We get the three level result. This means the nesting of Tuples grows linearly the number of properties in it. I also believe this means the left-most properties in the Tuple will be more quickly accessed than the right-most in wide Tuples because of the added levels of indirection. I'll verify this experimentally before I try to take advantage of it anywhere.

I'm going to stop there for now, but there's a lot more to investigate. Here's what the Tuple class looks like:

[Serializable, DebuggerDisplay("({Item1},{Item2})"), CompilationMapping(SourceLevelConstruct.RecordType)]

public sealed class Tuple<A, B> : IStructuralHash, IComparable

{

    // Fields

    public A _Item1;

    public B _Item2;

    // Methods

    public Tuple(A _Item1, B _Item2);

    public sealed override int CompareTo(Tuple<A, B> that);

    public override int CompareTo(object that);

    public override bool Equals(object that);

    public override int GetHashCode();

    public sealed override int GetStructuralHashCode(ref int nRemainingNodes);

    // Properties

    [CompilationMapping(SourceLevelConstruct.Field, 0)]

    public A Item1 { get; }

    [CompilationMapping(SourceLevelConstruct.Field, 1)]

    public B Item2 { get; }

My questions: What's IStructuralHash? What are SourceLevelConstruct.RecordType and SourceLevelConstruct.Value? How are the Compare functions implemented? How does the compiler know the difference between (1,2,3,4,5,6,7,8) and (1,2,3,4,5,6,(7,8)) between assemblies?

Finally, if you've come to my blog to try to learn a bit more about F#, may I also suggest John Harrop's page? There's an F# introduction, some free F# tutorials and a link to a for-pay F# Journal. I'm not affiliated with John and I don't subscribe to the journal but it does look interesting to me.

This posting is provided "AS IS" with no warranties, and confers no rights.

Comments