[英]generic duck typing in F#?
使用let inline和member約束我將能夠為已知成員進行duck typing,但是如果我想定義一個泛型函數如下:
讓duckwrapper <'a> duck = ...
使用簽名'b - >'a並且返回值將是實現'a(將是一個接口)並將調用轉發給duck的對象。
我在C#中使用Reflection.Emit完成了這個,但我想知道F#反射,引用或其他結構是否會使它變得更容易。
有關如何完成此任務的任何建議?
在閱讀Tims回答之后編輯 ,我想我會提供更多細節
當我寫關於使用引用來幫助時,我想到的是:
{new IInterface with member x.SayHello() = !!<@ %expr @>}
! 是將運算符轉換為函數的運算符,%expr是該方法的工作單元。 我能夠將表達式轉換為函數(我猜),但不知道如何
當然這不會完全成功,因為IInterface將是'我希望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需要一個名為decreaseBalance的成員函數,而destination需要一個名為increaseBalance的成員函數。 我可以通過內聯和成員約束來實現這個特定情況。 但是我想編寫一組給出接口和對象的函數。 在這種情況下,它可以是源(作為對象)和
type sourceContract =
abstract decreaseBalance : decimal -> sourceContract
作為類型。 結果將是sourceContract類型的對象,它將方法調用傳遞給源對象上具有相同名稱的方法。
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#reflection可能有一些方便的函數,這樣我就可以構建一個基於類型對象和一些函數值的類型
我擔心不會。 這是關於F#反射的文檔: http : //msdn.microsoft.com/en-gb/library/ee353491.aspx
您可以使用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#代碼那樣高效(但是你需要在你的情況下測量它)。
我不太清楚我明白你究竟想做什么,所以如果你能提供更多細節,我可以給出更具體的答案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.