Chapter 4 Types and optionals
4
Chapter 4

Types and optionals

Silk types should make the shape of the data easy to inspect. Use named structs for records, scalar types at boundaries, and optionals when absence is expected.

Name the data shape

A struct gives related fields one concrete type.

src/profile.slk
package profile;

struct User {
  id: u64;
  name: string;
  email: string?;
}

fn display_name (user: &User) -> string {
  return user.name;
}

Return optional data deliberately

User? says a lookup can miss without turning that miss into a failure.

src/profile.slk
fn find_user (id: u64) -> User? {
  if id == 1 {
    return Some(User{
      id: 1,
      name: "Ada",
      email: Some("ada@example.com"),
    });
  }

  return None;
}

Handle the optional at the call site

Use a fallback when that is all the caller needs.

src/profile.slk
fn email_label (id: u64) -> string {
  let user: User? = find_user(id);

  match user {
    Some(value) => {
      return value.email ?? "no email";
    },
    None => {
      return "missing user";
    },
  }
}

Check before building

The type checker should catch mismatched field, optional, and return shapes early.

Terminal
silk check src/profile.slk

Use optionals for normal absence. When callers need to know why something failed, move to a result shape instead of overloading None.

What changed

  • User made the record shape inspectable.
  • string? made a missing email explicit.
  • User? modeled a lookup miss as normal data.
  • match handled the optional before using the payload.

Chapter complete

Next, distinguish recoverable domain failures from typed contract errors.

Continue to results and errors