Skip to content

Instantly share code, notes, and snippets.

@ulises
Created November 12, 2012 13:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ulises/4c102e8c225c39037707 to your computer and use it in GitHub Desktop.
Save ulises/4c102e8c225c39037707 to your computer and use it in GitHub Desktop.
(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