3

使用 let 内联和成员约束,我将能够为已知成员进行鸭式输入,但如果我想定义一个像这样的通用函数怎么办:

让鸭包装器<'a>鸭= ...

带有签名 'b -> 'a 并且返回的值将是一个实现 'a 的对象(这将是一个接口)并将调用转发给鸭子。

我已经在 C# 中使用 Reflection.Emit 完成了这项工作,但我想知道 F# 反射、引用或其他构造是否会使其更容易。

关于如何做到这一点的任何建议?

阅读蒂姆斯回答后编辑 我想我会提供更多细节

当我写关于使用引用来帮助时,我的想法是这样的:

{new IInterface with member x.SayHello() = !!<@ %expr @>}

!!是将引用转换为函数的运算符,%expr 是该方法的工作单元。我可以将表达式转换为函数(我猜)但不知道如何

当然,这也不能完全做到这一点,因为 IInterface 将是 'a,我希望 F# 反射可能有一些方便的功能,以便我可以基于类型对象和一些函数值构造一个类型

编辑 作为 Tomas Petricek 答案的更新,我将提供一些代码来解释我的需求

type SourceRole =
   abstract transfer : decimal -> context

and context(sourceAccount:account, destinationAccount) =
   let source = sourceAccount
   let destination = destinationAccount

   member self.transfer amount = 
     let sourcePlayer = 
       {new SourceRole with
          member this.transfer amount =
              use scope =  new TransactionScope()
              let source = source.decreaseBalance amount
              let destination = destination.increaseBalance amount
              scope.Complete()
              context(source,destination)
              }
     sourcePlayer.transfer(amount)

这是在 F#中移植“ DCI ”教科书示例的尝试。源和目标是 DCI 角色。这个想法是任何遵守特定合同的数据对象都可以播放这些。在这种情况下,合同很简单。source 需要一个名为 reduceBalance 的成员函数,destination 需要一个名为 increaseBalance 的成员函数。对于这种特定情况,我可以使用 let inline 和 member 约束来实现这一点。但是我想写一组给定接口和对象的函数。在这种情况下,它可能是源(作为对象)和

type sourceContract = 
   abstract decreaseBalance : decimal -> sourceContract

作为类型。结果将是一个 sourceContract 类型的对象,它将方法调用通过管道传递给源对象上具有相同名称的方法。

4

2 回答 2

4

F# 反射 ( Microsoft.FSharp.Reflection) 是对普通System.ReflectionAPI 的 F# 友好包装器,因此我认为它不会在此处添加任何内容。

引用不能定义新类型:(你需要定义一个新类型来做你的基于接口的鸭子类型)

> <@ { new IInterface with member x.SayHello = "hello" } @>;;

  <@ { new IInterface with member x.SayHello = "hello" } @>;;
  ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stdin(7,4): error FS0449: Quotations cannot contain object expressions
> <@ type Test() = class end @>;;

  <@ type Test() = class end @>;;
  ---^^^^

stdin(8,4): error FS0010: Unexpected keyword 'type' in quotation literal

Reflection.Emit 仍然是解决这个问题的方法。

编辑:

我希望 F# 反射可能有一些方便的功能,以便我可以基于类型对象和一些函数值构造一个类型

恐怕它没有。这是有关 F# 反射的文档:http: //msdn.microsoft.com/en-gb/library/ee353491.aspx

于 2010-12-14T14:33:18.677 回答
2

您可以使用F# PowerPack中的组件编译 F# 引用。所以我认为你可以使用引号在运行时生成和执行代码。如果你写一个代表函数的引用并编译它,你会得到一个函数值,你可以用它来实现一个接口。这是一个简单的例子:

#r "FSharp.PowerPack.Linq.dll"
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.QuotationEvaluation

// Create a part using "Expr." calls explicitly
let expr = Expr.Value(13)
// Create a part using quotation syntax 
let expr2 = <@ (fun x -> x * %%expr) @>

// Compile & Run
let f = expr2.Compile()()
f 10

您可以混合引用语法和对 的调用Expr,这样可以更轻松地从基本块编写代码。编译有点愚蠢(目前),因此生成的代码不会像通常的 F# 代码那样高效(但您需要根据自己的情况对其进行测量)。

我不太确定我明白你到底想做什么,所以如果你能提供更多细节,我可以给出更具体的答案。

于 2010-12-14T23:43:12.553 回答