How to write a list (string * obj) without using upcast

My goal is to write a list of name / value pairs in DSL and make it readable. The values ​​here may be int , float , string or a list of any of these types.

I use string * obj pairs and pass them to a function that accepts (string * obj) list .

Do I need to write a list without raising the value of the obj parameter?

 let myfun (values:(string*obj) list) = // Do something... // This is pretty ugly myfun ["Name", upcast "Freddie"; "Age", upcast 50] // This would be the ideal myfun ["Name", "Freddie"; "Age", 50] 
+5
source share
2 answers

Programming 101: if you repeat the same thing over and over, pack it for reuse, make it a function. In your case, the function will be general (i.e., it will take a parameter of any type) and make upcast by parameter:

 let pair name value = name, value :> obj myfun [pair "Name" "Freddie"; pair "Age" 50] 

Hmm ... Not much nicer, is it? But wait, we're not done yet! Now that you have this feature, you can give it a nicer name that will make it nicer. Let's say ==> :

 let (==>) name value = name, value :> obj myfun ["Name" ==> "Freddie"; "Age" ==> 50] 

If your set of possible types is known in advance and relatively small (as your question indicates), you can take one more step and check the compiler that only allowed types are used. To do this, you need to use method overloads, static type constraints, and some syntax tricks:

 type Casters() = static member cast (v: string) = v :> obj static member cast (v: float) = v :> obj static member cast (v: int) = v :> obj static member cast (v: string list) = v :> obj static member cast (v: float list) = v :> obj static member cast (v: int list) = v :> obj let inline cast (casters: ^c) (value: ^t) = ( (^c or ^t) : (static member cast : ^t -> obj) value) let inline (==>) name value = name, (cast (Casters()) value) ["Name" ==> "Freddie"; "Age" ==> 50] // OK ["What?" ==> true] // Error: "bool" is not an allowed type 
+8
source

Say that your values ​​can only have certain types listed. I wonder if you have a specific reason to use obj instead of discriminatory union, which is great for this task?

I modified Fyodor's answer to use DU type instead of obj :

 type Value = | Int of int | Float of float | String of string | IntList of int list | FloatList of float list | StringList of string list type Casters() = static member cast v = Int v static member cast v = Float v static member cast v = String v static member cast v = IntList v static member cast v = FloatList v static member cast v = StringList v let inline cast (casters: ^c) (value: ^t) = ( (^c or ^t) : (static member cast : ^t -> Value) value) let inline (==>) name value = name, (cast (Casters()) value) ["Name" ==> "Freddie"; "Age" ==> 50] // OK ["What?" ==> true] // Error: "bool" is not an allowed type 

The advantage of this approach is that you now have pattern matching with the type when you access the values ​​and you don't need to perform unsafe obj suppression:

 let myfun (values:(string*Value) list) = values |> List.map (fun (k, v) -> match v with | Int v -> k + ":" + string v | String v -> k + ":" + v.Trim() ) // etc. |> String.concat "\n" myfun ["Name" ==> "Freddie"; "Age" ==> 50] |> printfn "%s" //Name:Freddie //Age:50 
+2
source

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


All Articles