// This does not work - no automatic conversion to 'obj'
let objs1 = [1; "hi"]
// ...but it works if compiler knows the type in advance
let objs2 : obj list = [1; "hi"]
// But, that only works for simple types - so even if we
// know the type in advance, it will not work for tuples :-(
let objs2 : (string*obj) list = [("hi", 1)]
// We could define our own tuple type to make this possible
// with a less generic base class - here, I just make base
// class generic in key, but not in value
type Tuple<'K> =
abstract Key : 'K
abstract Value : obj
// And then we have fully generic tuple that implements the
// less-generic variant (and boxes the value)
type Tuple<'K, 'V> =
| KV of 'K * 'V
member x.Key = let (KV(k, v)) = x in k
member x.Value = let (KV(k, v)) = x in v
interface Tuple<'K> with
member x.Key = let (KV(k, v)) = x in k
member x.Value = let (KV(k, v)) = x in box v
// Builds the more-generic version of the tuple
let (=>) k v = KV(k, v)
// This does not work, because we are creating list that
// contains 'Tuple' and 'Tuple'
let pars = ["x" => 1; "y" => "42"]
// But we can make it work if we know the type in advance
let namedPars (p:list>) = p
let pars = namedPars ["x" => 1; "y" => "42"]
// Note that this will NOT work
["x" => 1; "y" => "42"] |> namedPars
// It plays nicely with ParamArray too
type R() =
static member plot(s:string, [] pars:Tuple[]) =
printfn "%s - %A" s pars
// This gives us what Howard suggested!
R.plot("sin", "x" => 1, "y" => "42")
// But we can still write functions that require Tuple
// rather than tuple with any type of values
let fromNums (nums:list>) = nums
// Fine
fromNums ["a" => 1; "b" => 3]
// Not allowed
fromNums ["a" => 1; "b" => 'a']