2

I am wanting to format a tuple in a specific way and I am trying to do that by checking the type of the tuple (2 element, 3 element etc.). I am getting an error on the third line saying:

This runtime coercion of type test from type
'd
to
  'a * ('b * 'c)
involves an indeterminate type based on the information prior to this program point. 
Runtime type tests are not allowed on some type. Further type annotations are needed.

Here is my attempt:

  let namer x =
    match x with
    | :? ('a * ('b * 'c)) as a, b, c -> sprintf "%s_%s_%s" (a.ToString()) (b.ToString()) (c.ToString())
    | :? ('a * 'b) as a, b -> sprintf "%s_%s" (a.ToString()) (b.ToString())
    | a -> sprintf "%s" (a.ToString())

How should you do something like this? I want to be able to format the string based on the type of tuple.

What I am ultimately wanting is to be able to "flatten" a nested tuple to a string without a bunch of parenthesis. For example:

// What I want
let x = (1, (2, (3, 4)))
let name = namer x
printfn "%s" name
> 1_2_3_4

Update: This is different than the question "How can i convert between F# List and F# Tuple?" found here. I know how to do that. What I am wanting is to be able to detect if I have a tuple and what type of tuple. The ideal is a generic function that could take a single element, a tuple, or nested 2 element tuples. For example, legal arguments would be:

let name = namer 1
// or
let name = namer (1, 2)
// or 
let name = namer (1, (2, 3))
// or
let name = namer (1, (2, (3, 4)))

I also want to handle non-integer values. For example:

let name = namer (1, ("2", (3, "chicken")))
Matthew Crews
  • 4,105
  • 7
  • 33
  • 57
  • Why are you modelling as nested tuples? It’s not really that common in F# – 7sharp9 Mar 03 '20 at 17:18
  • @7sharp9 agreed that it is not common. In this case I have a Computation Expression where I have nested for...in...do loops where I want to keep track of the iterator value and use that for an id when I call `Run` at the end. I'll have a blogpost where I detail it out later. – Matthew Crews Mar 03 '20 at 17:22

1 Answers1

5

You can achieve this with a bit of reflection and a co-recursive function:

let isTuple tuple =
    tuple.GetType() |> Reflection.FSharpType.IsTuple 

let getFields (tuple: obj) = 
    tuple |> Reflection.FSharpValue.GetTupleFields |> Array.toList

let rec flatten fields =
    List.collect namer fields

and namer (tuple: obj) = 
    if isTuple tuple
    then tuple |> getFields |> flatten
    else [tuple]

namer(1, "test") |> printfn "%A"
namer(1, ("2", (3, "chicken"))) |> printfn "%A"

Try it online!

Inspired by:

aloisdg
  • 22,270
  • 6
  • 85
  • 105