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

Is this proposal still alive? #44

Closed
mlanza opened this issue May 5, 2023 · 12 comments
Closed

Is this proposal still alive? #44

mlanza opened this issue May 5, 2023 · 12 comments

Comments

@mlanza
Copy link

mlanza commented May 5, 2023

I haven't noticed recent activity. Wanting protocols so bad that I wrote my entire library around them, I am seriously interested in seeing this progress. I'd much rather have proper protocols in JavaScript (for performance and syntax reasons) than using my own custom implementations.

I saw this was approved for Stage 1. Any idea when it might appear again on a T39 agenda so that it can continue forward?

I'm just very interested in the outcome. Thank you.

@michaelficarra
Copy link
Member

This proposal is not abandoned. I still plan to actively champion it within TC39. Initially, I was planning to build support for it into a compiler like Babel and get usage feedback before asking for advancement to stage 2, but over time I've become convinced that the design is good, and we don't actually need that kind of feedback. What is currently keeping me from bringing the proposal back for advancement to stage 2 is motivation/demand from the community. We obviously don't want to add something that isn't going to be used. I feel like it's a chicken/egg problem, that if we had protocols, the community would use them and integrate against them, but it's hard to demonstrate that until they exist. I will probably have to talk with maintainers of libraries that would benefit from protocols to see what they think. Feedback like yours is appreciated. The more detail you can provide about your use case, the more it will help.

@mlanza
Copy link
Author

mlanza commented May 9, 2023

I'm really glad to hear this and I really, really appreciate your championing this.

Clojure has inspired my programming. I wanted to use it in browserland but I didn't want to endure ClojureScript transpilation since the resulting code is unpleasant and large. So I just ported the necessary ideas straight into JavaScript, including protocols. You can't port the Clojure mindset into a language/environment which lacks protocols. The Clojure approach—and it's exceptional!—necessitates them, as does nil-punning (#43).

The first thing about Clojure is how convenient having a set of 100 functions you can use almost all the time for dealing with things which are collection like. This has been oft cited:

It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.

And I have found it true in JavaScript because of protocols. Protocols give the appearance that similar data structures are one in the same, thus gaining the 100 functions.

Once I started coding my domain as data structures and using all the Clojure protocols in JavaScript it just made programming a pleasure. Anyone who has used Clojure, I'm sure, could attest the same.

The thing about a lacking feature is if there's a way to do something with some other feature, the community will use what's available. Well, protocols aren't presently an option in JavaScript. What they have instead are interfaces and types a la TypeScript and they're nice. But they're not protocols. So devs use what's at hand.

Interfaces are OOP. Protocols FP.

With interfaces you call the method on the object which abides the interface. Even lacking interfaces, JavaScript always had duck typing. But what I don't like is there are no guarantees that two objects which expose a like-named method are exposing the same behavior. It's likely, yes, but not enforced.

That's not the case with protocols. Protocols implement against a standalone function object like Functor.map and they're invoked from it. So they're safe, contractual guarantees. Two vendors could define Functor.map and each might have nuanced differences which make them incompatible. And this is no issue with protocols because each will have defined their own protocol (although they are named identically) in a safe way.

Or to put it another way, interfaces make the object the subject whereas protocols make the function the subject. I much prefer the latter.

JavaScript initially modeled itself after an OOP language, but it has been continuing to gain tooling well-suited to the FP mindset: records/tuples, partial application syntax, pipeline operator, etc. People want to do FP in JavaScript.

I implemented my own features in my own library to make this possible, but I am not as skilled as a language implementer to optimize all this for under-the-hood speed. I'd much rather have the things I've build become first-class citizens in the language.

Well, records/tuples are on their way. Soon later, hopefully, they get typed records/tuples because in tandem with protocols it provides great tooling for using the functional core, imperative shell pattern in JavaScript. These are central to the goodness Clojure provides!

The other beauty of protocols is how they have you stop thinking directly about types. Rather you're focus is on the behaviors the objects in your system implement. Everything plays 1 or more roles in the system. They yield certain behaviors. That's the focus. Then, as desired, you swap one object for another.

For example, in some instances I have a vanilla object, then I run a functionally pure operation on it returning a replacement object. Only that replacement object is no longer just a vanilla object, but another type. That type is optimized for the transformation. The dev need not care that the type morphed. It still represents the same entity and plays the same fixed role in the system irrespective of that. Such an entity is effectively a state machine and/or a union type.

Protocols ease the integration of third-party types into your app. With protocols you can abstract away the differences in how libraries do things. I took ImmutableJS and wrapped it so that it's maps, vectors, and sets seamlessly integrated. That is, all the code has a unified, harmonious flow it. It's not like you code against one library using style A and another style B. Everything is style A! Protocols make that possible.

People will use protocols. Of that I have no doubt. They're too good to be ignored. They just haven't had a chance to rely on them in JavaScript because they don't exist.

And you're always going to have factions. Some people will keep to types and interface and TypeScript. And some will delight in protocols. So you'll never get everyone. But you'll likely get the majority of FP programmers since protocols are much more FP oriented.

I will watch this proposal closely and provide feedback as I can. I have a lot of experience with protocols in JavaScript.

@mlanza
Copy link
Author

mlanza commented May 9, 2023

I reread your comment. You mention about talking to maintainer of libraries to see/if how they might use protocols. Protocols work well for use in generic libraries certainly, but I also believe they are exceptionally useful for the immutable types (records/tuples and eventually typed records/tuples) which represent one's domain types (e.g. Person, Student, Teacher, Class, Course, etc.), and I usually prefer to model those within my pure functional core.

So that's a use case that those library maintainers are unlikely to be thinking about.

@mlanza
Copy link
Author

mlanza commented May 9, 2023

More fuel:

Elm implements types. They're union types, ADTs. When you implement functions against them, the union necessitates an appropriate implementation of the function against each of the united types. You can't forget to do it because the compiler will complain.

I'm not sure how obvious it is to programmers, but the ADTs of Elm and Haskell and other functional languages are almost identical in practice to protocols. The approaches are compatible in what one can achieve, with the subtle differences based mostly on compilation.

You don't get the compile-time checking with protocols, but you gain dynamic type inclusion. A running program can extend a protocol when a new type is introduced where ADTs do not allow this. While the latter is provided in TypeScript, the former is not yet provided in JavaScript.

Protocols lean more toward JavaScript's dynamic typing where ADTs toward TypeScript's static typing. And, incidentally, Clojure is more like JavaScript than TypeScript. Thus, protocols, which nicely fit Clojure, fit JavaScript equally well.

@michaelficarra
Copy link
Member

You mention about talking to maintainer of libraries to see/if how they might use protocols. Protocols work well for use in generic libraries certainly, but I also believe they are exceptionally useful for [...] one's domain types (e.g. Person, Student, Teacher, Class, Course, etc.)

This is true, and I would personally use protocols throughout my own codebases even when I have no intention of using them as an integration point with other libraries. But I think the way we're going to reach the tipping point for widespread use/adoption within the JS community is if one or more major libraries require/encourage their use. If we can't get that buy-in, how else would we demonstrate to the other TC39 stakeholders (e.g. browsers) that we are confident that protocols will not go unused if added?

@mlanza
Copy link
Author

mlanza commented May 10, 2023

Here's what I can tell you.

Clojure changed me as a programmer. More than any other language. It introduced me to the world of purity. Now I still use OOP but I also use FP. My programs are FOOP. I write things using a functional core, imperative shell as this model greatly simplifies how easy it is to get your domain logic right. Having time removed from the equation is the best thing a programmer can get for simplifying things.

Well Clojure/Script is built on protocols and, if you know the devs of that community, they adore it. I built my library because I wanted to do Clojure without big dependencies and without transpiled, hard-to-read JavaScript. And, all credit to Clojure, I love what it does for web development and UIs.

A trouble with JavaScript is that devs think of it primarily as a kingdom of nouns and not also a kingdom of verbs. Since I learned FP, it was never just FP. It was FP plus OOP. They're not mutually exclusive but complementary.

And I've taken the hint already (from responses in this repo) that JavaScript folk are resistant to the kingdom of verbs. Well, they shouldn't be. DataScript is an amazing library built from ClojureScript. It was used to build Logseq, an equally impressive web app.

FP makes writing a lot of hard things way more feasible than using OOP. Undo/redo, for example. But FP is the kingdom of verbs. As are protocols.

Well, devs are doing and appreciating Clojure/Script. But it comes with major baggage (deps). But with things like first-class protocols and records/tuples much of what ClojureScript provides will have made its way into JavaScript proper (along with partial application syntax, and the pipeline operator). The gist is JavaScript (all languages really) are equipped to do FP. Doing it well is more about integration and syntax than anything else.

Furthermore, I'm sure you've minded the JavaScript news. React is the dominant web framework (FP!). FP is increasingly catching on in all languages. It's just not being practiced as much as it could because the native tooling is lacking. Well, protocols (and immutable data, which is coming!) are a major leap in filling that gap.

It's like every great invention: a catch 22. How do you prove something not yet made will be appreciated and adopted until you put it out there? Well, I'm deferring to the surrounding factors that say, "FP is ripe for greater use and appreciation."

Protocols are verbs, first and foremost, which make them FP. And with just a few more JavaScript proposals reaching maturity one could have 90% of Clojure/Script (FP!) in the browser. Remember, ClojureScript is way closer to JavaScript than TypeScript since Clojure is itself dynamically typed. Again, no sweat here because all this means is the dev can go either way. JavaScript will add types, yes, but not in a way that ever imposes them in an absolute sense.

So to sum up your ask, not that I won't continue pondering the question, it's a question of faith. And anyone on the T39 committee who has experience with Clojure/Script will understand the value proposition. My library shows that Clojure (FP!) in the browser without compilation is possible even if I'm not the best dev to efficiently and elegantly implement it.

The real question is how great are protocols? They're jaw-dropping great. The thing that makes Clojure so attractive and convenient is based on this quote (and protocols!):

It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.

I'll give it more thought. I'd love to help you win your audience!

@mlanza
Copy link
Author

mlanza commented May 11, 2023

Take my library. It's almost entirely protocols or types implementing protocols. What does it accomplish? It provides a consistent vernacular. I determined all its verbs. And whenever I integrate a new library (e.g. ImmutableJS) I make it fit those verbs keeping things consistent and homogeneous. Protocols make such integrations seamless.

In TypeScript, types implement interfaces. If an interface exists all instances of types utilizing it must abide it. It superimposes design constraints on the types I control.

Protocols attack the problem from the other side. They allow existing types (including those I don't control) to opt into a contracted interface. It superimposes nothing. Thus, protocols are well suited to integration where there are third-party modules and types one can't control, because they can be dynamically extended, later, if desired.

Interfaces don't do this.

Now consider what I did. I determined a vernacular I wanted, largely Clojure's, and I imposed it on all the natives and on my own custom types. I defined how the language spoke. That vast hole had to be filled with implementations. The presence of protocols make this possible, but implementations are the key. You don't get anything with just protocols.

My experience rests primarily on top of the 100-functions quote where I reference the power of having the consistent vernacular. This vernacular had to be implemented by me across basic (and custom) types. And this was great for providing a superb generic environment in which to execute architectural constructs and pipe and fit things together. It was not primarily about domain code, although I've used protocols there too. But that experience is the lesser one.

Still, I don't trivialize what protocols afforded in that generic utopia. It's the same thing Clojure programmers rave about: How simplicity returned to programming. How things are easier than before.

I'll go on a bit about gaining control over molding an environment to one's liking.

Take Functor.map. If you invoke it on an array what does it yield? Probably another array, right? You could even just call Array.prototype.map in its implementation. Or you could return an entirely new type. Clojure is lazy. It defers its work when possible. And with protocols you can decide Functor.map returns a lazy sequence. As long as it abides similar protocols, the dev doesn't need to be continually minding these potential type transformations. That's a very good thing!

I've worked in static typed languages (.NET) although not TypeScript but I was never as excited about static type checking as I am about protocols. Interfaces come before and enforce policy (and are great for static type thinkers); protocols come after and hoist policy onto existing types (and are great for dynamic type thinkers). It's the fact protocols can be applied later and to anything that makes them so great.

How you you increase adoption/desire?

You show people the promised land, how protocols offers significant advantages and otherwise improve things. And this is done with tons of examples in other languages.

My library is protocols plus other ideas and I didn't really implement it for mass adoption. I didn't want the hundreds of issues and I wanted to freely break things without worry. The problem is people haven't been demonstrating the goodness of protocols in JavaScript in blog posts, in part, because there are no de facto standard libraries for doing so. And no one wants to commit to something that might not become that.

Have faith and try to spur that faith at a T39 meeting.

Protocols are great! They offer something JavaScript can't do well enough on its own. If you implement them as first-class functions (suiting their FP use cases) you're going to have a fount of posts pop up and promote them. Of that, I have little doubt.

Clojure already provides a good specification. I'd recommend you do what it does.

I'll help in any way I'm able. I want to help you make this a reality. Just let me know.

@mlanza
Copy link
Author

mlanza commented May 13, 2023

I was looking over the polyfill implementations out there and I'm not sure they're feature complete and refined for practical use in production. Or to put it another way, have you been regularly using your own implementation in production day in and day out? I don't know the answer to that.

If we can't get that buy-in, how else would we demonstrate to the other TC39 stakeholders (e.g. browsers) that we are confident that protocols will not go unused if added?

But if even you aren't wildly crazy about protocols and demonstrating their benefits, then neither will be others. You can't sell what doesn't get you going! So are you using them? If not, why not?

If something is going to get good it requires regular real world use in order to evolve to meet actual use cases. This is perhaps the hang up to wide adoption. You've got to develop a thing that is demonstrably good, so good in fact that you wouldn't want to be without it once you've experienced its benefits. That's where I am with my own implementation.

The implementation should be a standalone library (one that does not require babeljs transpilation). It should be feature complete, e.g. having everything the final EcmaScript proposal will have. If such a library exists with long-term support then people would be likely to pick it up and use it themselves. But no one wants to commit to an experiment that might go away in this world of tech fads and a dozen new frameworks every year.

With a library with a promised future out there you (and others!) can begin putting out blog posts demonstrating the goodness. If the goodness is compelling it will appear in JavaScript Weekly and elsewhere. This will increase adoption and demonstrate to the committee it's wanted.

All you should be doing in the final stretch is converting your library to more integrated syntax.

And let's say protocols don't attract the dev market. Then, assuming you enjoy protocols (e.g. I can't live without 'em!) you'll keep using your library (just as I plan on doing with mine) and none of your work will have been wasted.

The benefit of getting it adopted is the integrated feel and performance benefits of having the some of the best programmers optimizing things.

@mlanza
Copy link
Author

mlanza commented May 13, 2023

On the note of feature complete, here are some things to think about. I didn't want to create individual issues at this point to avoid overwhelming you. I can if you think a discussion is warranted.

  • Clojure protocols allow you reify an instance of a one-off object meeting one or more protocols. That is, you should be able to specify protocols on a single object or on a class/type. My library accommodates this.
  • I wanted to implement protocols on the vanilla object (e.g. what you get when you const o = {}). I had to put special coding in place to make this possible. Initially, I implemented the desired protocols on Object itself but this was very bad. It means that every conceivable type already has implemented every protocol that Object does, which is not true and not the intent. So this is a special case and you may want to consider how you'd handle it. If protocols are implemented as first-class function with an explicit self instead of this then one could Object.isPlain(self) and handle it and that might be good enough. The drawback to doing it this way is the condition gets hardcoded in the master function because one cannot specify the case (or override it later) in a predetermined way (which is partly the point of protocols). The big idea of protocols is allowing a dev to superimpose a certain set of verbs on just about anything he wants.
  • Protocols are dynamic. Not only can you specify (apply protocols to objects/types dynamically), you can unspecify them (remove them).
  • Do you want to provide a fallback implementation for when a subject has not specified a protocol? I have found this very helpful for gracefully handling such omissions, or delegating to alternative protocols or just doing reasonable things to make potential issues go away.
  • Clojure has satisfies. It provides a way of seeing whether an object implements a certain protocol without invoking the protocol. This is an important condition to handle for avoiding errors. Can this object abide this protocol? If not, do this. In my implementation when I invoke satisfies against an object it either returns null or the protocol as implemented on that type/object (which is reasonable for binary truth).
  • How to you want to handle the prototype chain? If the called type doesn't implement the protocol, do you follow the prototype chain and try again?

This is what I mean by feature complete. Clojure handles all these things and so it already has a feature-complete specification for doing protocols well. It's tried, true, and tested: mature. JavaScript can only have this when people are using it regularly in production code and not just in play around projects that won't endure for years.

One will eventually run into these needs if one were using protocols routinely in production.

@mlanza
Copy link
Author

mlanza commented May 13, 2023

I've given you a lot to consider. I'll make an offer.

My library wasn't written as a standalone protocol library. And it was written prior to many of the EcmaScript advancements.

If you're open to it, I'd be willing to write a standalone library. I'd mostly match the Clojure approach I've explained. It'd be functional meaning, one would explicitly pass in the subject as I've illustrated in #45.

Functor.map(stooges, stooge => stooge.toUpperCase())

You understand that I'd be presenting a simple api (not the final syntax-integrated one) covering the general use cases. The language implementers will certainly optimize and not feel obliged to follow my internals.

You could review the library and provide feedback, of course. Then, once your satisfied it's feature complete, it becomes available for promotion/demonstration.

@ghost
Copy link

ghost commented Jul 4, 2023

What is currently keeping me from bringing the proposal back for advancement to stage 2 is motivation/demand from the community.

I've been waiting for it for years! Would really love to see it and start using it.

@rollo-b2c2
Copy link

With the increased popularity of rust more and more people will want this.

I want this.

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