我有
形式的辨别联合类型type ParameterName = string
type ParameterValues =
| String of string[]
| Float of float[]
| Int of int[]
type Parameter = Parameter of ParameterName * ParameterValues
我想将ParameterValues部分传递给一个带泛型参数返回单元的函数,例如
let func1 (name:string) (data:'a) = printfn "%s" name
解构Parameter
我可以像这样包裹func1
let func2 (Parameter (name, values)) =
match values with
| String s -> func1 name s
| Float s -> func1 name s
| Int s -> func1 name s
但是如果我必须为多个功能执行此操作,这将是不方便的。相反,我想定义一个更灵活的包装器,如下所示:
let func3 (fn: ('a -> 'b -> unit)) (Parameter (name, values)) =
match values with
| String s -> fn name s
| Float s -> fn name s
| Int s -> fn name s
然而这失败了,因为b
的类型在匹配表达式的第一个选项中被限制为string[]
;因此匹配表达式失败,错误为Type string does not match type float.
这是预期的吗?我该如何解决这个问题?
答案 0 :(得分:4)
这是预期的行为。问题是你不能直接将泛型函数作为参数传递给F#中的另一个函数。定义函数时如下:
let func3 (fn: ('a -> 'b -> unit)) (Parameter (name, values)) = (...)
...您正在定义一个泛型函数func3
,它有两个通用参数,当指定它们时,可以使用给定的函数和参数调用它们。这可以写成:
\forall 'a, 'b . (('a -> 'b -> unit) -> Parameter -> unit)
您需要做的是使这些类型参数不是顶级参数,而是使第一个参数本身成为通用函数。你可以写成:
(\forall 'a, 'b . ('a -> 'b -> unit)) -> Parameter -> unit
这可以使用接口笨拙地用F#编写:
type IFunction<'a> =
abstract Invoke<'b> : 'a -> 'b -> unit
let func1 =
{ new IFunction<string> with
member x.Invoke<'b> name (data:'b) = printfn "%s" name }
let func3 (fn: IFunction<string>) (Parameter (name, values)) =
match values with
| String s -> fn.Invoke name s
| Float s -> fn.Invoke name s
| Int s -> fn.Invoke name s
实际上,你的函数并不需要是通用的,因为你永远不会使用第二个参数 - 但是你可以通过将数据作为{{1你的代码比这个怪物要简单得多!
答案 1 :(得分:2)
根据Lee和Tomas的建议,我提出了以下解决方案:
type Parameter = Parameter of string * obj
let func0 (name:string) (data:obj) = printfn "%s %A" name data
let func1 (fn: string->obj->unit) (Parameter (name, value)) =
fn name value
let p1 = Parameter ("p1", [|"a"; "b"|])
let p2 = Parameter ("p1", [|1.; 2.|])
func1 func0 p1
func1 func0 p2