I commonly have a "oh yeah" moment writing F# when I realize I need an extra value somewhere. This is generally easily done by adding another value to the tuple being passed around. However, this means that various maps/sorts/collects/etc. need updating, and in particular the functions fst/snd only work on tuples of length 2.
It's not a huge issue, but it's annoying enough during exploratory development that I though I'd write a helper to alleviate the annoyance:
let inline get2 (t:^a) = (^a : (member get_Item2 : unit -> string) (t, ()))
let inline get2a (t:^a) = (^a : (member Item2 : string) t)
However, both versions do not work. The first, get2
, won't compile, with "Expected 1 expressions, got 2". The second, get2a
, will compile, but subsequently can't be used on tuples: "The type '(int * string)' does not support any operators named 'get_Item2'".
Is there any way of doing this that doesn't involve lots of overloads? with noisy (annotations not required in F# 2.0)OverloadID
annotations
The reason why ItemX
static constraints on F# tuples do not work is because System.Tuple<_,...,_>
is only the encoded form of tuples, and not the static representation used by the compiler. See 6.3.2 Tuple Expressions in the specification.
However, with a little work, you can obtain the runtime encoding of a given tuple like so:
open System
//like get2a but generic return type
let inline get2b (t:^a) = (^a : (member Item2 : 'b) t)
let x = (1,2)
let y = (1,2,3)
get2b (box x :?> Tuple<int,int>)
get2b (box y :?> Tuple<int,int,int>)
You can do it using reflection:
let inline get (t:^a) = t.GetType().GetProperty("Item2").GetValue(t,null) :?> string
Also, I would suggest that tuples are not really a great data structure for passing data around, they may be useful inside a function for small operations, but in case there is a frequent change in the structure, tuple can be really painful to work with
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