F# cast / convert custom type to primitive
Asked Answered
D

3

6

I've designed my app domain with custom F# types, but now it seems like these custom types will be a PITA when I want to actually use the data for various tasks... i.e. writing values to a CSV file, using other libraries that rely on primitives, etc.

For example, I have a custom type like this (which is used as a building block for other larger types):

type CT32 = CT32 of float

But code like this doesn't work:

let x = CT32(1.2)
let y = float x //error: The type "CT32" does not support a conversion...
let z = x.ToString() //writes out the namespace, not the value (1.2)

I've tried to use box/unbox and Convert.ToString() and the two F# casting operators but none of them seem to allow me to access the base primitive value contained by my types. Is there an easy way to get to the primitive values in custom types because so far they've been more of a headache than actually useful.

Dragonnade answered 2/6, 2015 at 17:17 Comment(0)
B
2

Your type CT32 is a Discriminated union with one case identifier CT32 of float. It's not an alias to float type so you couldn't just cast it to float. To extract the value from it you can use pattern matching (this is the easiest way).

type CT32 = CT32 of float
let x = CT32(1.2)

let CT32toFloat = function CT32 x -> x
let y = CT32toFloat x
let z = y.ToString()

As an alternative (if your custom types are numeric) you can use Units of Measure https://msdn.microsoft.com/en-us/library/dd233243.aspx They don't have runtime overhead (I believe discriminated unions are compiled to classes) but provide type safety at compile time.

Barret answered 2/6, 2015 at 17:27 Comment(2)
Thanks Petr, you the man! I had followed an F# tutorial awhile ago on designing with types and didn't quite fully understand how to use these single-case DUs.Dragonnade
You can also implement op_Explicit member to use pattern matching for general float cast as @RCH proposed belowBarret
L
5

Adding support for float is simple:

type CT32 = CT32 of float with
    static member op_Explicit x =
        match x with CT32 f -> f

let x = CT32(1.2)
let y = float x
Lipstick answered 2/6, 2015 at 17:40 Comment(3)
now the question is, why isn't this on every single single member DU.Quin
It would kind of defeat the point (type safety) of having the single-member DU, wouldn't it?Beyrouth
@MarkPattison I don't think so, no. The cast is explicit and simply allows usage for e.g. writing to a file. In general I would recommend against op_Implicit though. Being able to do float x was an explicit wish of the OP.Lipstick
B
2

Your type CT32 is a Discriminated union with one case identifier CT32 of float. It's not an alias to float type so you couldn't just cast it to float. To extract the value from it you can use pattern matching (this is the easiest way).

type CT32 = CT32 of float
let x = CT32(1.2)

let CT32toFloat = function CT32 x -> x
let y = CT32toFloat x
let z = y.ToString()

As an alternative (if your custom types are numeric) you can use Units of Measure https://msdn.microsoft.com/en-us/library/dd233243.aspx They don't have runtime overhead (I believe discriminated unions are compiled to classes) but provide type safety at compile time.

Barret answered 2/6, 2015 at 17:27 Comment(2)
Thanks Petr, you the man! I had followed an F# tutorial awhile ago on designing with types and didn't quite fully understand how to use these single-case DUs.Dragonnade
You can also implement op_Explicit member to use pattern matching for general float cast as @RCH proposed belowBarret
F
1

You can also use inline deconstruction which doesn't require modifications on the type itself.

An example from F# for fun and profit:

type EmailAddress = EmailAddress of string
let email = EmailAddress "[email protected]"
let (EmailAddress deconstructed) = email // you can now access the email string value
Fumble answered 19/1, 2020 at 8:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.