~subsetpark/fugue

375e79b44edaed47a1466b122d884c96726c3316 — Zach Smith 6 months ago ea47a68
Drop default-fn - use _init
3 files changed, 56 insertions(+), 56 deletions(-)

M README.md
M fugue.janet
M test/fugue.janet
M README.md => README.md +24 -20
@@ 64,12 64,12 @@ be selected for any descendent prototype instances.
**table**  | [source][1]

```janet
@{:_init <function identity> :_meta @{ :instance-default-fns @{} :prototype-allocations @{} :object-type :prototype :fields () :instance-defaults @{}} :_name "Prototype" :new <function new-from-Root>}
@{:_init <function identity> :_meta @{ :prototype-allocations @{} :object-type :prototype :fields () :instance-defaults @{}} :_name "Prototype" :new <function new-from-Root>}
```

Root of the Fugue object hierarchy.

[1]: fugue.janet#L15
[1]: fugue.janet#L14

## Root*?



@@ 112,7 112,7 @@ 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.

[4]: fugue.janet#L291
[4]: fugue.janet#L285

## declare-open-multi



@@ 128,7 128,7 @@ Extending an open multimethod (see `extend-multi`) from any other
environment makes the case extension available wherever the
multimethod has been imported.

[5]: fugue.janet#L604
[5]: fugue.janet#L598

## defgeneric



@@ 143,7 143,7 @@ first argument has a method corresponding to the name of the
function, call that object 's method with the arguments. Otherwise,
evaluate `body`.

[6]: fugue.janet#L325
[6]: fugue.janet#L319

## defmethod



@@ 161,7 161,7 @@ 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`.

[7]: fugue.janet#L336
[7]: fugue.janet#L330

## defmulti



@@ 233,7 233,7 @@ repl:12:> (cat "hello" 100)
"hello #100"
```

[8]: fugue.janet#L528
[8]: fugue.janet#L522

## defproto



@@ 252,7 252,7 @@ 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`.
                                        sense...) the parent of the prototype will be set to `fugue/Root`.

`fields` should be 0 or more pairs of the following format:



@@ 263,8 263,6 @@ following attributes are currently recognized:

- `:default`: provide a default value for all new instances of this
prototype
- `:default-fn` a 0-arity function to provide the 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


@@ 285,15 283,21 @@ prototype doesn 't have that field.

`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 of the
other fields defined on this protoype.
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` conststructor. It can be defined for a prototype (see
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 not of an even length, it wil be taken as an error.

---


@@ 309,7 313,7 @@ repl:47:> (speak (:new Pekingese))
"My name is Fido and I am Extremely Small"
```

[9]: fugue.janet#L192
[9]: fugue.janet#L182

## extend-multi



@@ 327,7 331,7 @@ 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.

[10]: fugue.janet#L620
[10]: fugue.janet#L614

## fields



@@ 340,7 344,7 @@ wherever the multimethod is imported.
Return all the defined fields for `obj` and its prototype
hierarchy.

[11]: fugue.janet#L363
[11]: fugue.janet#L357

## get-type-or-proto



@@ 353,7 357,7 @@ hierarchy.
Return the prototype of `obj`, if it has one, otherwise the keyword
output of `type`.

[12]: fugue.janet#L119
[12]: fugue.janet#L109

## multimethod-types-match?



@@ 367,7 371,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)

[13]: fugue.janet#L456
[13]: fugue.janet#L450

## prototype?



@@ 379,5 383,5 @@ and using `:_` as a fallback)

Is `obj` the result of a `defproto ` call? 

[14]: fugue.janet#L284
[14]: fugue.janet#L278


M fugue.janet => fugue.janet +28 -34
@@ 8,8 8,7 @@
  @{:_meta @{:object-type :prototype
             :fields defined-fields
             :prototype-allocations @{}
             :instance-defaults @{}
             :instance-default-fns @{}}
             :instance-defaults @{}}
    :_name name})

(def Root


@@ 34,8 33,7 @@
  (let [init-args @[]
        proto-allocated-fields @[]
        proto-allocations @{}
        instance-defaults @{}
        instance-default-fns @{}]
        instance-defaults @{}]
    (loop [[field-name attrs] :in fields
           :let [key-field (keyword field-name)]]
      # Assemble list of arguments to constructor


@@ 49,18 47,14 @@
        (put proto-allocations key-field (eval proto-value)))
      # Assemble mapping of fields to default values for instances
      (when-let [default-value (attrs :default)]
        (put instance-defaults key-field (eval default-value)))
      # Assemble mapping of fields to default functions for instances
      # (eg for newly generated values)
      (when-let [default-fn (attrs :default-fn)]
        (put instance-default-fns key-field (eval default-fn))))
        (put instance-defaults key-field (eval default-value))))

    [init-args proto-allocated-fields proto-allocations instance-defaults instance-default-fns]))
    [init-args proto-allocated-fields proto-allocations instance-defaults]))

(defn- proto-form
  "Generate the def form for a Prototype."
  [name parent fields defined-fields
   _init-args proto-allocated-fields proto-allocations instance-defaults instance-default-fns]
   _init-args proto-allocated-fields proto-allocations instance-defaults]
  ~(let [parent ',parent
         object (,bare-proto (string ',name) ,defined-fields)
         proto-allocations @{}]


@@ 73,7 67,6 @@
               (get-in parent [:_meta :prototype-allocations])))

     (put-in object [:_meta :instance-defaults] ',instance-defaults)
     (put-in object [:_meta :instance-default-fns] ',instance-default-fns)

     (loop [[allocation-field allocation-value] :pairs ',proto-allocations]
       (put object allocation-field allocation-value))


@@ 93,9 86,6 @@
         (let [defaults (get-in source-of-defaults [:_meta :instance-defaults])]
           (loop [[default-key default-value] :pairs defaults]
             (put inst default-key default-value)))
         (let [default-fns (get-in source-of-defaults [:_meta :instance-default-fns])]
           (loop [[default-key default-fn] :pairs default-fns]
             (put inst default-key (default-fn))))
         # Recurse to grandparent
         (set source-of-defaults (table/getproto source-of-defaults)))



@@ 200,7 190,7 @@

  `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`.
                                          sense...) the parent of the prototype will be set to `fugue/Root`.

  `fields` should be 0 or more pairs of the following format:



@@ 211,8 201,6 @@

  - `:default`: provide a default value for all new instances of this
  prototype
  - `:default-fn` a 0-arity function to provide the 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


@@ 233,14 221,20 @@

  `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 of the
  other fields defined on this protoype.
  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` conststructor. It can be defined for a prototype (see
  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 not of an even length, it wil be taken as an error.



@@ 268,18 262,18 @@
        field-definitions (field-definitions name fields)
        [init-args] field-definitions]
    (array
      ~(def ,name
         ,(proto-docstring name defined-fields)
         (->
           ,(proto-form name
                        parent
                        fields
                        defined-fields
                        ;field-definitions)
           (put :new ,(init-form name init-args))))
      (pred-form name)
      (pred*-form name)
      ;(getters name fields))))
     ~(def ,name
        ,(proto-docstring name defined-fields)
        (->
         ,(proto-form name
                      parent
                      fields
                      defined-fields
                      ;field-definitions)
         (put :new ,(init-form name init-args))))
     (pred-form name)
     (pred*-form name)
     ;(getters name fields))))

(defn prototype?
  ```

M test/fugue.janet => test/fugue.janet +4 -2
@@ 54,7 54,8 @@
    (inst-tests Light a-light)
    (is (number? (a-light :speed)))))

(fugue/defproto Stack () data {:default-fn |@[]})
(fugue/defproto Stack () data {})
(fugue/defmethod _init Stack [self] (put self :data @[]))

(deftest mutable-default-fns
  (let [a-stack (:new Stack)


@@ 71,7 72,8 @@
    (is (== @[:ok] (b-stack :data)))))

(def inner-stack @[])
(fugue/defproto DeepStack () data {:default-fn |@[inner-stack]})
(fugue/defproto DeepStack () data {})
(fugue/defmethod _init DeepStack [self] (put self :data @[inner-stack]))

(deftest inner-references-are-not-copied
  (let [a-stack (:new DeepStack)