Ship

Book · Field modes

Field modes

Draft

Pin a stored slot's mode, or share one cut across a type parameter.

On this page · 4 sections

One type

A mode is a fact about a value. It is not another type argument. Cache(String) and Cache(String) @ global are the same cache.

Deep slots

A bare field or payload follows the value that holds it. Leave the slot alone when that is what you want.

type Pair('a) = {
  left: 'a;
  right: 'a;
}

Deep Pair

test✓
  let pairpair: Book.LanguageReference.FieldModes.Pair(String) = Pair { left = "a""a": String; right = "b""b": String; };
  expect ✓(pairpair: Book.LanguageReference.FieldModes.Pair(String).leftleft: String, pairpair: Book.LanguageReference.FieldModes.Pair(String).rightright: String) == ("a""a": String, "b""b": String);

One slot

@ on a stored slot cuts that axis off the container. Other slots still follow the value. Variant payloads use the same annotation. A function payload needs parentheses, because @ binds tighter than ->. type! opts this variant out of Decode; a function payload has nothing to decode.

type Kept('a) = {
  held: 'a @ global;
  scratch: 'a;
}

type! Job =
  | Now((fn() -> Int) @ spawnable)
  | Later { at: String @ global; }

Cut Slots

test✓
  let keptkept: Book.LanguageReference.FieldModes.Kept(String) = Kept { held = "ada""ada": String; scratch = "note""note": String; };
  let jobjob: Book.LanguageReference.FieldModes.Job = NowNow: fn(fn() -> Int) -> Book.LanguageReference.FieldModes.Job(() => 22: Int);
  let ranran: Int = match jobjob: Book.LanguageReference.FieldModes.Job {
    | Now(runrun: fn() -> Int) -> runrun: fn() -> Int()
    | Later { ..; } -> 00: Int
  };
  expect ✓(keptkept: Book.LanguageReference.FieldModes.Kept(String).heldheld: String, keptkept: Book.LanguageReference.FieldModes.Kept(String).scratchscratch: String, ranran: Int) == ("ada""ada": String, "note""note": String, 22: Int);

Every occurrence of a parameter

When every stored 'a shares one cut, write it on the parameter. A slot may repeat that mode. It may not pick a different one. Leave the parameter bare when two slots of 'a differ.

type Cache('a @ global) = {
  value: 'a;
}

Parameter Cut

test✓
  let cachedcached: Book.LanguageReference.FieldModes.Cache(String) = Cache { value = "hello""hello": String; };
  ✓cachedcached: Book.LanguageReference.FieldModes.Cache(String): Cache(String);
  expect ✓cachedcached: Book.LanguageReference.FieldModes.Cache(String).valuevalue: String == "hello""hello": String;

Repeating the parameter's mode at a use site is the same type. The annotation does not add a cut the definition does not already have.

Restated Cut

test✓
  let cachedcached: Book.LanguageReference.FieldModes.Cache(String): Cache(String @ global) = Cache { value = "again""again": String; };
  expect ✓cachedcached: Book.LanguageReference.FieldModes.Cache(String).valuevalue: String == "again""again": String;

A stored slot takes a concrete mode. A mode variable names a region each call picks, so it belongs on a function's parameters and result.