Question

I'm defining a monadic observable/reactive parser. This behaves quite differently to a normal parser as it is a continuous query. The underlying type is:

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

From looking at various parser implementations in functional languages, it seems as though the more appropriate way to define things is a single case discriminated union:

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

Which means I then need to extract the underlying function to use it:

let find (Pattern p) = p

The question is: Is this just by convention, or for purposes of later extension, or is there a reason to do this even if the definition never changes?

Bonus question: If it's just for a more convenient type signature, why not just use a type alias:

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

I've advanced quite a way through this, and haven't found a case where composability is affected by not using the DU.

Was it helpful?

Solution

F# compiler doesn't preserve information about type abbreviation, so you do not benefit from type inference at all. Type signature can be understood as program specification; letting the type checker do their job is a good way to ensure correctness of your programs.

You need explicitly specify type annotation everywhere in the case of type alias:

type Vector = float * float

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

but it doesn't give you transparency as using DUs:

type Vector = V of float * float

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

In complex programs clear type signatures indeed make it easier to maintain composability.

Not only it's simpler to add more cases to single-case DUs, but also it's easier to extend DUs with member and static methods. One example is you often override ToString() for pretty printing.

OTHER TIPS

From what I understand, single case discriminated union type are there to provide a name that is semantically relevant to your problem domain, to an otherwise general purpose back end type whose name is 'string'.

It is a light abstraction leakage fix for semantics, and only that, AFAIK

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top