使用let inline和member约束我能够为已知成员进行鸭子输入,但是如果我想定义一个通用函数,那该怎么办:
让duckwrapper<" a> duck = ...
签名' b - > ' a,返回值将是实现' a(可能是接口)的对象,并将调用转发给duck。
我已经使用Reflection.Emit在C#中完成了这项工作,但我想知道F#反射,引用或其他结构是否会让它变得更容易。
有关如何完成此任务的任何建议?
修改 在阅读Tims回答后,我想我会提供更多细节
当我写关于使用引文来帮助时,我想到的是:
{new IInterface with member x.SayHello() = !!<@ %expr @>}
!!是将运算符转换为函数的运算符,%expr是该方法的工作单元。我能够将表达式转换为函数(我猜)但不知道如何
当然,由于IInterface是&#39;这是我希望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)
尝试移植&#34;&#34; F#中DCI的教科书示例。源和目标是DCI角色。它的想法是,任何符合特定合同的数据对象都可以发挥这些作用。在这种情况下,合同很简单。 source需要一个名为decreaseBalance的成员函数,而destination需要一个名为increaseBalance的成员函数。 我可以通过内联和成员约束来实现这个特定情况。 但是我想编写一组给定接口和对象的函数。在这种情况下,它可以是源(作为对象)和
type sourceContract =
abstract decreaseBalance : decimal -> sourceContract
作为类型。结果将是sourceContract类型的对象,它将方法调用传递给源对象上具有相同名称的方法。
答案 0 :(得分:4)
F#reflection(Microsoft.FSharp.Reflection
)是一个围绕普通System.Reflection
API的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
答案 1 :(得分:2)
您可以使用F# PowerPack中的组件编译F#引用。所以我认为你可以使用引号在运行时生成和执行代码。如果你写一个代表功能的报价&amp;编译它你将获得一个可用于实现接口的函数值。这是一个简单的例子:
#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#代码那么高效(但你需要在你的情况下测量它)。
我不太确定我明白你究竟想做什么,所以如果你能提供更多细节,我可以给出更具体的答案。