Converting between types in discriminatory unions

I have a function that can return different types, and I use a discriminatory union for this. What I need is a conversion from one type into a discriminatory union into another type. In addition, some types can be converted to all other types (String), but some of them can only be converted to String (MyCustomType)

To do this, I added the member ConvertTo method to ResultType :

 type MyTypes = | Boolean = 1 | Integer = 2 | Decimal = 3 | Double = 4 | String = 5 | MyCustomType = 6 type ResultType = | Boolean of bool | Integer of int | Decimal of decimal | Double of double | String of string | MyCustomType of MyCustomType with member this.ConvertTo(newType: MyTypes) = match this with | ResultType.Boolean(value) -> match newType with | MyTypes.Boolean -> this | MyTypes.Integer -> ResultType.Integer(if value then 1 else 0) ... | ResultType.MyCustomType(value) -> match newType with | MyTypes.MyCustomType -> this | MyTypes.String -> ResultType.String(value.ToString()) | _ -> failwithf "Conversion from MyCustomType to %s is not supported" (newType.ToString()) 

I don’t like this design, because if I add more types, it requires me to make a lot of changes: MyTypes, ResultType, as well as in several places in the ConvertTo member function.

Can anyone suggest a better solution for this type conversion?

Thank you in advance

+4
source share
2 answers

With a slightly different design, you can use System.Convert.ChangeType and the fact that discriminated union constructors are actually functions:

 // statically typed wrapper for System.Convert.ChangeType let conv a : 'T = System.Convert.ChangeType(a, typeof<'T>) :?> 'T type MyCustomType() = class end type ResultType = | Boolean of bool | Integer of int | Decimal of decimal | Double of double | String of string | MyCustomType of MyCustomType with member this.ConvertTo (newType:'T->ResultType) = match this with | Boolean b -> newType( conv b ) | Integer i -> newType( conv i ) | Decimal d -> newType( conv d ) | Double d -> newType( conv d ) | String s -> newType( conv s ) | MyCustomType m -> if typeof<'T> <> typeof<string> then raise (new System.InvalidCastException("MyCustomType can only be converted to String")) else String (m.ToString()) let i = Integer 42 let b = i.ConvertTo Boolean printfn "%A" b let d = i.ConvertTo Decimal printfn "%A" d let d2 = i.ConvertTo Double printfn "%A" d2 let s = i.ConvertTo String printfn "%A" s //let mi = i.ConvertTo MyCustomType // throws InvalidCastException let m = MyCustomType (new MyCustomType()) let sm = m.ConvertTo String printfn "%A" sm //let im = m.ConvertTo Integer // throws InvalidCastException 

EDIT: after adding additional custom types this will not help.

Perhaps you should make your own IConvertible types. You can then remove the special enclosure code from ConvertTo and rely entirely on System.Convert.ChangeType .

When you add a new custom type, you still have to extend the implementation of each custom ToObject type. No matter what is actually better than the central ConvertTo function is debatable.

+8
source

Why do you need to start type conversion? Discriminatory unions are a good way to hide type information until you need it, and abstract complexity. Typically, you have a match operator in a function that uses this type, and then you only do what you need.

If you are trying to create some type of parser or language, then you have no choice but to determine all cast or at least their error states. If you do not mind why / why you use it, perhaps I could suggest a different approach.

Aside: F # and .NET do not support overloading of return types at all.

+2
source

Source: https://habr.com/ru/post/1343765/


All Articles