protocol HTraverse {
    associatedtype Source: HList
   
    func traverse(_ f: () -> Void) -> HNil
}

extension HCons: HTraverse where T: HTraverse {
    typealias Source = HCons<H, T>
   
    func traverse(_ f: () -> Void) -> HNil {
        f()
        return self.tail.traverse(f)
    }
}

extension HNil: HTraverse {
    typealias Source = HNil
   
    public func traverse(_ f: () -> Void) -> HNil {
        f()
        return HNil()
    }
}

public protocol Providable {
    associatedtype S
    
    static func provide() -> State<S, Self>
}

precedencegroup Right {
    associativity: right
}

infix operator >>>: Right

public func >>><S, A, T: HList>(_ ma: State<S, A>, _ mb: State<S, T>) -> State<S, HCons<A, T>> {
    return ma.flatMap {
        (a: A) -> State<S, HCons<A, T>> in
            mb.map {
                (t: T) -> HCons<A, T> in HCons<A, T>(a, t)
            }
    }
}

public func end<S>() -> State<S, HNil> {
    return State<S, HNil> {
        s -> (HNil, S) in (HNil(), s)
    }
}

extension Array: Providable where Element: Providable, Element.S == Int {
    public typealias S = Int
    
    public static func provide() -> State<Int, Array<Element>> {
        return Element.provide().map { e -> Array<Element> in [e] }
    }
}

extension Bool: Providable {
    public typealias S = Int
    
    public static func provide() -> State<Int, Bool> {
        return State<Int, Bool> {
            s -> (Bool, Int) in
                if (s % 2 == 0) {
                    return (true, s + 1)
                } else {
                    return (false, s + 1)
                }
        }
    }
}

extension Int: Providable {
    public typealias S = Int
    
    public static func provide() -> State<Int, Int> {
        return State<Int, Int>{
            (s: Int) -> (Int, Int) in (s, s + 1)
        }
    }
}

extension Optional: Providable where Wrapped: Providable, Wrapped.S == Int {
    public typealias S = Int
    
    public static func provide() -> State<Int, Optional<Wrapped>> {
        return State<Int, Optional<Wrapped>> {
            s -> (Optional<Wrapped>, Int) in
                if (s % 2 == 0) {
                    return Wrapped.provide().map { w -> Optional<Wrapped> in .some(w) }.run(s)
                } else {
                    return (.none, s + 1)
                }
        }
    }
}

extension String: Providable {
    public typealias S = Int
    
    public static func provide() -> State<Int, String> {
        return State<Int, String> {
            (s: Int) -> (String, Int) in (String(s), s + 1)
        }
    }
}

public struct State<S, A> {
    public let run : (S) -> (A, S)
    
    public init(_ run : @escaping (S) -> (A, S)) {
        self.run = run
    }
    
    func eval(_ s: S) -> A {
        let (a, _) = self.run(s)
        return a
    }
    
    func flatMap<B>(_ f: @escaping (A) -> State<S, B>) -> State<S, B> {
        return State<S, B> {
            (s: S) -> (B, S) in
                let (a, s1) = self.run(s)
                return f(a).run(s1)
        }
    }
    
    func map<B>(_ f: @escaping (A) -> B) -> State<S, B> {
        return State<S, B> {
            (s: S) -> (B, S) in
                let (a, s1) = self.run(s)
                return (f(a), s1)
        }
    }
}

public protocol HList { }

public struct HNil: HList {
    init() { }
    
    static let hNil = HNil()
}

public struct HCons<H, T: HList>: HList {
    public let head: H
    public let tail: T

    public init(_ h: H, _ t: T) {
        self.head = h
        self.tail = t
    }
}

let a =
    String.provide() >>>
    Bool.provide() >>>
    Bool.provide() >>>
    Int.provide() >>>
    Optional<Int>.provide() >>>
    String.provide() >>>
    Bool.provide() >>>
    Bool.provide() >>>
    Int.provide() >>>
    Optional<Int>.provide() >>>
    end()

var counter = 0

let _ = a.eval(0).traverse { () -> Void in
    print(counter)
    counter = counter + 1
}
