Exploring ReasonML 学习笔记 -- 4. Polymorphic Variant Types

200 阅读1分钟

Exploring ReasonML

  1. Constructors are not tied to types anymore; they exist independently
type rgb = [`Red | `Green | `Blue];
  1. Polymorphic constructors exist on their own

There is no need to define them beforehand

/* Error: Unbound constructor Int */
Int(123); 

/* Success: type `Int(int) */
`Int(123);

/* use the same polymorphic constructor with different arities and/or type parameters */
`Int("abc", true);
`Int(1.0, 2.0, 3.0);
  1. Extending polymorphic variants
type rgb = [`Red | `Green | `Blue];
type color = [rgb | `Orange | `Yellow | `Purple];
  1. Type constraints for parameters
/ * let id: (([> `Blue | `Green | `Red ] as ‘a)) => ‘a = <fun>; */
let id = (x: [> `Red | `Green | `Blue]) => x;

let id = (x: [> rgb]) => x;

x has a lower bound and accepts all types that have at least the given three constructors.

  1. Polymorphic variant abbreviation patterns
type point = [ `Point(float, float) ];
type shape = [
  | `Rectangle(point, point)
  | `Circle(point, float)
];
let pi = 4.0 *. atan(1.0);
let computeArea = (s: shape) =>
  switch s {
  | `Rectangle(`Point(x1, y1), `Point(x2, y2)) =>
    let width = abs_float(x2 -. x1);
    let height = abs_float(y2 -. y1);
    width *. height;
  | `Circle(_, radius) => pi *. (radius ** 2.0)
  };

type shapePlus = [
  | `Rectangle(point, point)
  | `Circle(point, float)
  | `Triangle(point, point, point)
];
let computeAreaPlus = (sp: shapePlus) =>
  switch sp {
  | `Triangle(p1, p2, p3) => shoelaceFormula(p1, p2, p3)
  | `Rectangle(_, _) as r => computeArea(r)
  | `Circle(_, _) as c => computeArea(c)
  };

// or abbreviation
let computeAreaPlus = (sp: shapePlus) =>
  switch sp {
  | `Triangle(p1, p2, p3) => shoelaceFormula(p1, p2, p3)
  | #shape as s => computeArea(s)
  };
  1. Best practices: normal variants vs. polymorphic variants

Polymorphic:

  • Reuse: A constructor (possibly along with code processing it) is useful for more than one variant. Constructors for colors fall into this category, for example.

  • Decoupling: A constructor is used in multiple locations, but you don’t want those locations to depend on a single module where the constructor is defined. Instead, you can simply use the constructor without defining it.

  • Extensibility: You expect a variant to be extended later on. Similar to how shapePlus was an extension of shape, earlier in this chapter.

  • Conciseness: Due to the global namespace of polymorphic constructors, you can use them without qualifying them or opening their modules (see next subsection).

  • Use constructors without prior definitions: You can use polymorphic constructors without defining them beforehand via variants. That is occasionally convenient for throw-away types that are only used in single locations.