有没有简单的方法来做到这一点。它看起来像你真的想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
正确的形式是'让可变计算堆栈:'列表= ...' –