Chapter 7: Records, guards and deriving

Deriving

  • @derive
  • derivers
  • JSON

@derive(decoder, encoder) on a record writes functions that follow its fields one by one: user_decoder() and user_encoder(), which Bea code calls as User.decoder() and User.encoder(). They are exactly the code you would write by hand, as the Gleam tab shows.

The compiler knows no format. What a derived function does comes from the deriver, the imported module that @derive names: an ordinary Bea (or Gleam) module that exports record, field and done, and a function per type a field can have (string, int, list, option, …). The bea_json/decoder.bea tab is a complete one, and its decoders are the standard library's decode.Decoders.

A field whose type is another record uses that type's function (address_decoder()). You get only what you ask for: a type that should never be serialised simply doesn't derive an encoder.

Open in playground
//// `@derive` writes functions that follow a record's fields, like JSON
//// decoders and encoders. What they do comes from a deriver: an ordinary
//// module, here `bea_json/decoder` and `bea_json/encoder`.

import bea_json/decoder
import bea_json/encoder

@derive(decoder, encoder)
pub type Address { street: String, city: String }

@derive(decoder, encoder)
pub type User {
  name: String,
  age: Int,
  tags: List(String),
  address: Option(Address),
}

pub def main() {
  json = `{"name": "Ada", "age": 36, "tags": ["admin"],
    "address": {"street": "1 Hive Lane", "city": "Kanpur"}}`
  Ok(user) = decoder.from_json(json, User.decoder()) else panic
  echo user
  // A field of the wrong type: the error says which one.
  Error(problem) = decoder.from_json(
    `{"name": "Bea", "age": "two"}`,
    User.decoder(),
  ) else panic
  echo problem
  echo encoder.to_string(User(user | address: None), User.encoder())
}

This lesson's code is a module of a small Bea project, compiled and run by bea for this page: content/features/src/deriving.bea