Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support joining Promises of different types #14

Closed
juri opened this issue Mar 4, 2017 · 4 comments
Closed

Support joining Promises of different types #14

juri opened this issue Mar 4, 2017 · 4 comments

Comments

@juri
Copy link
Contributor

juri commented Mar 4, 2017

Promise.all only works with Promises of one type. It would be nice to have similar helpers for Promises of different types, too. I think the only way to accomplish it is to have a separate method for each arity. I didn't know if you're interested in including a set of methods like that, so I decided to start by filing an issue instead of going directly for a PR.

I solved it like this:

    public static func join<T1, T2>(_ p1: Promise<T1>, _ p2: Promise<T2>) -> Promise<(T1, T2)> {
        return Promise<(T1, T2)>(work: { fulfill, reject in
            p1.then({ value in
                if let p2val = p2.value {
                    fulfill((value, p2val))
                }
            }).catch(reject)
            p2.then({ value in
                if let p1val = p1.value {
                    fulfill((p1val, value))
                }
            }).catch(reject)
        })
    }

    public static func join<T1, T2, T3>(_ p1: Promise<T1>, _ p2: Promise<T2>, _ last: Promise<T3>) -> Promise<(T1, T2, T3)> {
        return Promise<(T1, T2, T3)>(work: { fulfill, reject in
            let joined = join(p1, p2)

            func callFulfill(_ joinedVal: (T1, T2), _ lastVal: T3) -> Void {
                fulfill((joinedVal.0, joinedVal.1, lastVal))
            }

            joined.then({ value in
                if let lastval = last.value {
                    callFulfill(value, lastval)
                }
            }).catch(reject)
            last.then({ value in
                if let jval = joined.value {
                    callFulfill(jval, value)
                }
            }).catch(reject)
        })
    }

I generated the three parameter version with this:

func types(_ n: Int) -> String {
    return (1...n).map { "T\($0)" }.joined(separator: ", ")
}


func createJoin(_ n: Int) -> String {
    var output: String = "public static func join<"
    output.append(types(n))
    output.append(">(")
    output.append((1...n-1).map { "_ p\($0): Promise<T\($0)>, " }.joined())
    output.append("_ last: Promise<T\(n)>) -> Promise<(\(types(n)))> {\n")

    output.append("return Promise<(\(types(n)))>(work: { fulfill, reject in\n")

    output.append("let joined = join(")
    output.append((1...n-1).map { "p\($0)" }.joined(separator: ", "))
    output.append(")\n\n")

    output.append("func callFulfill(_ joinedVal: (\(types(n-1))), _ lastVal: T\(n)) -> Void {\n")

    output.append("fulfill((")
    output.append((0...n-2).map { "joinedVal.\($0), " }.joined())
    output.append("lastVal))\n")
    output.append("}\n\n")

    ["joined.then({ value in",
     "if let lastval = last.value {",
     "callFulfill(value, lastval)",
     "}",
     "}).catch(reject)",
     "last.then({ value in",
     "if let jval = joined.value {",
     "callFulfill(jval, value)",
     "}",
     "}).catch(reject)"].forEach { line in
        output.append(line)
        output.append("\n")
    }

    output.append("})\n}")

    return output
}

print(createJoin(3))

So you could use that to generate methods for any arity N ≥ 3, as long as you also had generated ones for 3…N.

Should I file a pull request with something based on this?

@danielt1263
Copy link

The above is how it's done in, for example, RxSwift. They have a file with functions that allow up to 8 parameters.

@khanlou
Copy link
Owner

khanlou commented Mar 5, 2017

I like your code gen function!

In this library, what you call join is called zip. Currently, it only allows two parameters, but we could add higher arity versions.

The changes that I'm thinking about making to zip are a) removing the and parameter name, and b) moving to a new enum namespace named Promises, so that you dont have to specify a generic type that isnt used anywhere

@juri
Copy link
Contributor Author

juri commented Mar 5, 2017

How did I manage to miss that? zip seems kind of obvious in retrospect, but I managed to scroll right past it when browsing the source. Oh well. I'll rework the code gen to work with it.

@juri juri mentioned this issue Mar 5, 2017
@juri
Copy link
Contributor Author

juri commented Mar 12, 2017

This was partially invalid and the rest was implemented in #15. Closing.

@juri juri closed this as completed Mar 12, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants