// SPDX-License-Identifier: MPL-2.0
// Warmup 03: Algebraic effects
//
// Effects are AffineScript's unified model for anything "impure":
// I/O, state, exceptions, async, randomness, etc.
//
// The key idea: effects are declared in return types, not thrown sideways.
// The compiler knows every effect a function can perform.
//
// Run: affinescript check 03_effects.affine

// ── Declaring an effect ───────────────────────────────────────────────────────

effect IO {
  fn print(s: String);
  fn println(s: String);
  fn read_line() -> String;
}

// ── Using an effect ───────────────────────────────────────────────────────────
//
// The `/ EffectName` in the return type declares which effects are used.
// No `perform` keyword — just call the operation directly (ADR-008).

fn greet(name: ref String) -> () / IO {
  println("Hello, " ++ name ++ "!")
}

// Multiple effects are composed with `+`:

effect Exn[E] {
  fn throw(err: E) -> Never;
}

type IOError = { message: String }

fn read_line_safe() -> Result[String, IOError] / IO + Exn[IOError] {
  let line = IO.read_line();
  if line == "" {
    Exn.throw({ message: "empty input" })
  } else {
    Ok(line)
  }
}

// ── Effect polymorphism ───────────────────────────────────────────────────────
//
// Functions can be polymorphic over effects — they propagate whatever effects
// their argument functions use.

fn map_list[A, B, ..e](
  xs: ref Array[A],
  f: (ref A) -> B / e
) -> Array[B] / e {
  // Implementation would map f over xs
  []
}

// ── Parameterised effects ─────────────────────────────────────────────────────

effect State[S] {
  fn get() -> S;
  fn put(s: S) -> ();
}

effect Async {
  fn await[T](promise: Promise[T]) -> T;
  fn spawn[T](f: () -> T) -> Promise[T];
}

// ── Exercise 1: write a function that reads two lines and returns ──────────────
//    a tuple of them.  What effects does it declare?
// fn read_pair() -> (String, String) / ??? { ... }

// ── Exercise 2: write a counter using State[Int] that increments by n ─────────
//    and returns the new value.
// fn increment(n: Int) -> Int / State[Int] { ... }
