// IO monad: wraps a thunk (deferred computation)
struct IO<A> {
  private let effect: () -> A

  init(_ effect: @escaping () -> A) {
    self.effect = effect
  }

  // Transform the result without executing
  func map<B>(_ f: @escaping (A) -> B) -> IO<B> {
    IO<B> { f(self.effect()) }
  }

  func unsafeRun() -> A {
    effect()
  }
}

func rollDiceImpure() -> Int {
  return Int.random(in: 1...6)
}

func rollDice() -> IO<Int> {
  IO { rollDiceImpure() }
}

func allowedToLeaveHome() -> IO<Bool> {
  rollDice().map { $0 == 6 }
}
