Skip to content

Instantly share code, notes, and snippets.

@martintrojer
Created May 17, 2012 05:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save martintrojer/2716711 to your computer and use it in GitHub Desktop.
Save martintrojer/2716711 to your computer and use it in GitHub Desktop.
clojure-akka-vision
;; An (imaginary) actor macro takes an initial state and callback fns.
;; (actor {} (fn1 [state message]) (fn2 [state message) ...)
;; The most obvious callback fn is (receive [state message) that is called when a
;; message is consumed from the agents mailbox. receive is called with the old state
;; and the message as parameters -- it returns the new state.
;; sender is bound to the senders pid.
;; other callbacks are stuff broken link detection etc.
;; (spawn) creates a lightweight actor process, also has remote-actor semantics
;; (tell) is used to send messages to an spawned actor's pid.
(def hello-actor
(actor {:world-actor
(spawn
(actor {}
(receive [state [message-type word :as message]]
(condp = message-type
:hello (do
(tell sender (str (.toUpperCase word) "world!"))
state)
(unhandled message)))))}
(receive [state [message-type word]]
(condp = message-type
:start (do
(tell (:world-actor state) [:hello "hello"])
state)
(do
(println (str "Received message:" word))
(shutdown)))))
(let [pid (spawn hello-actor)]
(tell pid [:start]))
(ns cljakka.vision
(:import [akka.actor CljActor ActorSystem Props]))
;; I want to be able to create a class based on an "Actor" interface.
;; It's important that both the class and object can be defined and created
;; on the fly in the repl.
(defrecord hello-actor [world-actor]
CljActor
;; In idiomatic Clojure, I don't want to create a Class for each message,
;; instead I want (like in Erlang) all messages to be a basic collection
;; (like vector) and switch (match) on a keyword (atom) on one of the positions.
(onReceice [this [message-type word :as message]]
(condp = message-type
:start (.tell world-actor [:hello "hello"])
(do ;; else case
(println (str "Received message:" word))
(-> this .getContext .getSystem .shutdown)))))
(defrecord world-actor []
CljActor
(onReceive [this [message-type word :as message]]
(cond = message-type
:hello (.tell (.getSender this)
[(str (.toUpperCase word) "world!")])
;; else case
(.unhandled this message))))
;; The defrecords above creates a new class that implements the new interface,
;; it is then instanciated like (world-actor.)
;; There are other (niftier) ways in Clojure to create an object directly
;; on an interface directly without needing to define the class first
;; (with proxy and reify) - this create anonymous classes and is powerful.
;; However, this totally screws up Akka's factories, but I wants it! :)
(def world-actor2
(reify CljActor
(onRecieve [this message]
(.unhandled this message))))
;; Now, it is possible to generate a "proper" class like in the java examples.
;; You can do this in clojure with (gen-class), but that requires a compile
;; step. Which is for a clojure developer appaling. We live and
;; breathe in the REPL. See how awful it becomes;
;; https://github.com/martintrojer/clojak/blob/master/src/clojak/core.clj
(defn -main [& args]
(let [system (ActorSystem.)
;; here system.actorOf takes an object!
world-act (.actorOf system (world-actor.))
;; or perhaps the Props ctor takes an object?
world-act2 (.actorOf system (Props. (world-actor.)))
hello-act (.actorOf system (Props. (hello-actor. world-act)))
;; or Props takes the class, but how do create it with arguments?
hello-act2 (.actorOf system (Props. hello-actor))]
(.tell hello-act [:start])))
package akka.tutorial.first.java;
import akka.actor.ActorRef;
import akka.actor.UntypedActor;
import akka.actor.UntypedActorFactory;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class Hello {
public static void main(String[] args)
{
final ActorSystem system = ActorSystem.create("hellokernel");
Props props = new Props().withCreator(new UntypedActorFactory() {
public UntypedActor create() {
return new HelloActor();
}
});
ActorRef helloActor = system.actorOf(props);
helloActor.tell("start");
}
public static class HelloActor extends UntypedActor {
final ActorRef worldActor =
getContext().actorOf(new Props(new UntypedActorFactory() {
public UntypedActor create() {
return new WorldActor();
}
}));
public void onReceive(Object message) {
if (message instanceof String) {
String strmess = (String) message;
if (strmess == "start")
worldActor.tell("Hello", getSelf());
else
System.out.println("Received message '%s'".format((String)message));
}
else unhandled(message);
}
}
public static class WorldActor extends UntypedActor {
public void onReceive(Object message) {
if (message instanceof String)
getSender().tell(((String)message).toUpperCase() + " world!");
else unhandled(message);
}
}
}
package sample.hello
import akka.actor.{ ActorSystem, Actor, Props }
case object Start
object Main {
def main(args: Array[String]): Unit = {
val system = ActorSystem()
system.actorOf(Props[HelloActor]) ! Start
}
}
class HelloActor extends Actor {
val worldActor = context.actorOf(Props[WorldActor])
def receive = {
case Start ⇒ worldActor ! "Hello"
case s: String ⇒
println("Received message: %s".format(s))
context.system.shutdown()
}
}
class WorldActor extends Actor {
def receive = {
case s: String ⇒ sender ! s.toUpperCase + " world!"
}
}
@viktorklang
Copy link

Sorry, wonT have time, but you can check what we've done with TypedActors: https://github.com/akka/akka/blob/master/akka-actor/src/main/scala/akka/actor/TypedActor.scala#L217

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment