(ns emptyhead.newlib.delegate "Delegation: treat one message as though it were another, enabling behaviour sharing and polymorphism." (:require [emptyhead.thought.crud :as thought] [emptyhead.idea.crud :as idea] [emptyhead.thought.define :as def] [emptyhead.thought.eval :as eval] [emptyhead.thought.extend :as extend] [emptyhead.newlib.core])) (defn remove-delegate "Undelegate `del` from `msg`." [msg del] (let [remover (thought/register-thought! [:EH :CORE :NOP] :ext-stages [[:EH :MSG :REMOVE-DELEGATE msg del]])] (eval/execute! remover))) ;; Forget idea stored in `thought`'s data. Used to make destructors. (def/define! [:EH :IDEA :FORGET] (fn [thought parent] (idea/forget-idea! (thought/data thought)) [parent nil])) ;; FIXME Delegate order is currently undefined! Give this a proper order. ;; FIXME needs to be called with a list as second arg?? (defn add-delegate "Register `del` as a delegate for `msg`: implementations defined for `del` will now run trigger on `msg` as well. For instance, if message =(:foo :bar)= is defined and we =(add-delegate :quux :bar)= then =(:foo :quux)= will alias to =(:foo :bar)=. Note that this also works on the left hand side." [msg del] (remove-delegate msg del) (let [delegator (thought/register-thought! [:EH :IO :RETURN] :data del :transient false) remover (thought/register-thought! [:EH :IDEA :FORGET] :data delegator)] (extend/register-extension! delegator [:EH :MSG :DELEGATE msg]) (extend/register-extension! remover [:EH :MSG :REMOVE-DELEGATE msg del]))) (defn get-delegates "Get possible delegates for `msg`." [msg] (let [stage [:EH :MSG :DELEGATE msg] dels (-> (thought/register-thought! [:EH :CORE :CONTAINER] :ext-stages [stage]) eval/execute! thought/empty-stack second)] (mapcat #(cons % (get-delegates %)) dels)))