13

我正在定义一个单子可观察/反应式解析器。这与普通解析器的行为完全不同,因为它是一个连续查询。底层类型是:

IObservable<'a> -> IObservable<'b>

通过查看函数式语言中的各种解析器实现,似乎更合适的定义事物的方法是单例区分联合:

type Pattern<'a,'b> = Pattern of (IObservable<'a> -> IObservable<'b>)

这意味着我需要提取底层函数来使用它:

let find (Pattern p) = p

问题是:这只是按照惯例,还是出于以后扩展的目的,或者即使定义从未改变,也有理由这样做?

额外的问题:如果只是为了更方便的类型签名,为什么不使用类型别名:

type Pattern<'a,'b> = IObservable<'a> -> IObservable<'b>

我已经在这方面取得了很大进展,并且没有发现不使用 DU 会影响可组合性的情况。

4

2 回答 2

15

F# 编译器不保留有关类型缩写的信息,因此您根本不会从类型推断中受益。类型签名可以理解为程序规范;让类型检查器完成他们的工作是确保程序正确性的好方法。

在类型别名的情况下,您需要在任何地方显式指定类型注释:

type Vector = float * float

// val add : float * float -> float * float -> Vector
let add ((x1, y1): Vector) ((x2, y2): Vector): Vector = (x1 + y1, x2 + y2)

但它不会像使用 DU 那样给你透明度:

type Vector = V of float * float

// val add : Vector -> Vector -> Vector
let add (V(x1, y1)) (V(x2, y2)) = V(x1 + y1, x2 + y2)

在复杂的程序中,清晰的类型签名确实更容易维护可组合性。

不仅向单案例 DU 添加更多案例更简单,而且使用成员和静态方法扩展 DU 也更容易。一个例子是你经常重写ToString()漂亮的打印。

于 2012-04-14T09:02:20.143 回答
0

据我了解,单例区分联合类型可以提供与您的问题域语义相关的名称,以及名称为“字符串”的其他通用后端类型。

这是对语义的轻抽象泄漏修复,仅此而已,AFAIK

于 2012-04-14T08:52:46.547 回答