~subsetpark/fugue

66f804abae1e286703a7a247a17ec6298dd484c9 — Zach Smith 2 months ago 1f65356
Update ReADME
2 files changed, 143 insertions(+), 133 deletions(-)

M README.md
M fugue.janet
M README.md => README.md +128 -117
@@ 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 <function identity> :_meta @{ :prototype-allocations @{} :getters @{} :object-type :prototype :fields () :instance-defaults @{}} :_name "Prototype" :new <function new-from-Root>}
@{:_init <function identity> :_meta @{ :fields () :getters @{} :instance-defaults @{} :object-type :prototype :prototype-allocations @{}} :_name "Prototype" :new <function new-from-Root>}
```

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 <function cat> 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:

`<field-name> <field-attributes>`

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 name>`.
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 name>`.

---

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:

- `(@ <prototype-name> <dictionary>)`: 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. 
- `(@ <prototype-name> <dictionary>)`: 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 `(@ <field name>)`, where `<field name>` is a symbol, is
transformed into `(obj (keyword <field name>))`, if and only if
`<field name>` is defined for `proto`, so that `(@ name)` or its
setter form `(set (@ name) foo)` do the right thing.
transformed into `(obj (keyword <field name>))`, if and only if `<field
name>` 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


M fugue.janet => fugue.janet +15 -16
@@ 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]