Claude Code Plugins

Community-maintained marketplace

Feedback

convert-erlang-scala

@aRustyDev/ai
0
0

Converts Erlang code to idiomatic Scala while preserving functional programming patterns, actor model semantics through Akka, and fault-tolerance mechanisms. Use when converting Erlang applications, libraries, OTP behaviors, or concurrent systems to Scala, including supervision trees, gen_server patterns, distributed systems, and message-passing architectures on the JVM.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name convert-erlang-scala
description Converts Erlang code to idiomatic Scala while preserving functional programming patterns, actor model semantics through Akka, and fault-tolerance mechanisms. Use when converting Erlang applications, libraries, OTP behaviors, or concurrent systems to Scala, including supervision trees, gen_server patterns, distributed systems, and message-passing architectures on the JVM.

Erlang to Scala Conversion

Overview

This skill guides the conversion of Erlang code to idiomatic Scala while maintaining functional programming principles, concurrent programming patterns via Akka, and fault-tolerance capabilities. Scala provides strong functional programming support combined with object-oriented features on the JVM, making it suitable for porting Erlang applications while gaining access to the extensive Java/Scala ecosystem.

Key Language Differences

Type Systems

  • Erlang: Dynamic typing with pattern matching
  • Scala: Static typing with type inference, algebraic data types (sealed traits), and comprehensive pattern matching

Concurrency Models

  • Erlang: Actor model with lightweight processes (BEAM VM), message passing, process isolation
  • Scala: Akka actors (JVM-based), futures/promises, parallel collections

Runtime Environment

  • Erlang: BEAM VM with hot code swapping, distributed computing, per-process garbage collection
  • Scala: JVM with comprehensive standard library, Akka for distributed systems, shared heap garbage collection

Core Conversion Patterns

1. Module and Function Definitions

Erlang:

-module(calculator).
-export([add/2, multiply/2, power/2]).

add(X, Y) -> X + Y.

multiply(X, Y) -> X * Y.

power(X, N) when N > 0 -> X * power(X, N - 1);
power(_, 0) -> 1.

Scala:

object Calculator {
  def add(x: Int, y: Int): Int = x + y

  def multiply(x: Int, y: Int): Int = x * y

  def power(x: Int, n: Int): Int = n match {
    case 0 => 1
    case n if n > 0 => x * power(x, n - 1)
    case _ => throw new IllegalArgumentException("Negative exponent")
  }
}

2. Pattern Matching and Guards

Erlang:

-spec classify(number()) -> atom().
classify(N) when N < 0 -> negative;
classify(0) -> zero;
classify(N) when N > 0 -> positive.

process_result({ok, Value}) -> {success, Value};
process_result({error, Reason}) -> {failure, Reason};
process_result(_) -> unknown.

Scala:

def classify(n: Int): Symbol = n match {
  case n if n < 0 => 'negative
  case 0 => 'zero
  case n if n > 0 => 'positive
}

// Using sealed traits for better type safety
sealed trait Result[+A]
case class Ok[A](value: A) extends Result[A]
case class Error(reason: String) extends Result[Nothing]

sealed trait ProcessedResult
case class Success(value: Any) extends ProcessedResult
case class Failure(reason: String) extends ProcessedResult
case object Unknown extends ProcessedResult

def processResult(result: Result[Any]): ProcessedResult = result match {
  case Ok(value) => Success(value)
  case Error(reason) => Failure(reason)
}

3. Records to Case Classes

Erlang:

-record(person, {name, age, email}).

create_person(Name, Age, Email) ->
    #person{name=Name, age=Age, email=Email}.

get_name(#person{name=Name}) -> Name.

update_email(Person, NewEmail) ->
    Person#person{email=NewEmail}.

Scala:

case class Person(name: String, age: Int, email: String)

def createPerson(name: String, age: Int, email: String): Person =
  Person(name, age, email)

def getName(person: Person): String = person.name

def updateEmail(person: Person, newEmail: String): Person =
  person.copy(email = newEmail)

4. Lists and List Operations

Erlang:

% List comprehensions
double_list(List) -> [X * 2 || X <- List].

filter_even(List) -> [X || X <- List, X rem 2 =:= 0].

% Recursive list processing
sum([]) -> 0;
sum([H|T]) -> H + sum(T).

map(_, []) -> [];
map(F, [H|T]) -> [F(H) | map(F, T)].

Scala:

// List operations with higher-order functions
def doubleList(list: List[Int]): List[Int] =
  list.map(_ * 2)
  // or: for (x <- list) yield x * 2

def filterEven(list: List[Int]): List[Int] =
  list.filter(_ % 2 == 0)
  // or: for (x <- list if x % 2 == 0) yield x

// Recursive list processing
def sum(list: List[Int]): Int = list match {
  case Nil => 0
  case h :: t => h + sum(t)
}

def map[A, B](f: A => B, list: List[A]): List[B] = list match {
  case Nil => Nil
  case h :: t => f(h) :: map(f, t)
}

// Built-in alternatives (preferred)
val summed = list.sum
val mapped = list.map(f)

5. Higher-Order Functions and Lambdas

Erlang:

apply_twice(F, X) -> F(F(X)).

% Anonymous functions
Increment = fun(X) -> X + 1 end,
Result = apply_twice(Increment, 5). % Result = 7

% Partial application
add(X, Y) -> X + Y.
add_five(X) -> add(5, X).

Scala:

def applyTwice[A](f: A => A, x: A): A = f(f(x))

// Anonymous functions
val increment: Int => Int = _ + 1
val result = applyTwice(increment, 5) // result = 7

// Partial application
def add(x: Int, y: Int): Int = x + y
def addFive: Int => Int = add(5, _)
// or: val addFive = (x: Int) => add(5, x)

6. Actor Model / Process Communication

Erlang:

-module(counter).
-export([start/0, increment/1, get_value/1, loop/1]).

start() ->
    spawn(fun() -> loop(0) end).

increment(Pid) ->
    Pid ! {increment, self()},
    receive
        {ok, NewValue} -> NewValue
    after 5000 ->
        timeout
    end.

get_value(Pid) ->
    Pid ! {get, self()},
    receive
        {value, V} -> V
    after 5000 ->
        timeout
    end.

loop(Count) ->
    receive
        {increment, From} ->
            NewCount = Count + 1,
            From ! {ok, NewCount},
            loop(NewCount);
        {get, From} ->
            From ! {value, Count},
            loop(Count);
        stop ->
            ok
    end.

Scala (Akka):

import akka.actor._
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}

class Counter extends Actor {
  private var count = 0

  def receive: Receive = {
    case Increment =>
      count += 1
      sender() ! Ok(count)
    case Get =>
      sender() ! Value(count)
    case Stop =>
      context.stop(self)
  }
}

// Message definitions
case object Increment
case object Get
case class Ok(value: Int)
case class Value(count: Int)
case object Stop

// Usage
object CounterExample extends App {
  val system = ActorSystem("CounterSystem")
  val counter = system.actorOf(Props[Counter], "counter")

  implicit val timeout: Timeout = 5.seconds
  import system.dispatcher

  // Fire-and-forget (like Erlang's !)
  counter ! Increment

  // Request-response (like Erlang's receive)
  val future: Future[Value] = (counter ? Get).mapTo[Value]
  val Value(count) = Await.result(future, 5.seconds)

  counter ! Stop
  system.terminate()
}

7. gen_server Pattern

Erlang:

-module(kv_store).
-behaviour(gen_server).
-export([start_link/0, get/1, put/2]).
-export([init/1, handle_call/3, handle_cast/2, terminate/2]).

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

get(Key) ->
    gen_server:call(?MODULE, {get, Key}).

put(Key, Value) ->
    gen_server:cast(?MODULE, {put, Key, Value}).

init([]) ->
    {ok, #{}}.

handle_call({get, Key}, _From, State) ->
    Result = maps:get(Key, State, undefined),
    {reply, Result, State}.

handle_cast({put, Key, Value}, State) ->
    NewState = maps:put(Key, Value, State),
    {noreply, NewState}.

terminate(_Reason, _State) ->
    ok.

Scala (Akka):

import akka.actor._

class KVStore extends Actor {
  private var state: Map[String, Any] = Map.empty

  def receive: Receive = {
    case Get(key) =>
      sender() ! state.get(key)

    case Put(key, value) =>
      state = state + (key -> value)
  }

  override def postStop(): Unit = {
    // Cleanup logic (like terminate/2)
    println("KVStore stopped")
  }
}

case class Get(key: String)
case class Put(key: String, value: Any)

// Usage
object KVStoreExample extends App {
  val system = ActorSystem("KVSystem")
  val kvStore = system.actorOf(Props[KVStore], "kvStore")

  // Cast-like (fire-and-forget)
  kvStore ! Put("name", "Scala")
  kvStore ! Put("version", 3)

  // Call-like (request-response)
  import akka.pattern.ask
  import akka.util.Timeout
  import scala.concurrent.duration._
  import scala.concurrent.Await

  implicit val timeout: Timeout = 5.seconds
  val future = (kvStore ? Get("name")).mapTo[Option[Any]]
  val result = Await.result(future, 5.seconds)
  println(s"Result: $result")

  system.terminate()
}

8. Supervision and Fault Tolerance

Erlang:

-module(my_supervisor).
-behaviour(supervisor).
-export([start_link/0, init/1]).

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    ChildSpecs = [
        #{id => worker1,
          start => {my_worker, start_link, []},
          restart => permanent,
          shutdown => 5000,
          type => worker},
        #{id => worker2,
          start => {my_worker, start_link, []},
          restart => transient,
          shutdown => 5000,
          type => worker}
    ],
    {ok, {#{strategy => one_for_one,
            intensity => 5,
            period => 10}, ChildSpecs}}.

Scala (Akka):

import akka.actor._
import scala.concurrent.duration._

class MySupervisor extends Actor {
  override val supervisorStrategy = OneForOneStrategy(
    maxNrOfRetries = 5,
    withinTimeRange = 10.seconds
  ) {
    case _: ArithmeticException => SupervisorStrategy.Resume
    case _: NullPointerException => SupervisorStrategy.Restart
    case _: IllegalArgumentException => SupervisorStrategy.Stop
    case _: Exception => SupervisorStrategy.Escalate
  }

  val worker1 = context.actorOf(Props[MyWorker], "worker1")
  val worker2 = context.actorOf(Props[MyWorker], "worker2")

  def receive: Receive = {
    case msg => worker1 ! msg
  }
}

class MyWorker extends Actor {
  def receive: Receive = {
    case work: Work => processWork(work)
  }

  def processWork(work: Work): Unit = {
    // Work processing logic
    println(s"Processing: $work")
  }

  override def preStart(): Unit = {
    println(s"${self.path.name} starting")
  }

  override def postRestart(reason: Throwable): Unit = {
    println(s"${self.path.name} restarted due to: $reason")
  }
}

case class Work(data: String)

9. Error Handling

Erlang:

safe_divide(_, 0) -> {error, division_by_zero};
safe_divide(X, Y) -> {ok, X / Y}.

% Try-catch
try_operation(Data) ->
    try
        risky_function(Data)
    catch
        error:badarg -> {error, bad_argument};
        error:Reason -> {error, Reason};
        throw:Value -> {thrown, Value}
    end.

% Let it crash philosophy
process_data(Data) ->
    % Process crashes if something goes wrong
    transform(Data).

Scala:

// Using Either for explicit error handling
sealed trait DivisionError
case object DivisionByZero extends DivisionError

def safeDivide(x: Double, y: Double): Either[DivisionError, Double] =
  if (y == 0) Left(DivisionByZero)
  else Right(x / y)

// Using Option for simple cases
def safeDivideOption(x: Double, y: Double): Option[Double] =
  if (y == 0) None
  else Some(x / y)

// Try-catch for exception handling
import scala.util.{Try, Success, Failure}

def tryOperation(data: String): Either[String, Int] =
  Try(riskyFunction(data)) match {
    case Success(value) => Right(value)
    case Failure(ex: IllegalArgumentException) => Left("bad_argument")
    case Failure(ex) => Left(ex.getMessage)
  }

// For-comprehension for chaining operations
def complexOperation(x: Int, y: Int): Either[String, Int] = for {
  divided <- safeDivide(x.toDouble, y.toDouble).left.map(_.toString)
  result <- Right(divided.toInt)
} yield result

10. Binary Pattern Matching

Erlang:

parse_header(<<Type:8, Length:16/big, Rest/binary>>) ->
    {Type, Length, Rest}.

parse_packet(<<Magic:32/big, Version:8, Data/binary>>) ->
    {Magic, Version, Data}.

encode_header(Type, Length, Data) ->
    <<Type:8, Length:16/big, Data/binary>>.

Scala:

import akka.util.ByteString
import java.nio.ByteOrder

// Using Akka ByteString
def parseHeader(bytes: ByteString): Option[(Byte, Short, ByteString)] = {
  if (bytes.length < 3) None
  else {
    val iter = bytes.iterator
    val typ = iter.getByte
    val length = iter.getShort(ByteOrder.BIG_ENDIAN)
    val rest = bytes.drop(3)
    Some((typ, length, rest))
  }
}

// Using scodec for complex binary protocols
import scodec._
import scodec.bits._
import scodec.codecs._

case class Header(typ: Int, length: Int, data: ByteVector)

val headerCodec: Codec[Header] =
  (uint8 :: uint16 :: bytes).as[Header]

// Encoding
val encoded: BitVector = headerCodec.encode(Header(1, 256, hex"deadbeef")).require

// Decoding
val decoded: Header = headerCodec.decode(encoded).require.value

11. ETS Tables to Concurrent Collections

Erlang:

start() ->
    ets:new(cache, [named_table, public, set]),
    ok.

insert(Key, Value) ->
    ets:insert(cache, {Key, Value}),
    ok.

lookup(Key) ->
    case ets:lookup(cache, Key) of
        [{Key, Value}] -> {ok, Value};
        [] -> {error, not_found}
    end.

delete(Key) ->
    ets:delete(cache, Key),
    ok.

Scala:

import java.util.concurrent.ConcurrentHashMap
import scala.jdk.CollectionConverters._

object Cache {
  private val cache = new ConcurrentHashMap[String, Any]().asScala

  def insert(key: String, value: Any): Unit =
    cache.put(key, value)

  def lookup(key: String): Option[Any] =
    cache.get(key)

  def delete(key: String): Unit =
    cache.remove(key)
}

// Or using an actor for state management
class CacheActor extends Actor {
  private var cache: Map[String, Any] = Map.empty

  def receive: Receive = {
    case Insert(k, v) =>
      cache = cache + (k -> v)
      sender() ! Done

    case Lookup(k) =>
      sender() ! cache.get(k)

    case Delete(k) =>
      cache = cache - k
      sender() ! Done
  }
}

case class Insert(key: String, value: Any)
case class Lookup(key: String)
case class Delete(key: String)
case object Done

12. Distributed Erlang to Akka Cluster

Erlang:

% Send message to named process on remote node
send_to_node(Node, ProcessName, Message) ->
    {ProcessName, Node} ! Message.

% Register process globally
register_globally(Name, Pid) ->
    global:register_name(Name, Pid).

% Call remote process
call_remote(Node, Module, Function, Args) ->
    rpc:call(Node, Module, Function, Args).

Scala (Akka Cluster):

import akka.actor._
import akka.cluster.Cluster
import akka.cluster.routing._

// Remote actor communication
object DistributedExample {
  def sendToRemote(system: ActorSystem, path: String, message: Any): Unit = {
    val selection = system.actorSelection(path)
    selection ! message
  }

  // Cluster-aware routing
  def createClusterRouter(system: ActorSystem): ActorRef = {
    system.actorOf(
      ClusterRouterPool(
        local = akka.routing.RoundRobinPool(5),
        settings = ClusterRouterPoolSettings(
          totalInstances = 20,
          maxInstancesPerNode = 5,
          allowLocalRoutees = true
        )
      ).props(Props[Worker]),
      name = "workerRouter"
    )
  }
}

// Cluster singleton for global registration
import akka.cluster.singleton._

object SingletonExample {
  def createSingleton(system: ActorSystem): ActorRef = {
    system.actorOf(
      ClusterSingletonManager.props(
        singletonProps = Props[GlobalRegistry],
        terminationMessage = PoisonPill,
        settings = ClusterSingletonManagerSettings(system)
      ),
      name = "globalRegistry"
    )
  }
}

class GlobalRegistry extends Actor {
  private var registry: Map[String, ActorRef] = Map.empty

  def receive: Receive = {
    case Register(name, ref) =>
      registry = registry + (name -> ref)
      sender() ! Registered

    case Lookup(name) =>
      sender() ! registry.get(name)
  }
}

case class Register(name: String, ref: ActorRef)
case class Lookup(name: String)
case object Registered

Conversion Strategy

Step 1: Analyze Erlang Codebase

  • Identify module structure and dependencies
  • Map OTP behaviors (gen_server, gen_statem, gen_event, supervisor)
  • Document message-passing patterns and process hierarchies
  • List external dependencies and find Scala/Java equivalents
  • Analyze distributed Erlang usage

Step 2: Design Scala Architecture

  • Plan package organization and module structure
  • Design type hierarchy using sealed traits and case classes
  • Choose concurrency framework (Akka actors, Akka Typed, or Cats Effect)
  • Select fault-tolerance strategy (Akka supervision or custom)
  • Plan distributed system architecture (Akka Cluster, gRPC)

Step 3: Convert Core Logic

  • Start with pure functions and data structures
  • Convert pattern matching to Scala's match expressions
  • Translate list operations to Scala collections
  • Migrate error handling to Either/Option types or custom ADTs
  • Convert records to case classes

Step 4: Implement Concurrency

  • Replace spawn/receive with Akka actors
  • Convert gen_server to actor-based patterns
  • Implement supervision hierarchies with Akka supervision strategies
  • Add lifecycle callbacks (preStart, postStop, postRestart)

Step 5: Handle Distribution

  • Implement Akka Cluster for distributed scenarios
  • Set up cluster routing and sharding
  • Configure cluster singleton for global state
  • Implement serialization for remote messages

Step 6: Testing and Validation

  • Port EUnit/CommonTest to ScalaTest or Specs2
  • Test concurrent behaviors with Akka TestKit
  • Validate message-passing semantics
  • Property-based testing with ScalaCheck
  • Performance testing and JVM tuning

Common Libraries and Equivalents

Erlang Scala / JVM Equivalent
gen_server Akka actors, Akka Typed
supervisor Akka supervision
gen_statem Akka FSM, Akka Typed behaviors
ETS ConcurrentHashMap, Caffeine cache
Mnesia Slick, Doobie, Quill (SQL databases)
httpc, hackney Akka HTTP client, http4s, sttp
cowboy Akka HTTP, http4s, Play Framework
jsx, jiffy (JSON) Circe, Play JSON, spray-json
lager (logging) Logback, Log4j2, scala-logging
poolboy Akka routing, HikariCP (DB)
riak_core Akka Cluster Sharding

Best Practices

1. Embrace Static Typing

  • Use Scala's type system to catch errors at compile time
  • Define sealed traits for algebraic data types
  • Use type parameters and variance for generic code
  • Leverage type classes (implicits) for polymorphism

2. Preserve Functional Patterns

  • Keep functions pure where possible
  • Use immutable data structures by default
  • Leverage for-comprehensions for sequential operations
  • Use pattern matching extensively

3. Adapt Concurrency Models

  • Use Akka actors for actor-like behavior
  • Consider Akka Typed for better type safety
  • Use futures for asynchronous computations
  • Implement backpressure with Akka Streams

4. Supervision Strategies

  • Design supervision hierarchies carefully
  • Use different strategies per error type
  • Implement lifecycle hooks (preStart, postRestart)
  • Monitor critical actors with death watch

5. Error Handling

  • Prefer Either and Option over exceptions
  • Use Try for exception-throwing operations
  • Design error ADTs with sealed traits
  • Use for-comprehensions for error propagation

6. Performance Considerations

  • Profile JVM performance regularly
  • Tune garbage collection settings
  • Use specialized collections where appropriate
  • Consider Akka Streams for backpressure
  • Benchmark actor mailbox sizes

7. Testing

  • Write unit tests with ScalaTest or Specs2
  • Use Akka TestKit for actor testing
  • Property-based testing with ScalaCheck
  • Integration testing for distributed scenarios

Example: Complete Application Conversion

Erlang Chat Server

-module(chat_server).
-behaviour(gen_server).
-export([start_link/0, join/2, leave/1, send_message/2]).
-export([init/1, handle_call/3, handle_cast/2, terminate/2]).

-record(state, {users = #{}}).

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

init([]) ->
    {ok, #state{}}.

handle_call({join, Username, Pid}, _From, State = #state{users = Users}) ->
    monitor(process, Pid),
    NewUsers = Users#{Username => Pid},
    notify_all(NewUsers, {user_joined, Username}),
    {reply, ok, State#state{users = NewUsers}};

handle_call({leave, Username}, _From, State = #state{users = Users}) ->
    NewUsers = maps:remove(Username, Users),
    notify_all(NewUsers, {user_left, Username}),
    {reply, ok, State#state{users = NewUsers}}.

handle_cast({send_message, From, Message}, State = #state{users = Users}) ->
    notify_all(Users, {message, From, Message}),
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

notify_all(Users, Msg) ->
    maps:foreach(fun(_, Pid) -> Pid ! Msg end, Users).

join(Username, Pid) ->
    gen_server:call(?MODULE, {join, Username, Pid}).

leave(Username) ->
    gen_server:call(?MODULE, {leave, Username}).

send_message(From, Message) ->
    gen_server:cast(?MODULE, {send_message, From, Message}).

Scala Chat Server

import akka.actor._
import scala.collection.immutable.Map

class ChatServer extends Actor {
  private var users: Map[String, ActorRef] = Map.empty

  def receive: Receive = {
    case Join(username, userRef) =>
      context.watch(userRef)
      users = users + (username -> userRef)
      notifyAll(UserJoined(username))
      sender() ! Joined

    case Leave(username) =>
      users.get(username).foreach(context.unwatch)
      users = users - username
      notifyAll(UserLeft(username))
      sender() ! Left

    case SendMessage(from, message) =>
      notifyAll(Message(from, message))

    case Terminated(userRef) =>
      users.find(_._2 == userRef).foreach { case (username, _) =>
        users = users - username
        notifyAll(UserLeft(username))
      }
  }

  private def notifyAll(msg: ChatEvent): Unit = {
    users.values.foreach(_ ! msg)
  }

  override def postStop(): Unit = {
    println("Chat server stopped")
  }
}

// Message protocol
sealed trait ChatCommand
case class Join(username: String, userRef: ActorRef) extends ChatCommand
case class Leave(username: String) extends ChatCommand
case class SendMessage(from: String, message: String) extends ChatCommand

sealed trait ChatResponse
case object Joined extends ChatResponse
case object Left extends ChatResponse

sealed trait ChatEvent
case class UserJoined(username: String) extends ChatEvent
case class UserLeft(username: String) extends ChatEvent
case class Message(from: String, text: String) extends ChatEvent

// User client actor
class ChatClient(username: String, server: ActorRef) extends Actor {
  override def preStart(): Unit = {
    server ! Join(username, self)
  }

  def receive: Receive = {
    case Joined =>
      println(s"$username joined the chat")

    case UserJoined(user) =>
      println(s"$user joined")

    case UserLeft(user) =>
      println(s"$user left")

    case Message(from, text) =>
      println(s"[$from]: $text")

    case SendMsg(text) =>
      server ! SendMessage(username, text)
  }

  override def postStop(): Unit = {
    server ! Leave(username)
  }
}

case class SendMsg(text: String)

// Usage example
object ChatExample extends App {
  val system = ActorSystem("ChatSystem")
  val server = system.actorOf(Props[ChatServer], "server")

  val alice = system.actorOf(Props(new ChatClient("Alice", server)), "alice")
  val bob = system.actorOf(Props(new ChatClient("Bob", server)), "bob")

  Thread.sleep(100)
  alice ! SendMsg("Hello everyone!")
  bob ! SendMsg("Hi Alice!")

  Thread.sleep(1000)
  system.terminate()
}

Advanced Topics

Hot Code Swapping

Erlang's hot code swapping has limited JVM equivalents:

  • JRebel: Commercial tool for class reloading
  • sbt-revolver: Development-time hot reloading
  • Akka Rolling Updates: For production deployments
  • Containerized deployments: Blue-green or canary deployments
  • Feature flags: Toggle functionality without redeployment

Process Migration

For Erlang's process migration:

  • Akka Cluster Sharding: Automatic entity rebalancing
  • Akka Cluster Singleton: Migrate singleton across nodes
  • Akka Persistence: State recovery after migration
  • Custom serialization: Efficient message serialization

Binary Protocols

For complex binary protocols:

  • scodec: Composable binary codecs
  • Akka ByteString: Efficient binary operations
  • java.nio.ByteBuffer: Low-level binary handling
  • Protocol Buffers: Schema-based serialization

Distributed Tracing

Monitor distributed systems:

  • Kamon: Metrics and tracing for Akka
  • OpenTelemetry: Distributed tracing standard
  • Zipkin: Distributed tracing system
  • Jaeger: Distributed tracing platform

Troubleshooting

Common Issues

Issue: Actor mailbox overflow

  • Solution: Implement bounded mailboxes, backpressure, or use Akka Streams

Issue: Memory leaks in long-running actors

  • Solution: Implement state cleanup, use Akka Timers for periodic cleanup

Issue: Shared mutable state

  • Solution: Encapsulate all mutable state within actors, use immutable messages

Issue: JVM garbage collection pauses

  • Solution: Tune GC settings, use G1GC or ZGC, reduce allocation rate

Issue: Supervision strategy not triggering

  • Solution: Ensure exceptions are thrown (not caught), verify supervisor hierarchy

Issue: Cluster split-brain

  • Solution: Configure Akka Split Brain Resolver, use lease-based strategies

Issue: Serialization errors in distributed setup

  • Solution: Configure serialization bindings, use Protocol Buffers or Avro

Issue: Performance degradation under load

  • Solution: Profile with JMC/VisualVM, tune dispatcher settings, use routing

References

Official Documentation

Libraries

Learning Resources

  • "Programming in Scala" by Martin Odersky
  • "Akka in Action" by Raymond Roestenburg
  • "Functional Programming in Scala" by Paul Chiusano
  • "Reactive Messaging Patterns with the Actor Model" by Vaughn Vernon
  • Scala documentation: https://docs.scala-lang.org/
  • Akka documentation: https://doc.akka.io/

Tools

  • sbt: Scala build tool
  • ScalaTest/Specs2: Testing frameworks
  • ScalaCheck: Property-based testing
  • Metals: Scala language server
  • IntelliJ IDEA: IDE with Scala support