import Cocoa

enum Failable<T> {
  case fail
  case success(T)
  
  func map<U>(fn: (T) -> U) -> Failable<U> {
    switch self {
    case .fail: return .fail
    case let .success(t): return .success(fn(t))
    }
  }
  
  func flatMap<U>(fn: (T) -> Failable<U>) -> Failable<U> {
    switch self {
    case .fail: return .fail
    case let .success(t): return fn(t)
    }
  }
}

func repeatedlyAttempt<T>(attempts: Int, fn: () -> Failable<T>)  -> Failable<T> {
  guard attempts > 0 else { return .fail }
  
  let attempted = fn()
  switch attempted {
  case .success(_): return attempted
  case .fail: return repeatedlyAttempt(attempts: attempts - 1, fn: fn)
  }
}

func randomlyFail<T>(fn: () -> T) -> Failable<T> {
  return (arc4random_uniform(2) == 1) ? .success(fn()) : .fail
}

func doThing(i: Int) -> String {
  return "\(i)"
}

func reverseDoThing(s: String) -> Int {
  return Int(s)!
}

let failableDoThing = { i in randomlyFail(fn: { doThing(i: i) }) }
let failableReverseDoThing = { s in randomlyFail(fn: { reverseDoThing(s: s) }) }


repeatedlyAttempt(attempts: 5){ failableDoThing(10) }
  .flatMap{s in repeatedlyAttempt(attempts: 5){ failableReverseDoThing(s) } }