-
-
Save ulises/4c102e8c225c39037707 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns lcc-clojure.actors | |
(:require [clojure.tools.logging :as logging])) | |
(def actors (atom {})) | |
(defn msg | |
([from clause payload] | |
{:sender from :clause clause :payload payload}) | |
([from clause] | |
(msg from clause nil))) | |
(defn msg-handler | |
"Returns the handler for the incoming message. Returns nil if the message is not listed as a valid clause." | |
[clauses msg] | |
(get clauses msg)) | |
(defn act | |
"Act message sent to actors to react to an incoming message." | |
[old-f msg & payload] | |
(when (fn? old-f) | |
(old-f old-f msg))) | |
(defn receive | |
"Receive is the main spirit of actors. It takes a map where keys are the messages and values the corresponding handlers." | |
[clauses] | |
{:pre (map? clauses)} | |
(fn [old-f msg] | |
(if-let [f (msg-handler clauses (:clause msg))] | |
(f (dissoc msg :clause)) | |
old-f))) | |
(defn ! | |
"Sends the actor message msg." | |
([actor from clause] | |
(send-off actor act (msg from clause))) | |
([actor from clause payload] | |
(send-off actor act (msg from clause payload)))) | |
(defn !- | |
"Looks up the actor by name in the registry and then sends it the message msg." | |
([name from msg] | |
(when-let [actor (get @actors name)] | |
(! actor from msg))) | |
([name from msg payload] | |
(when-let [actor (get @actors name)] | |
(! actor from msg payload)))) | |
(defn all-actors | |
"Returns a list of all registered actors." | |
[] | |
(vals @actors)) | |
(defn clear-actors | |
"Clears the actors' registry." | |
[] | |
(reset! actors {})) | |
(defn spawn | |
"Spawns and registers a new named actor with handler f." | |
[name f & args] | |
(let [actor (agent nil)] | |
(swap! actors assoc name actor) | |
(send-off actor (fn [& _] (apply f args))) | |
actor)) | |
;;; ping-pong example | |
;;; use *agent* instead of self | |
(defn pong [] | |
(receive {:ping (fn [{:keys [sender payload]}] | |
(do (logging/info "[PONG] ping from:" sender "payload:" payload) | |
(when sender (! sender *agent* :pong)) | |
(pong))) | |
:stop (fn [& _] (logging/info "[PONG] stopping."))})) | |
(defn ping [pong n] | |
(if (zero? n) | |
(do (! pong *agent* :stop) | |
(logging/info "[PING] done.")) | |
(do (! pong *agent* :ping (str "pings left " n)) | |
(receive {:pong (fn [{:keys [sender payload]}] | |
(logging/info "[PING] pong received from " sender "with payload:" payload) | |
(ping sender (dec n)))})))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment