// This mimics the Bow library where they emulate HKTs with `Kind`

class Kind<F, A> {}

protocol Mappable {
  static func map<A, B>(
    _ fa: Kind<Self, A>,
    _ f: @escaping (A) -> B
  ) -> Kind<Self, B>
}

final class ForArray: Mappable {
  static func map<A, B>(
    _ fa: Kind<ForArray, A>,
    _ f: @escaping (A) -> B
  ) -> Kind<ForArray, B> {
    ArrayK(fa.fix().value.map(f))
  }
}

final class ArrayK<A>: Kind<ForArray, A> {
  let value: [A]
  init(_ value: [A]) { self.value = value }
}

extension Kind where F == ForArray {
  // CAN FAIL, NOT ACTUALLY SAFE
  func fix() -> ArrayK<A> { self as! ArrayK<A> }
}

final class ForOptional: Mappable {
  static func map<A, B>(
    _ fa: Kind<ForOptional, A>,
    _ f: @escaping (A) -> B
  ) -> Kind<ForOptional, B> {
    OptionalK(fa.fix().value.map(f))
  }
}

final class OptionalK<A>: Kind<ForOptional, A> {
  let value: A?
  init(_ value: A?) { self.value = value }
}

extension Kind where F == ForOptional {
  // CAN FAIL, NOT ACTUALLY SAFE
  func fix() -> OptionalK<A> { self as! OptionalK<A> }
}

final class ForTask: Mappable {
  static func map<A, B>(
    _ fa: Kind<ForTask, A>,
    _ f: @escaping (A) -> B
  ) -> Kind<ForTask, B> {
    TaskK(Task<B, Error> {
      let a = try await fa.fix().value.value
      return f(a)
    })
  }
}

final class TaskK<A>: Kind<ForTask, A> {
  let value: Task<A, Error>
  init(_ value: Task<A, Error>) { self.value = value }
}

extension Kind where F == ForTask {
  // CAN FAIL, NOT ACTUALLY SAFE
  func fix() -> TaskK<A> { self as! TaskK<A> }
}

final class ForResult: Mappable {
  static func map<A, B>(
    _ fa: Kind<ForResult, A>,
    _ f: @escaping (A) -> B
  ) -> Kind<ForResult, B> {
    ResultK(fa.fix().value.map(f))
  }
}

final class ResultK<A>: Kind<ForResult, A> {
  let value: Result<A, Error>
  init(_ value: Result<A, Error>) { self.value = value }
}

extension Kind where F == ForResult {
  // CAN FAIL, NOT ACTUALLY SAFE
  func fix() -> ResultK<A> { self as! ResultK<A> }
}

func add4Inside<F: Mappable>(_ fa: Kind<F, Int>) -> Kind<F, Int> {
    F.map(fa) { $0 + 4 }
}

add4Inside(ArrayK([1,2,3,4])).fix().value //> [5,6,7,8]
let opt: Int? = 4
add4Inside(OptionalK(opt)).fix().value //> Optional(8)

Explanation:

// The pattern here is a lot more verbose, and less safe, than GAT and HKTs.
// While in the HKT to implement Mappable just required:

extension Array: Mappable<Array> {
  static func map<A, B>(_ fa: Array<A>, _ f: (A) -> B) -> Array<B> {
    fa.map(f)
  }
}

// this instead requires:

final class ForArray: Mappable {
    static func map<A, B>(
        _ fa: Kind<ForArray, A>, 
        _ f: @escaping (A) -> B
    ) -> Kind<ForArray, B> {
        ArrayK(fa.fix().value.map(f))
    }
}

final class ArrayK<A>: Kind<ForArray, A> {
    let value: [A]
    init(_ value: [A]) { self.value = value }
}

extension Kind where F == ForArray {
    func fix() -> ArrayK<A> { self as! ArrayK<A> }
}

// More important than all the extra verbosity, `fix` is actually unsafe. 
// We could use it on an ArrayK and it would be fine, 
// but we could just as easily implement some other Kind<ForArray, A>` class.
// Then fix() would fail on that new object.