{"id":15684076,"url":"https://github.com/zkat/protocols","last_synced_at":"2025-09-09T19:17:14.731Z","repository":{"id":57170883,"uuid":"67018338","full_name":"zkat/protocols","owner":"zkat","description":"Multi-type protocol-based polymorphism","archived":false,"fork":false,"pushed_at":"2016-09-10T02:03:31.000Z","size":29,"stargazers_count":11,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-16T16:28:07.546Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/zkat.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-08-31T08:23:30.000Z","updated_at":"2025-06-11T12:14:10.000Z","dependencies_parsed_at":"2022-08-27T13:20:12.229Z","dependency_job_id":null,"html_url":"https://github.com/zkat/protocols","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/zkat/protocols","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fprotocols","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fprotocols/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fprotocols/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fprotocols/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zkat","download_url":"https://codeload.github.com/zkat/protocols/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fprotocols/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274348229,"owners_count":25268972,"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","status":"online","status_checked_at":"2025-09-09T02:00:10.223Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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-10-03T17:10:47.583Z","updated_at":"2025-09-09T19:17:14.706Z","avatar_url":"https://github.com/zkat.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Protocols [![Travis](https://img.shields.io/travis/zkat/protocols.svg)](https://travis-ci.org/zkat/protocols) [![npm version](https://img.shields.io/npm/v/@zkat/protocols.svg)](https://npm.im/@zkat/protocols) [![license](https://img.shields.io/npm/l/@zkat/protocols.svg)](https://npm.im/@zkat/protocols)\n\n[`@zkat/protocols`](https://github.com/zkat/protocols) is a JavaScript library\nis a library for making groups of methods, called \"protocols\", that work\ntogether to provide some abstract functionality that other things can then rely\non. If you're familiar with the concept of [\"duck\ntyping\"](https://en.wikipedia.org/wiki/Duck_typing), then it might make sense to\nthink of protocols as things that explicitly define what methods you need in\norder to \"clearly be a duck\".\n\nOn top of providing a nice, clear interface for defining these protocols, this\nmodule clear, useful errors when implementations are missing something or doing\nsomething wrong.\n\nOne thing that sets this library apart from others is that on top of defining\nduck-typed protocols on a single class/type, it lets you have different\nimplementations depending on the _arguments_. So a method on `Foo` may call\ndifferent code dependent on whether its first _argument_ is `Bar` or `Baz`. If\nyou've ever wished a method worked differently for different types of things\npassed to it, this does that!\n\n## Install\n\n`$ npm install @zkat/protocols`\n\n## Table of Contents\n\n* [Example](#example)\n* [API](#api)\n  * [`protocol()`](#protocol)\n  * [`implementation`](#impl)\n\n### Example\n\n```javascript\nimport protocol from \"@zkat/protocols\"\n\n// Quackable is a protocol that defines three methods\nconst Quackable = protocol({\n  walk: [],\n  talk: [],\n  isADuck: []\n})\n\n// `duck` must implement `Quackable` for this function to work. It doesn't\n// matter what type or class duck is, as long as it implements Quackable.\nfunction doStuffToDucks (duck) {\n  if (!duck.isADuck()) {\n    throw new Error('I want a duck!')\n  } else {\n    console.log(duck.walk())\n    console.log(duck.talk())\n  }\n}\n\n// elsewhere in the project...\nclass Person () {}\n\nQuackable(Person, {\n  walk() { return \"my knees go the wrong way but I'm doing my best\" }\n  talk() { return \"uhhh... do I have to? oh... 'Quack' 😒\"}\n  isADuck() { return true /* lol I'm totally lying */ }\n})\n\n// and another place...\nclass Duck () {}\n\nQuackable(Duck, {\n  walk() { return \"*hobble hobble*\" }\n  talk() { return \"QUACK QUACK\" }\n  isADuck() { return true }\n})\n\n// main.js\ndoStuffToDucks(new Person()) // works\ndoStuffToDucks(new Duck()) // works\ndoStuffToDucks({ walk() { return 'meh' } }) // =\u003e error\n```\n\n### API\n\n#### \u003ca name=\"protocol\"\u003e\u003c/a\u003e `protocol(\u003ctypes\u003e?, \u003cspec\u003e)`\n\nDefines a new protocol on across arguments of types defined by `\u003ctypes\u003e`, which\nwill expect implementations for the functions specified in `\u003cspec\u003e`.\n\nIf `\u003ctypes\u003e` is missing, it will be treated the same as if it were an empty\narray.\n\n\nThe types in `\u003cspec\u003e` must map, by string name, to the type names specified in\n`\u003ctypes\u003e`, or be an empty array if `\u003ctypes\u003e` is omitted. The types in `\u003cspec\u003e`\nwill then be used to map between method implementations for the individual\nfunctions, and the provided types in the impl.\n\n##### Example\n\n```javascript\nconst Eq = protocol(['a', 'b'], {\n  eq: ['a', 'b']\n})\n```\n\n#### \u003ca name=\"impl\"\u003e\u003c/a\u003e `proto(\u003ctarget\u003e?, \u003ctypes\u003e?, \u003cimplementations\u003e)`\n\nAdds a new implementation to the given `proto` across `\u003ctypes\u003e`.\n\n`\u003cimplementations\u003e` must be an object with functions matching the protocol's\nAPI. The types in `\u003ctypes\u003e` will be used for defining specific methods using\nthe function as the body.\n\nProtocol implementations must include either `\u003ctarget\u003e`, `\u003ctypes\u003e`, or both:\n\n* If only `\u003ctarget\u003e` is present, implementations will be defined the same as\n  \"traditional\" methods -- that is, the definitions in `\u003cimplementations\u003e`\n  will add function properties directly to `\u003ctarget\u003e`.\n\n* If only `\u003ctypes\u003e` is present, the protocol will keep all protocol functions as\n  \"static\" methods on the protocol itself.\n\n* If both are specified, protocol implementations will add methods to the `\u003ctarget\u003e`, and define multimethods using `\u003ctypes\u003e`.\n\nIf a protocol is derivable -- that is, all its functions have default impls,\nthen the `\u003cimplementations\u003e` object can be omitted entirely, and the protocol\nwill be automatically derived for the given `\u003ctypes\u003e`\n\n##### Example\n\n```javascript\nimport protocol from '@zkat/protocols'\n\n// Singly-dispatched protocols\nconst Show = protocol({\n  show: []\n})\n\nclass Foo {}\n\nShow(Foo, {\n  show () { return `[object Foo(${this.name})]` }\n})\n\nvar f = new Foo()\nf.name = 'alex'\nf.show() === '[object Foo(alex)]'\n```\n\n```javascript\nimport protocol from '@zkat/protocols'\n\n// Multi-dispatched protocols\nconst Comparable = protocol(['target'], {\n  compare: ['target'],\n})\n\nclass Foo {}\nclass Bar {}\nclass Baz {}\n\nComparable(Foo, [Bar], {\n  compare (bar) { return 'bars are ok' }\n})\n\nComparable(Foo, [Baz], {\n  compare (baz) { return 'but bazzes are better' }\n})\n\nconst foo = new Foo()\nconst bar = new Bar()\nconst baz = new Baz()\n\nfoo.compare(bar) // 'bars are ok'\nfoo.compare(baz) // 'but bazzes are better'\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzkat%2Fprotocols","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzkat%2Fprotocols","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzkat%2Fprotocols/lists"}