有沒有簡單的方法來做到這一點。它看起來像你真的想calculationStack
有類型:
(∃('t:>IFilter<'a>).Calculator<'a, 't>) list
但F#不提供存在的類型。您可以使用「雙否定編碼」 ∃'t.f<'t> = ∀'x.(∀'t.f<'t>->'x)->'x
拿出了以下解決方法:
// helper type representing ∀'t.Calculator<'t>->'x
type AnyCalc<'x,'a> = abstract Apply<'t when 't :> IFilter<'a>> : Calculator<'a,'t> -> 'x
// type representing ∃('t:>IFilter<'a>).Calculator<'a, 't>
type ExCalc<'a> = abstract Apply : AnyCalc<'x,'a> -> 'x
// packs a particular Calculator<'a,'t> into an ExCalc<'a>
let pack f = { new ExCalc<'a> with member this.Apply(i) = i.Apply f }
// all packing and unpacking hidden here
type TowerControl<'a>() =
let mutable calculationStack = List.empty
// note: type inferred correctly!
member this.addCalculation x =
let newList = (pack x)::calculationStack
calculationStack <- newList
// added this to show how to unpack the calculations for application
member this.SequenceCalculations (v:'a) =
calculationStack |> List.fold (fun v i -> i.Apply { new AnyCalc<_,_> with member this.Apply c = c.Calculate v }) v
// the remaining code is untouched
let floor10 = Floor<int> 10
let calc1 = Calculator<int, Floor<int>> (floor10, ((+) 10))
let cap10 = Cap 10
let calc2 = Calculator (cap10, ((-) 5))
let tower = TowerControl<int>()
tower.addCalculation calc1
tower.addCalculation calc2
這具有很大的優勢,它不需要修改Calculator<_,_>
類型,並且語義正是你想要的,但有以下缺點:
- 如果你不熟悉這種編碼存在的方式很難遵循。
即使你很熟悉,也有很多醜陋的樣板(兩種幫手類型),因爲F#不允許匿名通用資格。也就是說,即使考慮到F#不直接支持存在類型,它會更容易閱讀,如果你能寫的東西,如:
type ExCalc<'a> = ∀'x.(∀('t:>IFilter<'a>).Calculator<'a,'t>->'x)->'x
let pack (c:Calculator<'a,'t>) : ExCalc<'a> = fun f -> f c
type TowerControl<'a>() =
...
member this.SequenceCalcualtions (v:'a) =
calculationStack |> List.fold (fun v i -> i (fun c -> c.Calculate v)) v
但是相反,我們得拿出兩個助手名稱類型和他們的單一方法。這最終導致代碼難以遵循,即使對於已經熟悉這種通用技術的人也是如此。
在所擁有的Calculator<_,_>
類起飛的機會,還有一個更簡單的解決方案,可能的工作(這也取決於真正Calcuator的方法< ,>類,如果是簽名比您在此處介紹的要複雜得多):引入一個ICalculator<'a>
接口,使Calculator<_,_>
實現該接口,並使calculationStack
爲該接口類型的值列表。這會讓人們更容易理解,但只有當你擁有Calculator<_,_>
(或者如果已經有一個現有的可以繼續使用的界面)時纔可能。你甚至可以將接口設置爲私有的,這樣只有你的代碼知道它的存在。這是看起來如何:
type private ICalculator<'a> = abstract Calculate : 'a -> 'a
type Calculator<'a, 'b when 'b:> IFilter<'a>> (aFilter: 'b, operation: 'a -> 'a) =
member this.Calculate x =
let y = x |> operation
aFilter.Filter y
interface ICalculator<'a> with
member this.Calculate x = this.Calculate x
type TowerControl<'a>() =
let mutable calculationStack = List.empty
member this.addCalculation (x: Calculator<'a, #IFilter<'a>>) =
let newList = (x :> ICalculator<'a>)::calculationStack
calculationStack <- newList
member this.SequenceCalculations (v:'a) =
calculationStack |> List.fold (fun v c -> c.Calculate v) v
來源
2014-02-24 18:49:09
kvb
我懷疑添加''a'註釋到計算堆可能會有所幫助。 –
謝謝。我嘗試了'讓可變計算堆棧<'a> = List.empty'這導致'錯誤FS0830:可變值不能有通用參數' – NoIdeaHowToFixThis
正確的形式是'讓可變計算堆棧:'列表= ...' –