git.fiddlerwoaroof.com
name mode size
.circleci 040000
.github 040000
docs 040000
nix 040000
react-demo 040000
src 040000
.babelrc 100644 0 kb
.envrc 100644 0 kb
.eslintignore 100644 0 kb
.eslintrc 100644 0 kb
.gitignore 100644 0 kb
.npmignore 100644 0 kb
.prettierignore 100644 0 kb
README.org 100644 4 kb
gen-fun.html 100644 1 kb
jsconfig.json 100644 0 kb
package-lock.json 100644 719 kb
package.json 100644 1 kb
puppy 100644
shell.nix 100644 0 kb
README.org
#+TITLE: README for js-generic-functions #+AUTHOR: Ed L #+HTML_HEAD: <link rel="stylesheet" href="./colors.css" /> #+EXPORT_FILE_NAME: docs/index.html [[https://www.npmjs.com/package/js-generic-functions][https://img.shields.io/npm/v/js-generic-functions.svg]] [[https://circleci.com/gh/fiddlerwoaroof/js-generic-functions.svg?style=svg]] ** What is this? An implementation of generic functions based on CLOS and the protocols defined in the Art of the Metaobject protocol, adapted for JS. These adaptations include using the prototype chain instead of classes and additionally providing extensible specializers (as in https://github.com/sbcl/specializable). For the moment, this is only used to provide a Shape specializer, as the details of the interaction between such specializers and subtyping are an open question. ** Docs *** Basic Usage #+NAME: imports #+BEGIN_SRC js import { defgeneric } from "./genfuns.js"; #+END_SRC Defining a function works by calling src_js{defgeneric} with some information about the function name and arguments. Methods are then added by calling the appropriate methods with a pair of arguments: a list of specializers (prototypes in the simple case, although there are other options) and a function to run if those specializers match. #+NAME: basic-definition #+BEGIN_SRC js const example1generic = defgeneric("example1", "a", "b") .primary([Number, Object], (n, __) => [1, n]) .primary([Object, Number], (_, n) => [2, n]) .primary([Object, Object], (_, __) => [5, null]); #+END_SRC After a generic function has been defined, you can get the function to call it by accessing its src_js{.fn} attribute. #+NAME: call-the-function #+BEGIN_SRC js const example1 = example1generic.fn; expect(example1(5, {})).toEqual([1, 5]); expect(example1({}, 6)).toEqual([2, 6]); expect(example1("hello", {})).toEqual([5, null]); expect(example1({}, "world")).toEqual([5, null]); expect(example1({}, {})).toEqual([5, null]); #+END_SRC If a separate reference to the generic function object is maintained, you can add methods like so: #+NAME: add-methods #+BEGIN_SRC js example1generic .primary([String, Object], (s, __) => [3, s]) .primary([Object, String], (_, s) => [4, s]); expect(example1("hello", {})).toEqual([3, "hello"]); expect(example1({}, "world")).toEqual([4, "world"]); #+END_SRC *** Other sorts of specializers #+NAME: specializer-import #+BEGIN_SRC js import { Shape, Eql } from "./genfuns.js"; #+END_SRC #+NAME: specializer-examples #+BEGIN_SRC js const example2 = defgeneric("example2", "inp") .primary([Shape("a", "b")], inp => `a: ${inp.a} b: ${inp.b}`) .primary([Shape("a")], inp => `a: ${inp.a} b: <missing>`) .primary([Shape(["c", 1])], inp => `c: one`) .primary([Shape(["c", 2])], inp => `c: two`) .primary([Eql(1)], inp => "one").fn; expect(example2({ a: 3, q: "whatever" })).toEqual("a: 3 b: <missing>"); expect(example2({ a: 3, b: 4, q: "whatever" })).toEqual("a: 3 b: 4"); expect(example2({ c: 1, q: "whatever" })).toEqual("c: one"); expect(example2({ c: 2, q: "whatever" })).toEqual("c: two"); expect(example2(1)).toEqual("one"); #+END_SRC #+BEGIN_SRC js :tangle src/doc.test.js :comments noweb :noweb tangle :exports none <<imports>> <<specializer-import>> describe("defgeneric", () => { test("methods get called appropriately", () => { <<basic-definition>> <<call-the-function>> <<add-methods>> <<sample1>> }); test ('specializers work as expected', () => { <<specializer-examples>> }) }); #+END_SRC