[ANN] rescript-struct renamed to rescript-schema and a new release

Letting you know that a few days ago rescript-struct was renamed to rescript-schema. It wasn’t an easy decision since rescript-struct has already become a well-known name, but I think it was right to make the change. Actually, I felt so relieved after this, I didn’t imagine that the name bothered me so much.
The primary motivation for the rename was that I chose the original struct naming by coincidence:

  • I couldn’t find good free package names on npm
  • The first versions of the package were inspired by superstruct, so I thought that the struct name was fine.

But the problem with a struct is that in many languages, it has its own meaning, and for many people, I had to explain that struct is the same thing as schema, but just called differently. So, to avoid the confusion, rescript-struct was renamed to rescript-schema :partying_face:

You can install it from npm:

npm install rescript-schema

Also, after the rename, I’ve just done a feature release :rocket:

New S.schema helper

(schemaCtx => 'value) => S.t<'value>

It’s a helper built on S.literal, S.object, and S.tuple to create schemas for runtime representation of ReScript types conveniently.

@unboxed
type answer =
  | Text(string)
  | MultiSelect(array<string>)
  | Other({value: string, @as("description") maybeDescription: option<string>})

let textSchema = S.schema(s => Text(s.matches(S.string)))
// It'll create the following schema:
// S.string->S.variant(string => Text(string))

let multySelectSchema = S.schema(s => MultiSelect(s.matches(S.array(S.string))))
// The same as:
// S.array(S.string)->S.variant(array => MultiSelect(array))

let otherSchema = S.schema(s => Other({
  value: s.matches(S.string),
  maybeDescription: s.matches(S.option(S.string)),
}))
// Creates the schema under the hood:
// S.object(s => Other({
//   value: s.field("value", S.string),
//   maybeDescription: s.field("description", S.option(S.string)),
// }))
//       Notice how the field name /|\ is taken from the type's @as attribute

let tupleExampleSchema = S.schema(s => (#id, s.matches(S.string)))
// The same as:
// S.tuple(s => (s.item(0, S.literal(#id)), s.item(1, S.string)))

Also, it works fantastic with discriminated unions:

@tag("kind")
type shape =
  | @as("circle") Circle({radius: float})
  | @as("square") Square({x: float})
  | @as("triangle") Triangle({x: float, y: float})

// With S.schema
let circleSchema = S.schema(s => Circle({
  radius: s.matches(S.float),
}))

// With S.object
let circleSchema = S.object(s => {
  s.tag("kind", "circle")
  Circle({
    radius: s.field("radius", S.float),
  })
})

:brain: Note that S.schema relies on the runtime representation of your type, while S.object/S.tuple are more flexible and require you to describe the schema explicitly.

12 Likes