diff --git a/src/prop_net/propagator.clj b/src/prop_net/propagator.clj index 936d785..75c774d 100644 --- a/src/prop_net/propagator.clj +++ b/src/prop_net/propagator.clj @@ -1,45 +1,53 @@ -(ns prop-net.propagator - (:require [prop-net.cell :as c])) +(ns prop-net.propagator) + +(def ^:private no-output []) +(defn- no-output? [output] + (= output no-output)) (defprotocol IPropagator - (inputs [this]) - (output [this]) - (add-input! [this input-cell]) - (add-output! [this output-cell]) - (apply! [this])) + (inputs + [this] + "returns the functions to be called to retrieve the propagators inputs") + (output + [this] + "returns the function to be called to set the result content of output cell") + (add-input! + [this input-lookup] + "add an input function to retrieve the content of a cell") + (add-output! + [this output-set!] + "add an output function to set the content of a cell") + (apply! + [this] + "applies the propagator function to results of all input-lookup + calls and calls the output function with the result")) (defrecord Propagator - [inputs output function] + [input-getters output-setter function] IPropagator - (inputs [this] (deref (:inputs this))) - (output [this] (deref (:output this))) - (add-input! [this input-cell] - ;; this is some java shenanigans prop_net.cell.Cell - (when (not (instance? prop_net.cell.Cell input-cell)) - (throw (ex-info "Input must be a cell" {:input input-cell}))) - (let [old-inputs (deref (:inputs this))] - (do (swap! (:inputs this) conj input-cell) - :ok))) - (add-output! [this output-cell] - ;; TODO for some reason instance? is not evaluating to true here, but types should really be checked - ;; from the net itself - ;; (when (not (instance? prop_net.cell.Cell output-cell)) - ;; (throw (ex-info "Output must be a cell" {:output output-cell - ;; :type (type output-cell)}))) - (if (c/nothing? (deref (:output this))) - (do (reset! (:output this) output-cell) + (inputs [this] (deref (:input-getters this))) + (output [this] (deref (:output-setter this))) + (add-input! [this input-func] + (do (swap! (:input-getters this) conj input-func) + :ok)) + (add-output! [this output-set!] + (if (no-output? (output this)) + (do (reset! (:output-setter this) output-set!) :ok) - (throw (ex-info "Output already set" {:propagator this :conflict output})))) + (throw (ex-info "Output cell already present" {:propagator this :conflict output})))) + ;; apply will call output with the result of calling :function with all inputs (apply! [this] - (let [input-cells (deref (:inputs this)) - output-cell (deref (:outputs this))] - (c/add-content! output-cell (apply (:function this) - (map c/content input-cells)))))) + (let [input-getters (vec (inputs this)) + output-set! (output this)] + (output-set! (apply (:function this) + ;; there might be a cleaner way to apply each lookup? + (map (fn [lookup] (lookup)) + input-getters)))))) (defn function->propagator [func] (map->Propagator - {:inputs (atom #{}) - :output (atom c/nothing) + {:input-getters (atom #{}) + :output-setter (atom no-output) :function func}))