git.fiddlerwoaroof.com
README.org
35f07878
 #+TITLE: README for js-generic-functions
 #+AUTHOR: Ed L
14341a75
 #+HTML_HEAD: <link rel="stylesheet" href="./colors.css" />
35f07878
 #+EXPORT_FILE_NAME: docs/index.html
 
fb87df50
 [[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]]
 
0b0cb682
 ** What is this?
8d617730
 
b1907363
 
a00b5a50
 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.
 
0b0cb682
 ** Docs
a00b5a50
 
0b0cb682
 *** Basic Usage
5ff4d201
 
 #+NAME: imports
35f07878
 #+BEGIN_SRC js
5ff4d201
 import { defgeneric } from "./genfuns.js";
 #+END_SRC
 
35f07878
 Defining a function works by calling src_js{defgeneric} with some
5ff4d201
 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
35f07878
 #+BEGIN_SRC js
5ff4d201
   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
35f07878
 call it by accessing its src_js{.fn} attribute.
5ff4d201
 
 #+NAME: call-the-function
35f07878
 #+BEGIN_SRC js
5ff4d201
   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
35f07878
 #+BEGIN_SRC js
5ff4d201
   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
 
0b0cb682
 *** Other sorts of specializers
35f07878
 #+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
 
5ff4d201
 #+BEGIN_SRC js :tangle src/doc.test.js :comments noweb :noweb tangle :exports none
   <<imports>>
35f07878
   <<specializer-import>>
5ff4d201
 
   describe("defgeneric", () => {
     test("methods get called appropriately", () => {
       <<basic-definition>>
 
       <<call-the-function>>
 
       <<add-methods>>
 
       <<sample1>>
     });
35f07878
     test ('specializers work as expected', () => {
       <<specializer-examples>>
     })
5ff4d201
   });
 #+END_SRC