// Higher-kinded types are not supported in MoonBit traits, so we remove this trait definition for now.

/// State Monad 满足以下法则:
/// 1. 左单位元: pure(a).bind(f) == f(a)
/// 2. 右单位元: m.bind(pure) == m
/// 3. 结合律: m.bind(f).bind(g) == m.bind(fn(x) { f(x).bind(g) })

///|
struct State[S, V] {
  runState : (S) -> (V, S)
}

///|
pub fn[S, V] State::state(f : (S) -> (V, S)) -> State[S, V] {
  { runState: f, }
}

///|
pub fn[S, V] State::new(val : V) -> State[S, V] {
  { runState: fn(state : S) -> (V, S) { (val, state) }, }
}

///|
pub fn[S, V] State::run_state(self : State[S, V], state : S) -> (V, S) {
  (self.runState)(state)
}

///|
pub fn[S, V] State::eval_state(self : State[S, V], state : S) -> V {
  State::run_state(self, state).0
}

///|
pub fn[S, V] State::exec_state(self : State[S, V], state : S) -> S {
  State::run_state(self, state).1
}

///|
pub fn[S, V, A] State::bind(
  self : State[S, V],
  f : (V) -> State[S, A],
) -> State[S, A] {
  {
    runState: fn(state : S) {
      let (val, new_state) = (self.runState)(state)
      (f(val).runState)(new_state)
    },
  }
}

///|
// pub fn [S, V, W] State::map(self : State[S, V], f : (V) -> W) -> State[S, W] {
//   {
//     runState: fn(state : S) {
//       let (val, new_state) = (self.runState)(state)
//       (f(val), new_state)
//     },
//   }
// }
pub fn[S, V, W] State::map(self : State[S, V], f : (V) -> W) -> State[S, W] {
  self.bind(fn(val) { State::new(f(val)) })
}

///|
pub fn[S, V, W] State::apply(
  self : State[S, (V) -> W],
  arg : State[S, V],
) -> State[S, W] {
  self.bind(fn(f) { arg.map(f) })
}

///|
pub fn[S] State::modify(f : (S) -> S) -> State[S, Unit] {
  { runState: fn(state : S) { ((), f(state)) }, }
}

///|
pub fn[S] State::get() -> State[S, S] {
  { runState: fn(state : S) -> (S, S) { (state, state) }, }
}

///|
pub fn[S] State::put(new_state : S) -> State[S, Unit] {
  { runState: fn(_ : S) -> (Unit, S) { ((), new_state) }, }
}

///|
/// 将 Array[State[S, A]] 转为 State[S, Array[A]]
pub fn[S, A] State::sequence(states : Array[State[S, A]]) -> State[S, Array[A]] {
  fn fold_fn(
    acc : State[S, Array[A]],
    state : State[S, A],
  ) -> State[S, Array[A]] {
    acc.bind(fn(arr) { state.map(fn(a) { arr + [a] }) })
  }
  states.fold(init=State::new([]), fold_fn)
}

///|
/// 将 Array[A] 映射为 State[S, B] 后 sequence
pub fn[S, A, B] State::traverse(
  arr : Array[A],
  f : (A) -> State[S, B],
) -> State[S, Array[B]] {
  State::sequence(arr.map(f))
}