如何在不使用upcast的情况下编写(string * obj)列表

时间:2017-02-04 17:41:47

标签: f# dsl

我的目标是在DSL中编写名称/值对列表并使其可读。此处的值可以是intfloatstring或任何类型的列表。

我正在使用string * obj对,并将它们传递给一个(string * obj) list的函数。

是否要编写列表而不转发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]

2 个答案:

答案 0 :(得分:8)

编程101:如果你发现自己一遍又一遍地重复同样的事情,将其打包以便重复使用,使其成为一种功能。在您的情况下,该函数将是通用的(即获取任何类型的参数)并对参数执行upcast:

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

答案 1 :(得分:2)

您说您的值只能包含某些列出的类型。我想知道你是否有特别的理由使用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

这种方法的优点是,您现在可以在访问值时进行类型检查模式匹配,并且您不必对obj进行不安全的向下转换:

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