3

Converting list to tuple using how-can-i-convert-between-f-list-and-f-tuple

Adding let a' = Array.map box a since MakeTuple expects obj array, and let t = unbox<float*float*float> o to get the tuple.

Example:

let a = List.toArray [1.0; 2.0; 3.0]
let types = a |> Array.map (fun o -> o.GetType())
let tupleType = Microsoft.FSharp.Reflection.FSharpType.MakeTupleType types
let a' = Array.map box a
let o = Reflection.FSharpValue.MakeTuple (a' , tupleType)
let t = unbox<float*float*float> o

This runs for float list with length 3. Returns val t : float * float * float = (1.0, 2.0, 3.0) as desired.

But I want to convert lists of arbitrary length.

Converting from list to obj works for arbitrary length (and type), my problem is the unboxing.

holmen
  • 75
  • 5

1 Answers1

3

You motivate the question using a somewhat arbitrary example. I assume that your actual motivation for wanting this is something more tricky and it would be good to know what that actual motivation is - because it might be useful in giving a good answer.

First, if you want to work with a tuples of arbitrary length, you should probably not be using tuples. F# tuples are fixed (statically known) length. Other uses of them will be cumbersome. (Unlike in some dynamic languages where tuples are used for arbitrary-lenght data more frequently.) Second, if you do not know the type at compile-time, I'm not sure what you want to achieve via unboxing.

Depedning on your actual motivation, there is one trick that may be helpful. The trick is to invoke a generic method via reflection, passing it the unboxed values.

If we assume that all elements of your tuple have the same type, you can take them as an array (of unboxed, but generic values):

type Helper = 
  static member Process<'T>(data:'T[]) = 
    typeof<'T>.Name

Now you can invoke Helper.Process using reflection:

open Microsoft.FSharp.Reflection

let a = (1., 2.)

let tys = FSharpType.GetTupleElements(a.GetType())
for ty in tys do if ty <> tys.[0] then failwith "All types must be the same"

let flds = FSharpValue.GetTupleFields(a)
let arr = System.Array.CreateInstance(tys.[0], flds.Length)
System.Array.Copy(flds, arr, flds.Length)
typeof<Helper>.GetMethod("Process")
  .MakeGenericMethod([| tys.[0] |]).Invoke(null, [| arr |] )

This returns "Double", because when called via reflection, the type of 'T will be Double, so you are actually getting the values as unboxed.

There are some rare cases where a thing like this is useful, but my guess is that you do not actually need this and, instead, you'd be probably better off with a different way of representing your data.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • 1
    The actual motivation is an optimization algorithm (Nelder-Mead [link](http://www.brnt.eu/phd/node10.html#SECTION00622200000000000000)) which i use to learn F#. I've used _float list_ as _Vertex_ type, then _tuple_ when evaluating objective function, see [link](https://gist.github.com/holmen1/54a6e9dfaf25178e9e343a5b9fdb1586). I can now skip tuple step and make objective a function of _Vertex_, making adaption in objective function. Objective functions will differ, but I will know at compile-time. – holmen Jul 31 '20 at 04:13
  • I've tried using modification of Triple [link](http://fssnip.net/X/title/Triple-3D-Vector). It became complicated, so I returned to using float list [link](https://gist.github.com/holmen1/a2f55d8f73a20b9f51cb705fdcb40e88). So far only 2D, but will later be used for 6D and 20D vertices. Will float list be the best choice? @TomasPetricek – holmen Sep 02 '20 at 10:09