From 66f804abae1e286703a7a247a17ec6298dd484c9 Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Mon, 19 Jul 2021 22:07:33 -0400 Subject: [PATCH] Update ReADME --- README.md | 245 +++++++++++++++++++++++++++------------------------- fugue.janet | 31 ++++--- 2 files changed, 143 insertions(+), 133 deletions(-) diff --git a/README.md b/README.md index 0f98c6d..5ddb292 100644 --- a/README.md +++ b/README.md @@ -78,24 +78,24 @@ Accepts two forms: `(@ SomePrototype :some-field)` - Translates into `:some-field`, if `some-field` is defined on `SomePrototype`. -`(@ SomePrototype some-object :some-field)` - Asserts (as above) at -compile time that `some-field` is defined on `SomePrototype`; at -runtime, checks that `some-object` is a descendent of -`SomePrototype` and if so, translates to `(some-object :some-field)`. +`(@ SomePrototype some-object :some-field)` - Asserts (as above) at compile +time that `some-field` is defined on `SomePrototype`; at runtime, checks that +`some-object` is a descendent of `SomePrototype` and if so, translates to +`(some-object :some-field)`. -[1]: fugue.janet#L797 +[1]: fugue.janet#L851 ## Root **table** | [source][2] ```janet -@{:_init :_meta @{ :prototype-allocations @{} :getters @{} :object-type :prototype :fields () :instance-defaults @{}} :_name "Prototype" :new } +@{:_init :_meta @{ :fields () :getters @{} :instance-defaults @{} :object-type :prototype :prototype-allocations @{}} :_name "Prototype" :new } ``` Root of the Fugue object hierarchy. -[2]: fugue.janet#L40 +[2]: fugue.janet#L37 ## Root*? @@ -105,8 +105,7 @@ Root of the Fugue object hierarchy. (Root*? obj) ``` -Proto ancestor predicate: return if `obj` is a -descendent of Root. +Proto ancestor predicate: return if `obj` is a descendent of Root. [3]: eval#L-1 @@ -118,8 +117,7 @@ descendent of Root. (Root? obj) ``` -Proto instance predicate: return if `obj` is an -instance (that is, a direct child) of Root. +Proto instance predicate: return if `obj` is an instance (that is, a direct child) of Root. [4]: eval#L-1 @@ -131,12 +129,12 @@ instance (that is, a direct child) of Root. (allocate obj key value) ``` -Allocation-aware put. If `obj` has inherited an allocation to a -specific prototype for this key, then `fugue/allocate` will put `value` -at `key` in the appropriate prototype, and it will be inherited by -all descendents of that prototype. +Allocation-aware put. If `obj` has inherited an allocation to a specific +prototype for this key, then `fugue/allocate` will put `value` at `key` in +the appropriate prototype, and it will be inherited by all descendents of +that prototype. -[5]: fugue.janet#L362 +[5]: fugue.janet#L386 ## declare-open-multi @@ -152,7 +150,7 @@ Extending an open multimethod (see `extend-multi`) from any other environment makes the case extension available wherever the multimethod has been imported. -[6]: fugue.janet#L676 +[6]: fugue.janet#L723 ## defgeneric @@ -162,12 +160,11 @@ multimethod has been imported. (defgeneric name & rest) ``` -Define a generic function. When this function is called, if the -first argument has a method corresponding to the name of the -function, call that object 's method with the arguments. Otherwise, -evaluate `body`. +Define a generic function. When this function is called, if the first +argument has a method corresponding to the name of the function, call that +object 's method with the arguments. Otherwise, evaluate `body`. -[7]: fugue.janet#L399 +[7]: fugue.janet#L426 ## defmethod @@ -177,15 +174,15 @@ evaluate `body`. (defmethod name proto args & body) ``` -Simple single-dispatch method definition. Roughly equivalent to -`put` ing a function directly into a prototype. +Simple single-dispatch method definition. Roughly equivalent to `put` ing a +function directly into a prototype. Defines a few symbols for reference in the body of the method. - `__parent` - Bound to the parent of `proto`. - `__super` - Bound to the method at `name` within `__parent`. -[8]: fugue.janet#L417 +[8]: fugue.janet#L451 ## defmulti @@ -195,13 +192,12 @@ Defines a few symbols for reference in the body of the method. (defmulti name multi-types args & body) ``` -Define a multimethod based on all the arguments passed to the -function. +Define a multimethod based on all the arguments passed to the function. Example usage : ``` -> (defproto Foo ()) +> (defproto Foo nil) > (defmulti add [Foo] [f] (put f :value 1)) > (defmulti add [:number] [x] (+ x 1)) > (defmulti add [:string] [s] (string s "!")) @@ -214,17 +210,36 @@ Example usage : "s!" ``` -`defmulti` takes a sequence of type-or-prototypes, and builds a -function which will check its arguments against those types (as well -as all other ones specified in other `defmulti` calls to the same -function name), and execute the function body for the matching type -signature. +`defmulti` takes a sequence of *multimethod specifications*, and builds a +function which will check its arguments against those types (as well as all +other ones specified in other `defmulti` calls to the same function name), +and execute the function body for the matching type signature. -In addition to type names or prototypes, you can use the symbol `_` -or keyword `:_` as a wildcard that means "match any type". For -instance, +A multimethod specification can be any of the following data types: -``` +- a **keyword** representing the name of a simple or abstract type; for +instance, `:number` or `:string`. This will match against values of this +type. + +- A **symbol** referring to an existing table. This will match against tables +which have this table as a prototype. + +- The **fallback symbols** `:_` and `_`. These will match against anything. + +- A **match specification**, ie, a pattern understood by the `match` macro, +as long as it isn't one of the above values. This will match against any +value that would be matched in an execution of `match`. This includes tuples +with arbitrary predicates. + +Multimethod specs match in order of *most specific* to *least specific*; that +is: + +1. Match specifications +2. Prototypes +3. Simple types +4. Fallback + +``` repl:2:> (defmulti cat [:string :_] [s1 s2] (string s1 s2)) repl:3:> (cat "hello " "world!") "hello world!" @@ -232,23 +247,22 @@ repl:4:> (cat "hello " 42) "hello 42" repl:5:> (cat 42 "hello") error: could not apply multimethod to args (42 "hello") - in cat [repl] on line 2, column 1 - in _thunk [repl] (tailcall) on line 5, column 1 +in cat [repl] on line 2, column 1 +in _thunk [repl] (tailcall) on line 5, column 1 ``` -Defining a multimethod with the signature `[:string :_]` will match -on any two arguments if the first one is a string. +Defining a multimethod with the signature `[:string :_]` will match on any +two arguments if the first one is a string. -A multimethod without wilcards will be preferred to one with one in -the same position. For instance, if we define an additional -multimethod: +A multimethod without wilcards will be preferred to one with one in the same +position. For instance, if we define an additional multimethod: ``` repl:8:> (defmulti cat [:string :number] [s n] (string s " #" n)) ``` -Then that more specific method will be preferred and the wildcard -will be a fallback if the specific one doesn't match: +Then that more specific method will be preferred and the wildcard will be a +fallback if the specific one doesn't match: ``` repl:10:> (cat "hello " @"world") @@ -257,7 +271,7 @@ repl:12:> (cat "hello" 100) "hello #100" ``` -[9]: fugue.janet#L600 +[9]: fugue.janet#L629 ## defproto @@ -271,67 +285,66 @@ Object prototype definition. ## Usage -`name` should be any symbol. The resulting prototype will be -named after it. +`name` should be any symbol. The resulting prototype will be named after it. -`parent-name` is required; it can be an existing prototype, *or* -some null-ish value. If null-ish (`nil` or `()` should make the most -sense...) the parent of the prototype will be set to `fugue/Root`. +`parent-name` is required; it can be an existing prototype, *or* some +null-ish value. If null-ish (`nil` or `()` should make the most sense...) the +parent of the prototype will be set to `fugue/Root`. `fields` should be 0 or more pairs of the following format: ` ` Where `field-name` is a field to define on the prototype and -`field-attributes` is a struct describing the field. The following -field attributes are currently recognized: +`field-attributes` is a struct describing the field. The following field +attributes are currently recognized: + +- `:default`: provide a default value for all new instances of this prototype + +- `:init?`: if truthy, then this field will be a required parameter to the +prototype's constructor -- `:default`: provide a default value for all new instances of this -prototype -- `:init?`: if truthy, then this field will be a required parameter -to the prototype 's constructor - `:allocation`: if `:prototype`, then `fugue/allocate` will always act on the prototype when putting this field. + - `:allocate-value`: this field will have this attribute set at the -prototype, so that any children without their own values will -inherit it. -- `:getter`: specify a name for the defined function to access this -field (by default, has the same name as the field). Specify `false` -to prevent a getter from being defined. - -`defproto` will define a getter function for each of the defined -fields, unless `:getter` is false. - -`defproto` will also create a `:new` method in the created -prototype. This will take as positional arguments all of the fields -specified as `init?`, and then accept in `&keys` format any other -attributes to set on this object. - -The special method `:_init` will be called as the last step in the -`:new` constructor. It can be defined for a prototype (see -`defmethod`) to take a new instance and to make any arbitrary -mutations on the instance or prototype as part of object -instantiation. By default it simply returns the instance. - -The value provided to a field's `:default` entry will be inserted -directly to the instance. Thus, mutable/referenced terms like tables -and arrays will be shared amongst all instances. In cases where you -want to insert a new term for each new instance, use the `_init` -method to put a value at that field. - -If `fields` is of an odd length, the last element will be treated as -a prototype attributes struct. There is currently one valid prototype attribute: - -- `:constructor` : Set the name of the defined function that - calls `:new`. If false, no additional constructor will be - defined. By default, will be set to `new-`. +prototype, so that any children without their own values will inherit it. + +- `:getter`: specify a name for the defined function to access this field (by +default, has the same name as the field). Specify `false` to prevent a getter +from being defined. + +`defproto` will define a getter function for each of the defined fields, +unless `:getter` is false. + +`defproto` will also create a `:new` method in the created prototype. This +will take as positional arguments all of the fields specified as `init?`, and +then accept in `&keys` format any other attributes to set on this object. + +The special method `:_init` will be called as the last step in the `:new` +constructor. It can be defined for a prototype (see `defmethod`) to take a +new instance and to make any arbitrary mutations on the instance or prototype +as part of object instantiation. By default it simply returns the instance. + +The value provided to a field's `:default` entry will be inserted directly to +the instance. Thus, mutable/referenced terms like tables and arrays will be +shared amongst all instances. In cases where you want to insert a new term +for each new instance, use the `_init` method to put a value at that field. + +If `fields` is of an odd length, the last element will be treated as a +prototype attributes struct. There is currently one valid prototype +attribute: + +- `:constructor` : Set the name of the defined function that calls `:new`. If +false, no additional constructor will be defined. By default, will be set to +`new-`. --- An example usage: ``` -repl:43:> (fugue/defproto Dog () name {:allocate-value "Fido"}) +repl:43:> (fugue/defproto Dog nil name {:allocate-value "Fido"}) repl:44:> (fugue/defproto Pekingese Dog size {:default "Extremely Small"}) repl:45:> (fugue/defmethod speak Dog [self] (string "My name is " (self :name))) repl:46:> (fugue/defmethod speak Pekingese [self] (string (prototype-method self) " and I am " (self :size))) @@ -339,7 +352,7 @@ repl:47:> (speak (:new Pekingese)) "My name is Fido and I am Extremely Small" ``` -[10]: fugue.janet#L259 +[10]: fugue.janet#L284 ## extend-multi @@ -354,10 +367,10 @@ syntax as `defmulti`. See that function's documentation for full usage reference. -Whenever a case is added to `multi`, that case is available -wherever the multimethod is imported. +Whenever a case is added to `multi`, that case is available wherever the +multimethod is imported. -[11]: fugue.janet#L692 +[11]: fugue.janet#L754 ## fields @@ -370,7 +383,7 @@ wherever the multimethod is imported. Return all the defined fields for `obj` and its prototype hierarchy. -[12]: fugue.janet#L50 +[12]: fugue.janet#L45 ## get-type-or-proto @@ -383,7 +396,7 @@ hierarchy. Return the prototype of `obj`, if it has one, otherwise the keyword output of `type`. -[13]: fugue.janet#L173 +[13]: fugue.janet#L197 ## match @@ -395,12 +408,11 @@ output of `type`. Prototype-aware version of `match`. Introduces one new case form: -- `(@ )`: Will pattern match against an - instance of `prototype-name`. Additionally, will validate at - compile-time that every key in `dictionary` is a field that's - present on the specified prototype. +- `(@ )`: Will pattern match against an instance +of `prototype-name`. Additionally, will validate at compile-time that every +key in `dictionary` is a field that's present on the specified prototype. -[14]: fugue.janet#L831 +[14]: fugue.janet#L877 ## multimethod-types-match? @@ -414,7 +426,7 @@ Check to see if the types `args` match the sequence `arg-types`, according to multimethod rules (ie, following prototype membership and using `:_` as a fallback) -[15]: fugue.janet#L525 +[15]: fugue.janet#L551 ## new-Root @@ -436,9 +448,9 @@ Constructor for Root. Return a new object with Root as the prototype. (prototype? obj) ``` -Is `obj` the result of a `defproto ` call? +Is `obj` the result of a `defproto ` call? -[17]: fugue.janet#L354 +[17]: fugue.janet#L378 ## with-slots @@ -452,16 +464,15 @@ Anaphoric macro with transformed getter/setters. Introduces two useful forms for referring to `obj`. -It introduces a *reference symbol* - `@` by default -(see `with-slots-as `to specify the symbol). +It introduces a *reference symbol* - `@` by default (see `with-slots-as `to +specify the symbol). The pattern `(@ )`, where `` is a symbol, is -transformed into `(obj (keyword ))`, if and only if -`` is defined for `proto`, so that `(@ name)` or its -setter form `(set (@ name) foo)` do the right thing. +transformed into `(obj (keyword ))`, if and only if `` is defined for `proto`, so that `(@ name)` or its setter form `(set (@ +name) foo)` do the right thing. -The reference symbol by itself is introduces as a reference to -`obj`. +The reference symbol by itself is introduces as a reference to `obj`. Returns `obj`. @@ -472,15 +483,15 @@ Example : ``` repl:2:> (defproto Foo nil name {:default "Jane Doe"}) repl:4:> (with-slots Foo (new-Foo) - (set (@ name) "Cosmo Kramer") - (print (@ name)) - (print (Foo? @))) +(set (@ name) "Cosmo Kramer") +(print (@ name)) +(print (Foo? @))) Cosmo Kramer true @Foo{:_meta @{:object-type :instance} :name "Cosmo Kramer"} ``` -[18]: fugue.janet#L742 +[18]: fugue.janet#L804 ## with-slots-as @@ -496,5 +507,5 @@ Specifies `as` as the reference symbol for `with-slots`. See `with-slots` documentation for more details. -[19]: fugue.janet#L779 +[19]: fugue.janet#L840 diff --git a/fugue.janet b/fugue.janet index 24cde1a..ccfa03f 100644 --- a/fugue.janet +++ b/fugue.janet @@ -24,7 +24,7 @@ (defn- base-proto "Basic prototype table." - [name defined-fields instance-defaults proto-allocated-fields & kvs] + [name defined-fields instance-defaults proto-allocated-fields & rest] (table :_meta @{:object-type :prototype :fields defined-fields @@ -32,7 +32,7 @@ :instance-defaults instance-defaults :getters @{}} :_name name - ;kvs)) + ;rest)) (def Root "Root of the Fugue object hierarchy." @@ -215,11 +215,10 @@ "Generate the defn form for the Prototype predicate." [name] (let [pred-name (pred-name name) - pred-docstring (string/format ``` - Proto instance predicate: return if `obj` is an - instance (that is, a direct child) of %s. - ``` - (string name))] + pred-docstring (string/format + (string "Proto instance predicate: return if `obj` is an " + "instance (that is, a direct child) of %s.") + (string name))] ~(defn ,pred-name ,pred-docstring [obj] @@ -238,12 +237,10 @@ [name] (let [pred-name (pred-name name) rec-pred-name (symbol name "*?") - - rec-pred-docstring (string/format ``` - Proto ancestor predicate: return if `obj` is a - descendent of %s. - ``` - (string name))] + rec-pred-docstring (string/format + (string "Proto ancestor predicate: return if `obj` is a " + "descendent of %s.") + (string name))] ~(defn ,rec-pred-name ,rec-pred-docstring [obj] @@ -255,9 +252,11 @@ "Generate the init form wrapper." [name constructor-name] (when constructor-name - (let [docstring (string/format "Constructor for %s. Return a new object with %s as the prototype." - (string name) - (string name))] + (let [docstring (string/format + (string "Constructor for %s. Return a new object with %s " + "as the prototype.") + (string name) + (string name))] ~(defn ,constructor-name ,docstring [& rest] -- 2.45.2