没有简单的方法可以做到这一点。看起来你真的想要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