{"id":16880609,"url":"https://github.com/squaremo/multimethods-for-javascript","last_synced_at":"2025-03-20T03:34:01.243Z","repository":{"id":16399285,"uuid":"19150143","full_name":"squaremo/multimethods-for-javascript","owner":"squaremo","description":"Multimethods (on argument type) for JavaScript **EXPERIMENT**","archived":false,"fork":false,"pushed_at":"2014-04-28T12:08:44.000Z","size":160,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-25T05:41:16.410Z","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/squaremo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-04-25T15:08:27.000Z","updated_at":"2020-01-11T05:10:55.000Z","dependencies_parsed_at":"2022-09-24T11:52:05.920Z","dependency_job_id":null,"html_url":"https://github.com/squaremo/multimethods-for-javascript","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/squaremo%2Fmultimethods-for-javascript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/squaremo%2Fmultimethods-for-javascript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/squaremo%2Fmultimethods-for-javascript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/squaremo%2Fmultimethods-for-javascript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/squaremo","download_url":"https://codeload.github.com/squaremo/multimethods-for-javascript/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244544338,"owners_count":20469661,"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-10-13T15:59:28.719Z","updated_at":"2025-03-20T03:34:01.222Z","avatar_url":"https://github.com/squaremo.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Multimethods for JavaScript\n\nAnother experiment in providing multimethods for JavaScript. My other\nsuch experiment is an implementation of [Prototypes with multiple\ndispatch][pmd].\n\nThis one simplifies things by keeping method information in the\nprocedure, rather than keeping it in the prototypes; this means it is\nnot sensitive to changing prototypes, i.e., it assumes the prototype\nchain is fixed at the time of method declaration. This is usually a\nreasonable assumption, since in most JavaScript code the constructors\nwill have been fully defined (i.e., their prototypes set) before being\nused, and don't change afterwards. It also assumes objects are created\nusing a constructor, and not with `Object.create`.\n\nThe result (to be shown) is that we can build a finite automaton for\nmethod selection, given some additional assumptions about the ordering\nof selectors. Furthermore, the runtime method selection relies only on\n`instanceof` and `typeof` (i.e., not on the `constructor` or\n`__proto__` properties, nor on the `Object.getPrototypeOf' method).\n\nMy motivation for this module is avoiding hand-written overloading of\nfunctions in [amqp.node][]. In the amqp.node API I would like to have,\nfor instance,\n\n```js\nChannel#assertQueue(queue, callback)\nChannel#assertQueue(options, callback)\nChannel#assertQueue(queue, options, callback)\n```\n\nRather than writing code like this:\n\n```js\nChannel.prototype.assertQueue = function(arg1, arg2, arg3) {\n    var queue, options, callback;\n    if (arguments.length == 2) {\n      callback = arg2;\n      if (typeof arg1 === 'string') {\n        queue = arg1; options = {};\n      }\n      else if (typeof arg1 === 'object') {\n        queue = ''; options = arg1;\n      }\n      else {\n        throw new IllegalArgumentError(\n          \"First argument must be a string or object\");\n      }\n    }\n    else if (arguments.length == 3) {\n      queue = arg1; options = arg2; callback = arg3;\n    }\n    else throw new IllegalArgumentError(\"Requires 2 or 3 arguments\");\n\n    // ...\n}\n```\n\nI'd like to write something more like this:\n\n```js\nvar assertQueue = make_procedure();\n\ndefine_method(assertQueue, Channel, String, Function,\n    function(ch, queue, callback) {\n      return assertQueue(ch, queue, {}, callback);\n    });\n\ndefine_method(assertQueue, Channel, Object, Function,\n    function(ch, options, callback) {\n      return assertQueue('', options, callback);\n    });\n\ndefine_method(assertQueue, Channel, String, Object, Function,\n    function(ch, name, options, callback) {\n      // ... do an RPC with the name and options given\n    });\n\nChannel.prototype.assertQueue = assertQueue;\n```\n\nThe idea being, of course, that the method selection algorithm takes\nthe place of that hand-written code above.\n\nIn the example the methods vary by missing out arguments, so they\ndelegate to the all-possible-arguments version; but in general there\nwill be scenarios in which entirely different things can happen\ndepending on the types of the arguments. In those latter cases, it's\nhandy to be able to define the variations in different places, say if\none is extending a procedure defined elsewhere to account for a new\ntype.\n\n## Related work\n\n[http://blog.vjeux.com/2010/javascript/javascript-full-dispatch-multimethod.html]\ntakes a very similar tack to my intial implementation, but is perhaps\na bit more general (since it allows methods to be defined using\npredicates rather than just constructors). The method selection is\n\"first match wins\", which is ultimately not what I'm after.\n\n[http://krisjordan.com/multimethod-js] looks similar (mainly due to\nthe name), but mimics the generic dispatch feature in Clojure (also\ncalled \"multimethods\", which is a misnomer, in my opinion).\n\nThe algorithm, which can be summarised as \"calculate a dispatch value\nbased on the argument then select the method associated with that\nvalue\", doesn't lend itself to dispatching on the types of the\narguments. For this reason it's not of use to me. (The Clojure\nimplementation is a bit more capable, and is able to dispatch using a\nsubclass relationship; it's still \"first match wins\" though.)\n\n[pmd]: https://github.com/squaremo/js-pmd\n[amqp.node]: https://github.com/squaremo/amqp.node\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsquaremo%2Fmultimethods-for-javascript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsquaremo%2Fmultimethods-for-javascript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsquaremo%2Fmultimethods-for-javascript/lists"}