gpt4 book ai didi

f# - 当方法具有返回值时,如何在 F# 中提供 Expression>?

转载 作者:行者123 更新时间:2023-12-04 12:22:13 25 4
gpt4 key购买 nike

我正在尝试将一些 C# 代码转换为 F#。具体来说,我正在尝试使用 Hyprlinkr 转换一些代码到F#。

C# 代码如下所示:

Href = this.linker.GetUri<ImagesController>(c =>
c.Get("{file-name}")).ToString()

哪里 GetUri方法定义为
public Uri GetUri<T>(Expression<Action<T>> method);

ImagesController.Get被定义为
public HttpResponseMessage Get(string id)

在 F# 中,我试图这样做:
Href = linker.GetUri<ImagesController>(
fun c -> c.Get("{file-name}") |> ignore).ToString())

这会编译,但在运行时抛出此异常:

System.ArgumentException was unhandled by user code
HResult=-2147024809
Message=Expression of type 'System.Void' cannot be used for return type 'Microsoft.FSharp.Core.Unit'
Source=System.Core



据我了解,F# 表达式是一个返回 unit 的表达式。 ,但它真的应该是 Expression<Action<T>> , '返回' void .

我使用的是 F# 3.0(我认为 - 我使用的是 Visual Studio 2012)。

我该如何解决这个问题?

最佳答案

我的猜测是它应该在 F# 3.1 中修复。这是来自 VS2013 预览版

type T = static member Get(e : System.Linq.Expressions.Expression<System.Action<'T>>) = e
type U = member this.MakeString() = "123"
T.Get(fun (u : U) -> ignore(u.MakeString())) // u => Ignore(u.MakeString())

更新:无法从问题中检查实际库,因此我会尝试模仿我看到的界面。此代码在 F# 3.1 中运行良好
open System
open System.Linq.Expressions

type Linker() =
member this.GetUri<'T>(action : Expression<Action<'T>>) : string = action.ToString()

type Model() = class end

type Controller() =
member this.Get(s : string) = Model()

let linker = Linker()
let text1 = linker.GetUri<Controller>(fun c -> c.Get("x") |> ignore) // c => op_PipeRight(c.Get("x"), ToFSharpFunc(value => Ignore(value)))
let text2 = linker.GetUri<Controller>(fun c -> ignore(c.Get("x"))) // c => Ignore(c.Get("x"))

printfn "Ok"

更新 2:我已经查看了 Hyprlinkr 的源代码,我想我已经找到了原因。
分析表达式树的库代码的当前实现对其形状做出了某些假设。特别是:
// C#
linker.GetUri((c : Controller) => c.Get("{file-name}"))
  • 代码假定表达式树的主体是方法调用表达式(即从 Controller 调用某些方法)
  • 然后代码一一选择方法调用参数,并尝试通过将它们包装到 0 参数 lambda、编译和运行它来获取其值。库隐式地依赖于参数值是常量值或从封闭环境中捕获的值。

  • F# 运行时生成的表达式树的形状(即使用管道时)将是

    c => op_PipeRight(c.Get("x"), ToFSharpFunc(value => Ignore(value)))

    这仍然是方法调用表达式(所以假设 1 仍然是正确的)但它的第一个参数使用参数 c。如果这个参数将被转换为没有参数的 lambda (() => c.Get("x")) - 那么这种 lambda 的方法体将引用一些自由变量 c - 正是异常消息中写入的内容。

    作为一种对 F# 更友好的替代方案,我可以建议为 GetUri 添加额外的重载
    public string GetUri<T, R>(Expression<Func<T, R>> e)

    它可以同时用于 C# 和 F# 端
    // C#
    linker.GetUri((Controller c) => c.Get("{filename}"))

    // F#
    linker.GetUri(fun (c : Controller) -> c.Get("{filename}"))

    关于f# - 当方法具有返回值时,如何在 F# 中提供 Expression<Action<T>>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17693241/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com