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

Consider the more general "function class" pattern #34

Closed
domenic opened this issue Jul 3, 2015 · 6 comments
Closed

Consider the more general "function class" pattern #34

domenic opened this issue Jul 3, 2015 · 6 comments

Comments

@domenic
Copy link
Member

domenic commented Jul 3, 2015

A continuation of #11, with more focus, since that was cleaned up. As discussed there, observables are an instance of the "function class" pattern, specifically for functions of the signature { next, return, throw } -> { unsubscribe }.

Function class is to me a more compelling general pattern to consider introducing into the language than the specific instance of observables. For example, a function class built around Promise -> void would allow lazy transformations on single asynchronous values.

I'd like this proposal to give more consideration to how it fits with the general function class idea. For example, it seems likely that Observable should be a subclass of Function. That would also help avoid any bikeshedding over [Symbol.observe] vs. subscribe or similar.

@benlesh
Copy link

benlesh commented Jul 8, 2015

@domenic I need to more elaborate straw-man to understand what you're suggesting. Can you type up an example?

@trxcllnt
Copy link

trxcllnt commented Jul 8, 2015

@Blesh I remember mentioning a potential Function.create to @domenic in New York last year, and this sounds like the same idea. Essentially, something like:

// polyfill
Function.create = (proto, body) => {
    let newFn = (...args) => { return body(...args); };
    for(var key in proto) {
        if(proto.hasOwnProperty(key)) {
            newFn[key] = proto[key];
        }
    }
    return newFn;
};

let randomNumbersObs = Function.create(Observable.prototype, (observer) => {
    let id = setInterval(() => observer.next(Math.random()), 100);
    return _ => clearInterval(id);
});

let dispose = randomNumbersObs.map((i) => { value: i })({
    next(x) { console.log(x.value); }
});

@benlesh
Copy link

benlesh commented Jul 8, 2015

that is pretty interesting. As an orthogonal proposal, I think that's useful.

The real problem is that everyone would have to reimplement the guarantees of Observable. There is some inherent behavior in observable that pretty much requires it be a type.

@benjamingr
Copy link

@Blesh well, it relates to observables (and other types) in interesting ways.

    class Observable extends Function { // the same way a generator is a function
        constructor(fn) { ... }
        map(fn) { ... }
        ...
        call(subscriber){
             // this is `.subscribe`
        }
    }

Which would make the API look like:

 var o = myObservableReturningFn.map(..).filter(...);

 o(v => console.log(v));

Instead of:

var o = myObservableReturningFn(...).map(..).filter(...);
o.subscribe(v => console.log(v));

The interesting thing is that because a function class is just an implementation of the continuation comonad we can apply this "non-strictness at invocation" to anything, functions returning arrays, promises or whatever. The limitation is that this comonad needs to be applied on a functor itself. We are basically building and composing functions here based on the return type getting an interesting signature of:

-- takes a function to `a` and return a function to `a` which is also an `a`
bind:: (() -> a) -> (a & (() -> a))

With a sufficiently clever typesystem this would be possible, in JS I don't think it is. We can settle for particular instances or "applying this pattern in the language". So the laziness is in not calling the function and not in an implicit laziness contract.

@zenparsing
Copy link
Member

I think the method-wrapper is important for readability, at least for Observable:

getObservable()(); // Looks weird
getObservable().subscribe(); // Better

@trxcllnt
Copy link

@zenparsing getObservable().call() is still an option, and you could implement subscribe to call under the hood.

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

5 participants