如何在不使用 upcast 的情况下编写 (string*obj) 列表
How to write a list of (string*obj) without using upcast
我的目标是在 DSL 中编写 name/value 对的列表并使其可读。此处的值可以是 int
、float
、string
或任何这些类型的列表。
我正在使用 string * obj
对并将它们传递给接受 (string * obj) list
.
的函数
有没有不upcast obj
参数就写列表的?
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]
编程 101:如果您发现自己一遍又一遍地重复同样的事情,请将其打包以供重用,将其作为一个函数。在您的情况下,该函数将是通用的(即采用任何类型的参数)并对参数进行向上转换:
let pair name value = name, value :> obj
myfun [pair "Name" "Freddie"; pair "Age" 50]
嗯...不是更好,是吗?但是等等,我们还没有完成!现在你有了这个函数,你可以给它起一个更好听的名字,这样调用它会更好听。说,==>
:
let (==>) name value = name, value :> obj
myfun ["Name" ==> "Freddie"; "Age" ==> 50]
如果您事先知道可能的类型集并且相对较小(正如您的问题似乎表明的那样),您可以更进一步,让编译器检查是否只使用了允许的类型。为此,您需要使用方法重载、静态解析类型约束和一些语法技巧:
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
你说你的值只能有某些列出的类型。我想知道您是否有特殊理由使用 obj
而不是完全适合此任务的可区分联合?
我修改了 Fyodor 的答案以使用 DU 类型而不是 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
这种方法的优点是,当您访问值时,您现在有 type-checked 模式匹配,并且您不必对 obj
:[=15= 进行不安全的向下转换]
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
我的目标是在 DSL 中编写 name/value 对的列表并使其可读。此处的值可以是 int
、float
、string
或任何这些类型的列表。
我正在使用 string * obj
对并将它们传递给接受 (string * obj) list
.
有没有不upcast obj
参数就写列表的?
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]
编程 101:如果您发现自己一遍又一遍地重复同样的事情,请将其打包以供重用,将其作为一个函数。在您的情况下,该函数将是通用的(即采用任何类型的参数)并对参数进行向上转换:
let pair name value = name, value :> obj
myfun [pair "Name" "Freddie"; pair "Age" 50]
嗯...不是更好,是吗?但是等等,我们还没有完成!现在你有了这个函数,你可以给它起一个更好听的名字,这样调用它会更好听。说,==>
:
let (==>) name value = name, value :> obj
myfun ["Name" ==> "Freddie"; "Age" ==> 50]
如果您事先知道可能的类型集并且相对较小(正如您的问题似乎表明的那样),您可以更进一步,让编译器检查是否只使用了允许的类型。为此,您需要使用方法重载、静态解析类型约束和一些语法技巧:
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
你说你的值只能有某些列出的类型。我想知道您是否有特殊理由使用 obj
而不是完全适合此任务的可区分联合?
我修改了 Fyodor 的答案以使用 DU 类型而不是 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
这种方法的优点是,当您访问值时,您现在有 type-checked 模式匹配,并且您不必对 obj
:[=15= 进行不安全的向下转换]
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