Skip to content

Latest commit

 

History

History
84 lines (55 loc) · 2.4 KB

README.md

File metadata and controls

84 lines (55 loc) · 2.4 KB

re-opaque

A ReasonML library for opaque data types with validation.

Opaque strings

At runtime, they are just strings. But since each of them is a distinct module, they form distinct types; so you can never e.g. mix up an Email with a Name. In addition, you can set up validation rules and guarantee (short of escape hatches like Obj.magic) that all strings in the type will have been validated.

Example

Below we create three string-like modules, UserName, Email, and MessageText. Each has its own validation logic (or lack thereof).

open Opaque.String;

module UserName: StringType = Make(
  Validation.Compose(
    Validation.MinLength({let n = 10;}),
    Validation.MaxLength({let n = 80;}),
  ), ()
);

module Email = Opaque.String.Make(Opaque.RegexValidation({
  let regex = [%re {|/magic email regex/|}];
}, ()));

// This has no validation (although in practice a max length might be good)
module MessageText = Opaque.String.Make(String.NoValidation);

We can't create a username that's less than 5 characters:

// raises `TooShort("bad", 10)`
let badUsername = "bad"->UserName.fromString;

Or an email that doesn't match our regex:

// raises `RegexMatchError("i am not an email", <some regex>)`
let badEmail = "i am not an email"->Email.fromString;

This guarantee means that you have total confidence that you won't be handling invalid data, and pushes error boundaries as early as possible.

Error handling

The fromString function will raise an exn exceptions to on failure. Different validators raise different exceptions, so you can switch on them with try or | exception _ to do error handling.

Alternatively you can use resultFromString, which avoids potential error cascades. It's up to you.

// Ok("probablyok"), which is typed as result(UserName.t, exn)
let goodUsername = "probablyok"->UserName.resultFromString;

// Error(TooShort("nope", 10))
let badUsername = "nope"->UserName.resultFromString;

Extras

The module also generates fromJson and toJson functions, allowing easy conversion to/from JSON for the custom types.

There's also a MakeStringSet functor which creates a Set module for managing sets of custom types with Belt.String. There might be more on this later.

Build

yarn re:build

# Alternatively, in watch mode
yarn re:watch

Test

yarn test

# Alternatively, in watch mode
yarn test:watch