
convert-erlang-scala
by aRustyDev
AI Contexts, Commands, Plugins, Docs, Examples, Specs, Etc
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
- Akka - Actor model for JVM
- Akka HTTP - HTTP server/client
- Cats - Functional programming abstractions
- Cats Effect - Functional effects
- scodec - Binary serialization
- Circe - JSON library
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
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon