Chapter 7: Records, guards and deriving
Functions through their type
- Type.name()
- hand-written decoders
A type's functions are named after it,
<type>_<name>, and Type.name reaches
them through the type: Account.decoder() is
account_decoder(), and Role.decoder() is
role_decoder(). Without the parentheses,
Role.decoder is the function itself.
Only records can be derived, so the union Role gets a
decoder written by hand. Because it is named the same way, it fits right
in: the derived account_decoder uses it for the
role field.
This is naming, not methods. The Gleam is the same as with the name
written out, so Gleam code calls account_decoder() as
always.
//// A type's functions are named `<type>_<name>`, and Bea code can reach
//// them through the type: `Account.decoder()` is `account_decoder()`.
import bea_json/decoder
pub type Role {
Admin
Member
}
/// Only records are derived. A union's decoder is written by hand, and is
/// still `Role.decoder()`; derived records with a `Role` field use it.
pub def role_decoder() -> decode.Decoder(Role) {
use role <- decode.then(decode.string)
case role {
"admin" -> decode.success(Admin)
"member" -> decode.success(Member)
_ -> decode.failure(Member, "Role")
}
}
@derive(decoder)
pub type Account { email: String, role: Role }
pub def main() {
json = `{"email": "ada@example.com", "role": "admin"}`
Ok(account) = decoder.from_json(json, Account.decoder()) else panic
echo account
Error(problem) = decoder.from_json(`"guest"`, Role.decoder()) else panic
echo problem
// The function itself, as a value:
parse_role = decoder.from_json(_, Role.decoder())
echo list.map([`"admin"`, `"member"`], parse_role)
}//// A type's functions are named `<type>_<name>`, and Bea code can reach
//// them through the type: `Account.decoder()` is `account_decoder()`.
import bea_json/decoder
import gleam/dynamic/decode
import gleam/list
pub type Role {
Admin
Member
}
/// Only records are derived. A union's decoder is written by hand, and is
/// still `Role.decoder()`; derived records with a `Role` field use it.
pub fn role_decoder() -> decode.Decoder(Role) {
use role <- decode.then(decode.string)
case role {
"admin" -> decode.success(Admin)
"member" -> decode.success(Member)
_ -> decode.failure(Member, "Role")
}
}
pub type Account {
Account(email: String, role: Role)
}
/// Derived by `@derive(decoder)` on `Account`.
pub fn account_decoder() {
use <- decoder.record("Account")
use email <- decoder.field(
"email",
decoder.string(),
fn(value: Account) { value.email },
)
use role <- decoder.field(
"role",
role_decoder(),
fn(value: Account) { value.role },
)
decoder.done(Account(email: email, role: role))
}
pub fn main() {
let json = "{\"email\": \"ada@example.com\", \"role\": \"admin\"}"
case decoder.from_json(json, account_decoder()) {
Ok(account) -> {
echo account
case decoder.from_json("\"guest\"", role_decoder()) {
Error(problem) -> {
echo problem
let parse_role = decoder.from_json(_, role_decoder())
echo list.map(["\"admin\"", "\"member\""], parse_role)
}
_ -> panic
}
}
_ -> panic
}
}src/type_functions.bea:28
Account("ada@example.com", Admin)
src/type_functions.bea:30
UnableToDecode([DecodeError("Role", "String", [])])
src/type_functions.bea:33
[Ok(Admin), Ok(Member)]This lesson's code is a module of a small Bea project, compiled and run by bea for this page: content/features/src/type_functions.bea