F# 使用接口扩展对象的设计备选方案

F# 使用接口扩展对象的设计备选方案,f#,F#,在再次使用专家F#时,我决定实现用于操纵代数表达式的应用程序。这进展顺利,现在我决定作为下一个练习,通过构建更高级的应用程序来扩展这一点 我的第一个想法是建立一个允许更可扩展的方法来创建函数,而无需重新编译。为此,我提出了如下建议: type IFunction = member x.Name : string with get /// additional members omitted type Expr = | Num of decimal | Var o

在再次使用专家F#时,我决定实现用于操纵代数表达式的应用程序。这进展顺利,现在我决定作为下一个练习,通过构建更高级的应用程序来扩展这一点

我的第一个想法是建立一个允许更可扩展的方法来创建函数,而无需重新编译。为此,我提出了如下建议:

type IFunction =
    member x.Name : string with get
    /// additional members omitted

type Expr =
    | Num of decimal
    | Var of string
    ///... omitting some types here that don't matter
    | FunctionApplication of IFunction * Expr list
let makeDifferentiable (deriv : int -> IFunction)  (f : IFunction)=
    { new IFunction with
          member x.Name = f.Name
      interface IDifferentiable with
          member x.Derivative = deriv }
所以说一个Sin(x)可以表示为:

let sin = { new IFunction() with member x.Name = "SIN" }
let sinExpr = FunctionApplication(sin,Var("x"))
到目前为止一切都很好,但我想实现的下一个想法是使用额外的接口来表示属性的功能。例如

type IDifferentiable =
     member Derivative : int -> IFunction // Get the derivative w.r.t a variable index 
我在这里试图实现的一个想法是,我实现一些函数和它们的所有逻辑,然后继续我想要实现的逻辑的下一部分。然而,就目前的情况而言,这意味着对于我添加的每个接口,我必须重新访问我实现的所有iFunction。相反,我希望有一个函数:

let makeDifferentiable (f : IFunction) (deriv : int -> IFunction) =
    { f with
        interface IDifferentiable with
            member x.Derivative = deriv }
但正如中所讨论的,这是不可能的。可能的替代方案不符合我的扩展性要求。我的问题是,哪些替代方案会很好地发挥作用

[编辑]我被要求对“不符合我的可扩展性要求”的评论进行扩展。此功能的工作方式是执行以下操作:

type IFunction =
    member x.Name : string with get
    /// additional members omitted

type Expr =
    | Num of decimal
    | Var of string
    ///... omitting some types here that don't matter
    | FunctionApplication of IFunction * Expr list
let makeDifferentiable (deriv : int -> IFunction)  (f : IFunction)=
    { new IFunction with
          member x.Name = f.Name
      interface IDifferentiable with
          member x.Derivative = deriv }
然而,理想情况下,我会在添加接口时继续向对象添加其他接口。所以,如果我现在想添加一个接口来判断on函数是否为偶数:

type IsEven =
    abstract member IsEven : bool with get
然后,我希望能够(但不是必须,如中所述,如果我不进行此更改,所有内容仍应编译)更改我对正弦的定义

let sin = { new IFunction with ... } >> (makeDifferentiable ...) 

这样做的结果是,我可以创建一个对象来实现IFunction接口,而且可能实现,但不一定也实现很多不同的其他接口;然后,我在它们上面定义的操作,可能会根据某个函数是否实现了接口来优化它们正在做的事情。这还允许我先添加其他功能/接口/操作,而无需更改我定义的函数(尽管它们不会利用这些附加功能,但也不会破坏这些功能。[/EDIT]

我现在唯一能想到的就是为我想要实现的每个功能创建一个字典,将函数名作为键,并提供详细信息以动态构建接口,例如:

let derivative (f : IFunction) =
    match derivativeDictionary.TryGetValue(f.Name) with
    | false, _ -> None
    | true, d  -> d.Derivative

这将需要我为每个功能创建一个这样的函数,并为每个功能添加一个字典。特别是如果使用代理异步实现,这可能不会太慢,但仍然感觉有点笨拙。

我认为您试图解决的问题就是所谓的问题。您实际上是在尝试编写te可在两个方向上扩展的代码。有区别的联合和面向对象的模型为您提供了一个或另一个:

  • 区分并集使添加新操作变得容易(只需编写具有模式匹配的函数),但添加新类型的表达式却很困难(您必须扩展DU并修改所有代码
    使用它)

  • 接口使添加新类型的表达式变得容易(只需实现接口),但添加新操作却很困难(您必须修改接口并更改创建接口的所有代码)

总的来说,我不认为尝试提出解决方案让你同时做到这两件事是很有用的(它们最终变得非常复杂),所以我的建议是选择一个你更经常需要的解决方案

回到您的问题,我可能会将函数与参数一起表示为函数名:

type Expr =
  | Num of decimal
  | Var of string
  | Application of string * Expr list
真的-表达式就是这样。可以求导数这一事实是你要解决的问题的另一部分。现在,为了使导数可扩展,你可以保留一个导数字典:

 let derrivatives = 
   dict [ "sin", (fun [arg] -> Application("cos", [arg])) 
          ... ] 

这样,您就有了一个真正建模表达式的
Expr
类型,并且您可以编写微分函数,在字典中查找导数。

您能澄清一下包装器类型不满足扩展性要求的原因吗?如果您在obje上同时实现了IFunction和IDifferentiable这和我想象的{f和…}非常相似语法就可以了,所以我不确定你到底在寻找什么。试着看看F#中的类型类实现。我在电话上,所以我不会写一个详细的答案,但看看这里,你也可以使用记录类型而不是对象类型,并留下接口限制。你的设计使用记录实现了什么(例如,
类型函数{Name:string;导数:(int->Function);IsEven:boolean}
)不会吗?我只是看不到接口的好处。因为添加一个额外的功能会迫使我为每个函数实现该字段。我正试图提前计划一种情况,在这种情况下,我有一些功能正在进行,可能定义了50个不同的函数,我想在函数上添加另一个操作;s我希望操作能检查接口是否实现,如果实现了,做点什么,如果没有。如果我不更新(全部),什么都不会被破坏我已经定义的函数。是的,看起来我确实在寻找表达式问题的解决方案。我将你的答案标记为答案,除了字符串vs IFunction,这基本上是我想到的备份解决方案。我仍然认为我正在寻找的解决方案会更好,因为它将保留所有函数数据/属性RTI集成在一个对象中,并使在外部库中定义附加接口变得更容易。仅供参考,在.NET中是否有一个基本的限制,为什么我希望