2017-02-17 59 views
2

我是F#的新手,并试图设计一些类型,我注意到有多少OOP影响了我的设计决策。我很难搜索这个特定的问题,并且空手而归。设计F#类型与隐藏底层实现从界面

我将描述我在C#中试图做的事情,因为我更熟悉术语。让我们说,我有一个接口,指定容器类的一些最小必需方法。我们称之为​​。然后我有两个类实现这个接口,ContainerAContainerB带有不同的底层实现,这些实现对用户是隐藏的。这是一个非常常见的OOP模式。

我想在F#中实现同样的事情只与不可变类型留在功能世界,即我如何实现一种类型,其功能是可以互换的,但用户将使用的公共功能保持不变:

type 'a MyType = ... 

let func1 mytype = ... 
let func2 mytype -> int = ... 

MyType的定义是未知的,可以稍后改变,例如如果找到更高效的功能版本(比如更好地实现容器类型),但是没有太多努力或需要重新设计整个模块。一种方法是在函数和区分的联合中使用模式匹配,但这看起来不可扩展。

+4

所以你想在F#中做同样的事情,因为你可以在其中实现类和接口。 – kemiller2002

+1

是的。一种可能是将设计完全从C#复制到F#,但是底层实现必须是派生类的一部分。例如,在容器上运行的函数应该调用'container.IsEmpty'吗?我怎样才能设计类/类型,让'add add ... ='不会修改对象的状态? – NordCoder

+4

目前还不清楚您是否尝试在F#中执行OOP,或者您是否正在通过功能镜头来重新考虑问题。你想完成一个接口的功能吗,但是以F#惯用方式,还是你想在代码中使用字面接口? –

回答

2

在函数式语言中使用比在OO语言中更简单的类型更为典型。

建模形状是一个典型的例子。

下面是一个典型的面向对象的方法:

type IShape = 
    abstract member Area : double 

type Circle(r : float) = 
    member this.Area = System.Math.PI * r ** 2.0 
    interface IShape with 
     member this.Area = this.Area 

type Rectangle(w : float, h : float) = 
    member this.Area = w * h 
    interface IShape with 
     member this.Area = this.Area 

请注意,这是很容易使用这种方法来添加新的类型,我们可以引入一个TriangleHexagon类相对较少的努力。我们只需创建类型并实现接口。

相反,如果我们想要一个新的Perimeter成员添加到我们的IShape,我们将不得不改变每一个可能是大量的工作落实。

现在让我们来看看如何,我们可能会在函数式语言模型的形状:

type Shape = 
    |Circle of float 
    |Rectangle of float * float 

[<CompilationRepresentation (CompilationRepresentationFlags.ModuleSuffix)>] 
module Shape = 
    let area = function 
     |Circle r -> System.Math.PI * r ** 2.0 
     |Rectangle (w, h) -> w*h 

现在,希望你可以看到,它更容易添加perimeter功能,我们只需对每Shape情况下,模式匹配和编译器可以检查我们是否对每个案例进行了详尽的实施。

相比之下,添加新的Shape s现在要困难得多,因为我们必须返回并更改每个作用于Shape的函数。

结果是,无论我们选择使用哪种形式的建模,都存在权衡。这个问题被称为表达问题


您可以方便地申请第二模式您Container问题:

type Container = 
    |ContainerA 
    |ContainerB 

let containerFunction1 = function 
    |ContainerA -> .... 
    |ContainerB -> .... 

在这里,您有两个或更多的情况下和独特的落实为每个个案的功能单一类型包含在模块功能,而不是类型本身。

+0

我喜欢OO和FP范例之间权衡的观点,我并不知道表达问题。谢谢!并感谢所有其他有用的评论者! – NordCoder

+1

@NordCoder值得注意的是,还有其他解决方案。如果你有一个适合ad-hoc多态性(即type-classes/traits)的良好系统,那么就可以开辟另一个选择。 .NET目前还不支持它们,尽管最近演示了一种实现它们的机制。 – TheInnerLight

+0

我意识到这一点,实际上我的F#代码受Haskell库启发,该库使用类型类来实现此目的。我的理解是F#“类型类”实现与Haskell相比有点受限,但如果我错了,请纠正我。 – NordCoder