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)