F# F中的动态函数#

F# F中的动态函数#,f#,F#,我试图探索F#的动态功能,以应对无法用静态类型系统表达某些功能的情况。因此,我试图为(比如)选项类型创建一个mapN函数,但创建一个参数数量动态的函数时遇到了问题。我试过: let mapN<'output> (f : obj) args = let rec mapN' (state:obj) (args' : (obj option) list) = match args' with | Some x :: xs -> mapN' ((state :?&

我试图探索F#的动态功能,以应对无法用静态类型系统表达某些功能的情况。因此,我试图为(比如)选项类型创建一个
mapN
函数,但创建一个参数数量动态的函数时遇到了问题。我试过:

let mapN<'output> (f : obj) args =
  let rec mapN' (state:obj) (args' : (obj option) list) = 
    match args' with
    | Some x :: xs -> mapN' ((state :?> obj -> obj) x) xs
    | None _ :: _ -> None
    | [] -> state :?> 'output option

  mapN' f args

let toObjOption (x : #obj option) = 
  Option.map (fun x -> x :> obj) x

let a = Some 5
let b = Some "hi"
let c = Some true

let ans = mapN<string> (fun x y z -> sprintf "%i %s %A" x y z) [a |> toObjOption; b |> toObjOption; c |> toObjOption]
我意识到为选项创建一个计算表达式,或者通过
map5
定义
map2
等等,这将更为惯用,但我特别想探索F#的动态功能,看看这样的事情是否可能


这仅仅是一个在F#中无法实现的概念,还是我缺少了一种方法?

我想你只能通过反思来采用这种方法

但是,还有其他方法可以解决整个问题,而不必使用动态或您提到的其他静态选项。您可以使用
选项获得许多相同的便利。应用
,您需要定义自己(或从库中获取)。此代码被窃取并改编自:

模块选项=
让应用fOpt xOpt=
将fOpt、xOpt与
|一些f,一些x->一些(f x)
|无
让结果选项=
让()=Option.apply
一些(乐趣x y z->sprintf“%i%s%A”x y z)
大约5
一些“嗨”
有些是真的

为了解释您的方法不起作用的原因,问题是您不能将类型为
int->int
(表示为
FSharpFunc
)的函数强制转换为类型为
obj->obj
(表示为
FSharpFunc
)的值。这些类型是相同的泛型类型,但由于泛型参数不同,转换失败

如果插入了大量装箱和拆箱,那么函数实际上可以工作,但这可能不是您想要编写的内容:

let ans = mapN<string> (fun (x:obj) -> box (fun (y:obj) -> box (fun (z:obj) -> 
  box (Some(sprintf "%i %s %A" (unbox x) (unbox y) (unbox z)))))) 
    [a |> toObjOption; b |> toObjOption; c |> toObjOption]
ans=mapN(fun(x:obj)->box(fun(y:obj)->box(fun(z:obj)->
框(一些(sprintf“%i%s%A”(unbox x)(unbox y)(unbox zщщ)))
[a |>ToobOption;b |>ToobOption;c |>ToobOption]
如果你想通过动态黑客探索更多可能的选项,那么你可以使用F#reflection做更多的事情。我通常不会在生产中使用它(简单更好-我只需要手动或类似的方式定义多个映射函数),但会执行以下操作:

let rec mapN<'R> f args = 
  match args with 
  | [] -> unbox<'R> f
  | x::xs ->
      let m = f.GetType().GetMethods() |> Seq.find (fun m -> 
        m.Name = "Invoke" && m.GetParameters().Length = 1)
      mapN<'R> (m.Invoke(f, [| x |])) xs

mapN<obj> (fun a b c -> sprintf "%d %s %A" a b c) [box 1; box "hi"; box true]  
let rec mapN f
|x::xs->
让m=f.GetType().GetMethods()|>Seq.find(有趣的m->
m、 Name=“Invoke”&&m.GetParameters().Length=1)

mapnerop;这是一种称为Applicative的功能“模式”。非常有用,我也会用F#这样的语言来做。在不支持部分应用程序的静态类型语言(C#、kotlin、java等)中,我将使用代码生成工具为1个参数、2个参数等生成重载。在C++中,支持变量的通用参数列表,这样我们就可以编写一个像OP那样运行函数的MAPN。@ QuasBrnnFox,我读了关于F的文章以获得乐趣和利润,关于<代码>应用< /C>函数,但是我完全忘记了。谢谢你在这里指出它的用途谢谢你解释为什么我的解决方案不起作用以及如何使它起作用。我同意动态解决方案非常粗糙,不适合生产。这并没有改变我对F#的看法,我只是好奇那里的功能。
let ans = mapN<string> (fun (x:obj) -> box (fun (y:obj) -> box (fun (z:obj) -> 
  box (Some(sprintf "%i %s %A" (unbox x) (unbox y) (unbox z)))))) 
    [a |> toObjOption; b |> toObjOption; c |> toObjOption]
let rec mapN<'R> f args = 
  match args with 
  | [] -> unbox<'R> f
  | x::xs ->
      let m = f.GetType().GetMethods() |> Seq.find (fun m -> 
        m.Name = "Invoke" && m.GetParameters().Length = 1)
      mapN<'R> (m.Invoke(f, [| x |])) xs

mapN<obj> (fun a b c -> sprintf "%d %s %A" a b c) [box 1; box "hi"; box true]