~subsetpark/fugue

283a17b0976b2b361262a74e0f327dde9c519e01 — Zach Smith 3 years ago e766d2f extend-protocol v0.3.0
Call protocols open multimethods
3 files changed, 54 insertions(+), 44 deletions(-)

M README.md
M fugue.janet
M test/fugue.janet
M README.md => README.md +18 -18
@@ 57,7 57,7 @@ be selected for any descendent prototype instances.

## fugue

[Root](#Root), [allocate](#allocate), [declare-protocol](#declare-protocol), [defgeneric](#defgeneric), [defmethod](#defmethod), [defmulti](#defmulti), [defproto](#defproto), [extend-protocol](#extend-protocol), [fields](#fields), [multimethod-types-match?](#multimethod-types-match), [proto-or-type](#proto-or-type), [prototype?](#prototype)
[Root](#Root), [allocate](#allocate), [declare-open-multi](#declare-open-multi), [defgeneric](#defgeneric), [defmethod](#defmethod), [defmulti](#defmulti), [defproto](#defproto), [extend-multi](#extend-multi), [fields](#fields), [multimethod-types-match?](#multimethod-types-match), [proto-or-type](#proto-or-type), [prototype?](#prototype)

## Root



@@ 86,21 86,21 @@ all descendents of that prototype.

[2]: fugue.janet#L164

## declare-protocol
## declare-open-multi

**macro**  | [source][3]

```janet
(declare-protocol name)
(declare-open-multi name)
```

Declare a protocol, ie, a multimethod with `varfn` behaviour.
Declare an open multimethod, ie, one that can be extended.

Declaring a protocol allows it to be extended (see
`extend-protocol`) from any other environment, making the case
extension available wherever the protocol has been imported.
Extending an open multimethod (see `extend-multi`) from any other
environment makes the case extension available wherever the
multimethod has been imported.

[3]: fugue.janet#L437
[3]: fugue.janet#L447

## defgeneric



@@ 164,8 164,8 @@ Example usage :

`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
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 `_`


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

[6]: fugue.janet#L363
[6]: fugue.janet#L373

## defproto



@@ 269,23 269,23 @@ repl:47:> (speak (:new Pekingese))

[7]: fugue.janet#L15

## extend-protocol
## extend-multi

**macro**  | [source][8]

```janet
(extend-protocol protocol multi-types args & body)
(extend-multi multi multi-types args & body)
```

Extend a protocol (see `declare-protocol`) using the same syntax as
`defmulti`.
Extend an open multimethod (see `declare-open-multi`) using the same
syntax as `defmulti`.

See that function's documentation for full usage reference.

Whenever a case is added to `protocol`, that case is available
wherever the protocol is imported.
Whenever a case is added to `multi`, that case is available
wherever the multimethod is imported.

[8]: fugue.janet#L450
[8]: fugue.janet#L460

## fields


M fugue.janet => fugue.janet +17 -17
@@ 393,8 393,8 @@

  `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
  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 `_`


@@ 440,41 440,41 @@

  (with-syms [args]
    (let [cases (get-multi-cases name)
          cond-form (construct-cond name cases args)
          cond-form (construct-cond (string name) cases args)
          docstring (make-docstring cases)]
      (emit-defn name docstring args cond-form))))

(defmacro declare-protocol
(defmacro declare-open-multi
  ```
  Declare a protocol, ie, a multimethod with `varfn` behaviour.
  Declare an open multimethod, ie, one that can be extended.

  Declaring a protocol allows it to be extended (see
  `extend-protocol`) from any other environment, making the case
  extension available wherever the protocol has been imported.
  Extending an open multimethod (see `extend-multi`) from any other
  environment makes the case extension available wherever the
  multimethod has been imported.
  ```
  [name]
  ~(do (varfn ,name "Open multimethod." [] nil)
     (-> (in (dyn ',name) :ref)
         (,declare-varmulti))))

(defmacro extend-protocol
(defmacro extend-multi
  ```
  Extend a protocol (see `declare-protocol`) using the same syntax as
  `defmulti`.
  Extend an open multimethod (see `declare-open-multi`) using the same
  syntax as `defmulti`.

  See that function's documentation for full usage reference.

  Whenever a case is added to `protocol`, that case is available
  wherever the protocol is imported.
  Whenever a case is added to `multi`, that case is available
  wherever the multimethod is imported.
  ```
  [protocol multi-types args & body]
  [multi multi-types args & body]

  (let [ref (in (dyn protocol) :ref)]
  (let [ref (in (dyn multi) :ref)]
    (put-var-case ref multi-types args body)

    (with-syms [args]
      (let [cases (get-var-cases ref)
            fn-name (->> protocol (string/split "/") (last))
            fn-name (->> multi (string/split "/") (last))
            cond-form (construct-cond fn-name cases args)
            docstring (make-docstring cases "Open ")]
        (emit-varfn protocol (symbol fn-name) docstring args cond-form)))))
        (emit-varfn multi (symbol fn-name) docstring args cond-form)))))

M test/fugue.janet => test/fugue.janet +19 -9
@@ 147,14 147,24 @@
  # the earliest position.
  (is (= "x+2" (cat2 "x" 2))))

(fugue/declare-protocol to-string)
(fugue/extend-protocol to-string [:number] [n] (string "+" n))
(fugue/extend-protocol to-string [:string] [s] (string s "!"))

(deftest protocols
  # Protocols can be extended from other environments, which is a bit
  # tough to do here. So just test the basics.
  (is (= "s!" (to-string "s")))
  (is (= "+10" (to-string 10))))
(fugue/defmulti to-string [:number] [n] (string "+" n))
(do
  (fugue/defmulti to-string [:string] [s] (string s "!")))

(fugue/declare-open-multi to-string2)
(do
  (fugue/extend-multi to-string2 [:number] [n] (string "+" n))
  (fugue/extend-multi to-string2 [:string] [s] (string s "!")))

(deftest open-multimethods

  # normal multimethods obey scoping rules; a scoped def is not
  # available in enclosing (or adjacent) environments.
  (is (= "+10" (to-string 10)))
  (assert-thrown (to-string "s"))
  
  # open methods can be extended from other scopes.
  (is (= "s!" (to-string2 "s")))
  (is (= "+10" (to-string2 10))))

(run-tests!)