{"id":13726462,"url":"https://github.com/tc39/proposal-first-class-protocols","last_synced_at":"2025-03-31T06:08:32.735Z","repository":{"id":41533929,"uuid":"64504922","full_name":"tc39/proposal-first-class-protocols","owner":"tc39","description":"a proposal to bring protocol-based interfaces to ECMAScript users","archived":false,"fork":false,"pushed_at":"2022-11-28T19:58:35.000Z","size":439,"stargazers_count":353,"open_issues_count":19,"forks_count":9,"subscribers_count":49,"default_branch":"master","last_synced_at":"2025-03-24T04:14:08.305Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tc39.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-29T19:17:34.000Z","updated_at":"2024-12-28T17:04:45.000Z","dependencies_parsed_at":"2023-01-21T20:19:40.637Z","dependency_job_id":null,"html_url":"https://github.com/tc39/proposal-first-class-protocols","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-first-class-protocols","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-first-class-protocols/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-first-class-protocols/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-first-class-protocols/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tc39","download_url":"https://codeload.github.com/tc39/proposal-first-class-protocols/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246423731,"owners_count":20774820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T01:03:06.222Z","updated_at":"2025-03-31T06:08:32.718Z","avatar_url":"https://github.com/tc39.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"ECMAScript First-Class Protocols Proposal\n=========================================\n\nAs of ES2015, new ECMAScript standard library APIs have used a protocol-based\ndesign, enabled by the introduction of Symbols. Symbols are ECMAScript values\nwhich have identity and may be used as object property keys. The goal of this\nproposal is to provide a convenient syntactic facility for protocol-based\ndesign.\n\nThe proposal is at **Stage 1** after having been proposed at the\n[September 2017](https://github.com/tc39/agendas/blob/master/2017/09.md)\nTC39 meeting.\n\n## What does it look like?\n\nThe most up-to-date information about this proposal can be found in [the July 2018 presentation to the committee](/July%202018%20Update_%20ECMAScript%20Proposal_%20First-Class%20Protocols.pdf).\n\n\n## How can I play with it?\n\nAn outdated prototype using [sweet.js](https://www.sweetjs.org/) is available at\nhttps://github.com/disnet/sweet-interfaces. It needs to be updated to use the\nlatest syntax. A polyfill for the runtime components is available at\nhttps://github.com/michaelficarra/proposal-first-class-protocols-polyfill.\n\n\n## What is it used for?\n\nThe most well-known protocol in ECMAScript is the iteration protocol. APIs such\nas `Array.from`, the `Map` and `Set` constructors, destructuring syntax, and\n`for-of` syntax are all built around this protocol. But there are many others.\nFor example, the protocol defined by `Symbol.toStringTag` could have been\nexpressed using protocols as\n\n```js\nprotocol ToString {\n  tag;\n\n  toString() {\n    return `[object ${this[ToString.tag]}]`;\n  }\n}\n\nObject.prototype[ToString.tag] = 'Object';\nProtocol.implement(Object, ToString);\n```\n\nThe auto-flattening behaviour of `Promise.prototype.then` was a very controversial decision.\nValid arguments exist for both the auto-flattening and the monadic versions to be the default.\nProtocols eliminate this issue in two ways:\n\n1. Symbols are unique and unambiguous. There is no fear of naming collisions,\n   and it is clear what function you are using.\n1. Protocols may be applied to existing classes, so there is nothing\n   preventing consumers with different goals from using their own methods.\n\n```js\nprotocol Functor {\n  map;\n}\n\nclass Identity {\n  constructor(val) { this.val = val; }\n  unwrap() { return this.val; }\n}\n\nPromise.prototype[Functor.map] = function (f) {\n  return this.then(function(x) {\n    if (x instanceof Identity) {\n      x = x.unwrap();\n    }\n    let result = f.call(this, x);\n    if (result instanceof Promise) {\n      result = new Identity(result);\n    }\n    return result;\n  });\n};\n\nProtocol.implement(Promise, Functor);\n```\n\nFinally, one of the biggest benefits of protocols is that they eliminate the\nfear of mutating built-in prototypes. One of the beautiful aspects of\nECMAScript is its ability to extend its built-in prototypes. But with the\nlimited string namespace, this is untenable in large codebases and impossible\nwhen integrating with third parties. Because protocols are based on symbols,\nthis is no longer an anti-pattern.\n\n```js\nclass Ordering {\n  static LT = new Ordering;\n  static EQ = new Ordering;\n  static GT = new Ordering;\n}\n\nprotocol Ordered {\n  compare;\n\n  lessThan(other) {\n    return this[Ordered.compare](other) === Ordering.LT;\n  }\n}\n\nString.prototype[Ordered.compare] = function() { /* elided */ };\nProtocol.implement(String, Ordered);\n```\n\n\n## Relationship to similar features\n\n### Haskell type classes\n\nThis proposal was strongly inspired by Haskell's type classes. The conceptual\nmodel is identical aside from the fact that in Haskell the type class instance\n(essentially an implicit record) is resolved automatically by the type checker.\nFor a more Haskell-like calling pattern, one can define functions like\n\n```js\nfunction fmap(fn) {\n  return function (functor) {\n    return functor[Functor.fmap](fn);\n  };\n}\n```\n\nSimilar to how each type in Haskell may only have a single implementation of\neach type class (newtypes are used as a workaround), each class in JavaScript\nmay only have a single implementation of each protocol. Haskell programmers\nget around this limitation through the use of newtypes. Users of this proposal\nwill extend the protocol they wish to implement with each possible alternative\nand allow the consumer to choose the implementation with the symbol they use.\n\nHaskell type classes exist only at the type level and not the term level, so they\ncannot be passed around as first class values, and any abstraction over them must\nbe done through type-level programming mechanisms. The protocols in this proposal\nare themselves values which may be passed around as first class citizens.\n\n### Rust traits\n\nRust traits are very similar to Haskell type classes. Rust traits have\nrestrictions on implementations for built-in data structures; no such\nrestriction exists with this proposal. The `implements` operator in this\nproposal would be useful in manually guarding a function in a way that Rust's\ntrait bounds do. Default methods in Rust traits are equivalent to what we've\ncalled methods in this proposal.\n\n### Java 8+ interfaces\n\nJava interfaces, as of Java 8, have many of the same features as this proposal.\nThe biggest difference is that Java interfaces are not ad-hoc, meaning existing\nclasses cannot be declared to implement interfaces after they've already been\ndefined. Additionally, Java interfaces share the member name namespace with\nclasses and other interfaces, so they may overlap, shadow, or otherwise be\nincompatible, with no way for a user to disambiguate.\n\n### Ruby mixins\n\nRuby mixins are similar to this proposal in that they allow adding\nfunctionality to existing classes, but different in a number of ways. The\nbiggest difference is the overlapping/conflicting method names due to\neverything existing in one shared namespace. Another difference that is unique\nto Ruby mixins, though, is that they have no check that the methods they rely\non are implemented by the implementing class.\n\n### ECMAScript `mixin(...)` pattern\n\n```js\nclass A extends mixin(SuperClass, FeatureA, FeatureB) {}\n```\n\nThis mixin pattern usually ends up creating one or more intermediate prototype\nobjects which sit between the class and its superclass on the prototype chain.\nIn contrast, this proposal works by copying the provided protocol methods\ninto the class or its prototype. This proposal is also built entirely off of\nSymbol-named properties, but doing so using existing mechanisms would be\ntedious and difficult to do properly. For an example of the complexity involved\nin doing it properly, see the output of the sweet.js implementation.\n\n\n## Links to previous related discussions/strawmen\n\n* [[ES Wiki] strawman:syntax_for_efficient_traits](https://web.archive.org/web/20160616221253/http://wiki.ecmascript.org/doku.php?id=strawman:syntax_for_efficient_traits)\n* [[ES Wiki] strawman:classes_with_trait_composition](https://web.archive.org/web/20160318073016/http://wiki.ecmascript.org/doku.php?id=strawman:classes_with_trait_composition)\n* [[es-discuss] Traits - current state of discussion](https://esdiscuss.org/topic/traits-current-state-of-discussion)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-first-class-protocols","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftc39%2Fproposal-first-class-protocols","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-first-class-protocols/lists"}