Files
EMPTYHEAD/src/cljs/emptyhead/newlib/delegate.cljs
2026-01-25 17:12:19 +01:00

46 lines
1.9 KiB
Clojure

(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)))