Chapter 4: Functions as values

Go

  • Result
  • result.try
  • record update

Rules return Result(Game, String): either an updated game or a reason the move is illegal. result.map and result.try chain them so the first error stops the rest.

Anonymous functions can annotate their parameters too: fn(g: Game) { … }.

Open in playground
import gleam/result

pub type Player {
  Black
  White
}

pub type Game {
  Game(
    white_captured_stones: Int,
    black_captured_stones: Int,
    player: Player,
    error: String,
  )
}

def apply_rules(
  game: Game,
  rule1: fn(Game) -> Result(Game, String),
  rule2: fn(Game) -> Game,
  rule3: fn(Game) -> Result(Game, String),
  rule4: fn(Game) -> Result(Game, String),
) {
  outcome =
    game
    |> rule1()
    |> result.map(rule2)
    |> result.try(rule3)
    |> result.try(rule4)

  case outcome {
    Ok(updated_game) -> {
      updated_player = case updated_game.player {
        Black -> White
        _ -> Black
      }

      Game(updated_game | player: updated_player)
    }

    Error(s) -> Game(game | error: s) 
  }
}

pub def main() {
  initial_game = Game(
    white_captured_stones: 0,
    black_captured_stones: 0,
    player: Black,
    error: "",
  )

  r1 = fn(g: Game) { Ok(g) }
  r2 = fn(g: Game) { Game(g | white_captured_stones: 2) }
  r3 = fn(g: Game) { Ok(g) }
  r4 = fn(g: Game) { Ok(g) }

  echo apply_rules(initial_game, r1, r2, r3, r4)
}

An exercism exercise from the Gleam track, solved in Bea: beacode/src/e16_go.bea