【发布时间】:2014-03-15 19:33:03
【问题描述】:
假设我有两个班级:
public class Triangle {
public float Base { get; set; }
public float Height { get; set; }
public float CalcArea() { return Base * Height / 2.0; }
}
public class Cylinder {
public float Radius { get; set; }
public float Height { get; set; }
public float CalcVolume() { return Radius * Radius * Math.PI * Height }
}
我们这里有两个几何形状的描述以及两者的操作。
这是我在 F# 中的尝试:
type Triangle = { Base: float; Height: float }
module TriangleStuff =
let CalcArea t =
t.Base * t.Height / 2.0
type Cylinder = { Radius: float; Height: float }
module CylinderStuff =
let CalcVolume c =
c.Radius * c.Radius * Math.PI * c.Height
假设我对这两个类进行了观察(它们都有Height!)并且我想提取一个对任何具有高度属性的东西都有意义的操作。所以在 C# 中我可能会拉出一个基类并在那里定义操作,如下所示:
public abstract class ShapeWithHeight {
public float Height { get; set; }
public virtual bool CanSuperManJumpOver() {
return Height == TALL; // Superman can *only* jump over tall buildings
}
public const float TALL = float.MaxValue;
}
public class Triangle : ShapeWithHeight {
public float Base { get; set; }
public float CalcArea() { return Base * Height / 2.0; }
public override bool CanSuperManJumpOver() {
throw new InvalidOperationException("Superman can only jump over 3-d objects");
}
}
public class Cylinder : ShapeWithHeight {
public float Radius { get; set; }
public float CalcVolume() { return Radius * Radius * Math.PI * Height }
}
请注意各个子类对于此操作的实现可能有自己的想法。
言归正传,我可能在某个地方有一个函数可以接受 一个三角形或一个圆柱体:
public class Superman {
public void JumpOver(ShapeWithHeight shape) {
try {
if (shape.CanSuperManJumpOver()) { Jump (shape); }
} catch {
// ...
}
}
}
.. 这个函数可以接受三角形或圆柱体。
我无法将相同的思路应用到 F#。
我一直在阅读有关函数式语言的文章。传统的想法是更喜欢表达代数值类型而不是继承类。这种想法认为,最好用更小的构建块来组合或构建更丰富的类型,而不是从抽象类开始并从那里缩小。
在 F# 中,我希望能够定义一个函数,该函数接受一个已知具有 Height 属性的参数,并以某种方式使用它(即 CanSuperManJumpOver 的基本版本)。我应该如何在功能世界中构建这些类型来实现它?我的问题在功能世界中是否有意义?欢迎任何有想法的cmets。
【问题讨论】:
标签: c# architecture f# functional-programming