- List Operations & Iteration
- Folding (Left & Right)
- Reduce (No Initial Value)
- Scan (Running Totals)
- Picking / First Match
- Take, Drop & Slice Operations
- Zip & Unzip Operations
- Grouping & Partitioning
- Tree Folding / Structural Recursion
- Stream Processing (Infinite/Lazy)
- Conditional Branching & Pattern Matching
- Error Handling
- Concurrency Models
- Parallel Processing
- Laziness Models
- Performance Comparison
- When to Choose Which Language?
Transform each element in a collection.
| Scala | Erlang | Rust |
|---|---|---|
xs.map(_ + 1) |
lists:map(fun(X)->X+1 end, Xs). |
xs.iter().map(|x| x+1).collect() |
Example Output:
[1,2,3] → [2,3,4]
Keep only elements that satisfy a predicate.
| Scala | Erlang | Rust |
|---|---|---|
xs.filter(_ > 5) |
lists:filter(fun(X)->X>5 end, Xs). |
xs.iter().filter(|x| **x > 5).collect() |
Example Output:
[3,6,2,9] → [6,9]
Map and then flatten nested structures.
| Scala | Erlang | Rust |
|---|---|---|
xs.flatMap(x => List(x,x)) |
lists:flatmap(fun(X)->[X,X] end, Xs). |
xs.iter().flat_map(|x| vec![x,x]).collect() |
Example Output:
[1,2,3] → [1,1,2,2,3,3]
Execute an action for each element (non-functional, but common).
| Scala | Erlang | Rust |
|---|---|---|
xs.foreach(println) |
lists:foreach(fun(X)->io:format("~p~n",[X]) end, Xs). |
xs.iter().for_each(|x| println!("{}", x)) |
✅ Safe for large lists (tail-recursive/iterative)
Processes elements from left to right.
| Scala | Erlang | Rust |
|---|---|---|
xs.foldLeft(0)(_ + _) |
lists:foldl(fun(X,A)->A+X end, 0, Xs). |
xs.iter().fold(0, |a,x| a+x) |
Execution Order:
[1,2,3] with + and 0:
((0+1)+2)+3 = 6
✅ Useful for building lists
Processes elements from right to left.
| Scala | Erlang | Rust |
|---|---|---|
xs.foldRight(0)(_ + _) |
lists:foldr(fun(X,A)->X+A end, 0, Xs). |
xs.iter().rev().fold(0, |a,x| a+x) |
Execution Order:
[1,2,3] with + and 0:
1+(2+(3+0)) = 6
Tip: Use foldLeft for operations where order doesn't matter (like sum). Use foldRight for operations like building lists in correct order.
Uses first element as initial accumulator.
| Scala | Erlang | Rust |
|---|---|---|
xs.reduce(_ + _) |
[H|T] = Xs, lists:foldl(fun(X,A)->A+X end, H, T). |
xs.iter().copied().reduce(|a,b| a+b) |
Note: Fails on empty lists! Use fold with an initial value for safety.
| Scala | Erlang | Rust |
|---|---|---|
xs.reduceRight(_ + _) |
[H|T] = lists:reverse(Xs), lists:foldl(fun(X,A)->X+A end, H, T). |
xs.iter().rev().copied().reduce(|a,b| a+b) |
Produces intermediate results at each step (like a fold, but keeps all intermediate values).
| Scala | Erlang | Rust |
|---|---|---|
xs.scanLeft(0)(_ + _) |
Manual implementation with foldl and accumulator list |
xs.iter().scan(0, |a,x| {*a+=x; Some(*a)}).collect() |
Example:
[1,2,3].scanLeft(0)(_ + _) → [0,1,3,6]
| Scala | Erlang | Rust |
|---|---|---|
xs.scanRight(0)(_ + _) |
Manual foldr logic |
Reverse, scan, then reverse result |
Rust Example:
let mut out: Vec<i32> = xs.iter().rev()
.scan(0, |a,x| {*a+=x; Some(*a)})
.collect();
out.reverse();Find the first element matching a condition.
xs.collectFirst { case x if x > 10 => x }
// Returns: Option[Int]pick([H|T]) when H > 10 -> {ok,H};
pick([_|T]) -> pick(T);
pick([]) -> none.xs.iter().find(|x| **x > 10)
// Returns: Option<&i32>Alternative: position to get the index instead of the value.
| Scala | Erlang | Rust |
|---|---|---|
xs.take(3) |
lists:sublist(Xs, 3). |
xs.iter().take(3).collect() |
| Scala | Erlang | Rust |
|---|---|---|
xs.drop(2) |
lists:nthtail(2, Xs). |
xs.iter().skip(2).collect() |
| Scala | Erlang | Rust |
|---|---|---|
xs.slice(1, 4) |
lists:sublist(Xs, 2, 3). |
&xs[1..4] or xs.iter().skip(1).take(3) |
| Scala | Erlang | Rust |
|---|---|---|
xs.zip(ys) |
lists:zip(Xs, Ys). |
xs.iter().zip(ys.iter()) |
Example:
[1,2,3].zip(['a','b','c']) → [(1,'a'), (2,'b'), (3,'c')]
| Scala | Erlang | Rust |
|---|---|---|
pairs.unzip |
lists:unzip(Pairs). |
pairs.iter().cloned().unzip() |
| Scala | Erlang | Rust |
|---|---|---|
xs.zipWithIndex |
Manual with recursion | xs.iter().enumerate() |
| Scala | Erlang | Rust |
|---|---|---|
xs.groupBy(_ % 2) |
Manual with maps/dicts | Use itertools crate or manual implementation |
Scala Example:
[1,2,3,4].groupBy(_ % 2) → Map(0 -> [2,4], 1 -> [1,3])
| Scala | Erlang | Rust |
|---|---|---|
xs.partition(_ > 5) |
lists:partition(fun(X)->X>5 end, Xs). |
xs.iter().partition(|x| **x > 5) |
Returns: Two collections: (elements matching, elements not matching)
| Scala | Erlang | Rust |
|---|---|---|
xs.distinct |
Use sets: sets:to_list(sets:from_list(Xs)) |
xs.iter().collect::<HashSet<_>>() then convert back |
Scala:
sealed trait Tree
case class Leaf(v: Int) extends Tree
case class Node(l: Tree, r: Tree) extends TreeErlang:
{leaf, V}.
{node, Left, Right}.Rust:
enum Tree {
Leaf(i32),
Node(Box<Tree>, Box<Tree>)
}Scala:
def foldTree[A](t: Tree)(leaf: Int => A)(combine: (A, A) => A): A =
t match {
case Leaf(v) => leaf(v)
case Node(l, r) =>
combine(
foldTree(l)(leaf)(combine),
foldTree(r)(leaf)(combine)
)
}
// Example: Sum all leaves
foldTree(tree)(identity)(_ + _)Erlang:
fold_tree({leaf, V}, Leaf, _) ->
Leaf(V);
fold_tree({node, L, R}, Leaf, Combine) ->
Combine(
fold_tree(L, Leaf, Combine),
fold_tree(R, Leaf, Combine)
).Rust:
fn fold_tree<F, C>(t: &Tree, leaf: &F, combine: &C) -> i32
where
F: Fn(i32) -> i32,
C: Fn(i32, i32) -> i32,
{
match t {
Tree::Leaf(v) => leaf(*v),
Tree::Node(l, r) => {
combine(
fold_tree(l, leaf, combine),
fold_tree(r, leaf, combine)
)
}
}
}
// Example: Sum all leaves
fold_tree(&tree, &|x| x, &|a, b| a + b)Height:
def height(t: Tree): Int = foldTree(t)(_ => 1)(_ max _ + 1)Count Leaves:
def countLeaves(t: Tree): Int = foldTree(t)(_ => 1)(_ + _)val naturals = LazyList.from(1)
val evens = naturals.filter(_ % 2 == 0)
val first10Evens = evens.take(10).toList
// [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
// Fibonacci sequence
val fibs: LazyList[Int] = 0 #:: 1 #:: fibs.zip(fibs.tail).map { case (a, b) => a + b }% Infinite stream generator
gen(N) -> fun() -> {N, gen(N+1)} end.
% Usage
Stream = gen(1),
{Val1, Stream2} = Stream(),
{Val2, Stream3} = Stream2().let evens: Vec<i32> = (1..)
.filter(|x| x % 2 == 0)
.take(10)
.collect();
// Fibonacci
let fibs = std::iter::successors(Some((0, 1)), |&(a, b)| Some((b, a + b)))
.map(|(a, _)| a);x match {
case 0 => "zero"
case n if n > 0 => "positive"
case _ => "negative"
}
// Pattern matching on types
result match {
case Some(value) => s"Got $value"
case None => "Got nothing"
}case X of
0 -> zero;
N when N > 0 -> positive;
_ -> negative
end.
% Pattern matching in function heads
process({ok, Value}) -> Value;
process({error, Reason}) -> {failed, Reason}.match x {
0 => "zero",
n if n > 0 => "positive",
_ => "negative"
}
// Pattern matching with enums
match result {
Ok(value) => println!("Success: {}", value),
Err(e) => eprintln!("Error: {}", e),
}
// Destructuring
match point {
Point { x: 0, y } => println!("On y-axis at {}", y),
Point { x, y: 0 } => println!("On x-axis at {}", x),
Point { x, y } => println!("At ({}, {})", x, y),
}Option:
Some(3) // Has value
None // No value
// Chaining
val result = someOption
.map(_ + 1)
.filter(_ > 5)
.getOrElse(0)Either:
Right(value) // Success case
Left(error) // Error case
// Chaining
for {
a <- Right(1)
b <- Right(2)
c <- Right(3)
} yield a + b + cTry:
Try(riskyOperation()) match {
case Success(value) => value
case Failure(exception) => handleError(exception)
}{ok, Value} % Success
{error, Reason} % Error
% Pattern matching
case read_file(Path) of
{ok, Data} -> process(Data);
{error, enoent} -> file_not_found;
{error, Reason} -> {failed, Reason}
end.Result:
Ok(v) // Success
Err(e) // Error
// Chaining with ?
fn process() -> Result<i32, String> {
let a = operation1()?; // Returns early if Err
let b = operation2()?;
Ok(a + b)
}
// Combinators
result
.map(|x| x + 1)
.and_then(|x| other_operation(x))
.unwrap_or(0)Option:
Some(x) // Has value
None // No value
// Combinators
option
.map(|x| x + 1)
.filter(|x| *x > 5)
.unwrap_or(0)Comparison:
- Rust and Scala: Expressive algebraic types with rich combinators
- Erlang: Simpler but extremely practical for distributed systems
Features:
- BEAM VM lightweight processes (can spawn 100k+ easily)
- Message-passing via mailboxes
- Actor model with supervision trees
- Hot code loading without downtime
- "Let it crash" philosophy with automatic restarts
- Built-in fault tolerance
Example:
% Spawn a process
Pid = spawn(fun() -> loop() end),
% Send a message
Pid ! {message, Data},
% Receive a message
receive
{message, Data} -> process(Data);
{stop} -> exit(normal)
after 5000 ->
timeout
end.Akka Actors (Erlang-inspired):
class MyActor extends Actor {
def receive = {
case Message(data) =>
sender() ! Response(process(data))
}
}
val system = ActorSystem("MySystem")
val actor = system.actorOf(Props[MyActor])
actor ! Message("data")Futures & Promises:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
val future = Future {
expensiveComputation()
}
future.map(result => result + 1)
.recover { case e: Exception => 0 }ZIO / Cats Effect (Pure FP):
val program = for {
a <- ZIO.succeed(1)
b <- ZIO.succeed(2)
_ <- Console.printLine(s"Sum: ${a + b}")
} yield a + bThreads:
use std::thread;
let handle = thread::spawn(|| {
expensive_computation()
});
let result = handle.join().unwrap();Async/Await:
async fn fetch_data() -> Result<String, Error> {
let response = reqwest::get("https://api.example.com")
.await?;
response.text().await
}
#[tokio::main]
async fn main() {
let result = fetch_data().await;
}Channels:
use std::sync::mpsc;
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send("message").unwrap();
});
let received = rx.recv().unwrap();val xs = (1 to 1000000).toVector
// Sequential
xs.map(_ + 1)
// Parallel
xs.par.map(_ + 1)
// With custom thread pool
val customTaskSupport = new ForkJoinTaskSupport(
new ForkJoinPool(8)
)
xs.par.tasksupport = customTaskSupportuse rayon::prelude::*;
let xs: Vec<i32> = (1..1000000).collect();
// Parallel iteration
let sum: i32 = xs.par_iter()
.map(|x| x + 1)
.sum();
// Parallel sort
let mut data = vec![5, 2, 8, 1, 9];
data.par_sort();% Parallel map using processes
pmap(F, List) ->
Parent = self(),
Pids = [spawn(fun() ->
Parent ! {self(), F(Item)}
end) || Item <- List],
[receive {Pid, Result} -> Result end || Pid <- Pids].
% Usage
Results = pmap(fun(X) -> X * 2 end, [1,2,3,4,5]).| Language | Lazy Model | Details |
|---|---|---|
| Scala | LazyList, by-name params (=> A), lazy vals, streaming libs (FS2) |
Structured laziness with clear evaluation semantics |
| Erlang | Lazy via closures or message-driven streams | Message-based laziness, excellent for distributed streaming |
| Rust | Iterators are lazy by default | Zero-cost abstractions, only evaluated when consumed |
Scala:
def lazyComputation: LazyList[Int] = {
println("Computing...")
LazyList(1, 2, 3)
}
// "Computing..." only printed when evaluatedRust:
let lazy_iter = (1..100)
.map(|x| {
println!("Processing {}", x);
x * 2
});
// Nothing printed until .collect() or iterationErlang:
lazy_seq(N) ->
fun() ->
[N | lazy_seq(N+1)]
end.- Zero-cost abstractions: Iterators compile to the same code as hand-written loops
- No garbage collection: Predictable performance
- Systems-level control: Direct memory management
- Perfect for: Low-latency systems, embedded, game engines, HFT
Benchmark Example: Numeric processing is 2-5x faster than Scala, 10-20x faster than Erlang
- JIT compilation: Optimizes hot paths at runtime
- Rich type system: Enables compiler optimizations
- Overhead: Slight compared to Rust due to JVM and GC
- Perfect for: Business logic, data processing, backend services
Benchmark Example: Near-Java performance for most operations
- Slowest numeric performance: Not optimized for number crunching
- Unmatched concurrency: Handles millions of lightweight processes
- Extreme reliability: 99.9999999% uptime (9 nines)
- Perfect for: Telecom, chat systems, distributed databases, fault-tolerant systems
Key Insight: Choose Erlang when uptime and concurrency matter more than raw speed
Strengths:
- Complex FP data models (ADTs, type classes, pattern matching)
- Stream processing pipelines (FS2, Akka Streams, ZIO Streams)
- JVM ecosystem integration required (Kafka, Spark, Cassandra)
- Need both expressiveness AND reasonable performance
- Large team with mixed FP/OOP experience
Ideal Projects:
- Backend microservices
- Data engineering pipelines
- Financial systems with complex business logic
- Real-time analytics
Example Companies: Twitter (used heavily), LinkedIn, Spotify, Netflix
Strengths:
- Massive concurrency (100k+ simultaneous connections)
- 24/7 uptime with hot code reloading (no downtime deploys)
- Distributed systems with network partitions
- Fault tolerance is non-negotiable
- "Let it crash" philosophy fits your architecture
Ideal Projects:
- Chat/messaging platforms (WhatsApp)
- Telecom switches
- IoT device management
- Real-time bidding systems
- Game servers with many concurrent players
Example Companies: WhatsApp (2M+ connections per server), Ericsson, Discord, RabbitMQ
Strengths:
- Maximum performance required (C/C++ level)
- Systems programming (OS, drivers, embedded)
- Memory safety without GC overhead
- Async servers at blazing speed
- WebAssembly compilation needed
- Zero runtime overhead
Ideal Projects:
- Game engines
- Embedded systems
- CLI tools
- High-frequency trading
- Blockchain/crypto
- Browser engines, databases
Example Companies: Mozilla (Firefox), Dropbox, AWS (Firecracker), Discord (switched from Go)
Functions that take functions as arguments or return functions.
// Scala
def twice(f: Int => Int): Int => Int =
x => f(f(x))
val addTwo = (x: Int) => x + 2
twice(addTwo)(5) // 9| Scala | Erlang | Rust |
|---|---|---|
f compose g or f andThen g |
Manual: fun(X) -> f(g(X)) end |
Manual or use crates |
Scala Example:
val f = (x: Int) => x + 1
val g = (x: Int) => x * 2
val h = f compose g // g then f
h(3) // 7| Scala | Erlang | Rust |
|---|---|---|
def add(x: Int)(y: Int) = x + y |
Manual with closures | Manual with closures |
Scala Example:
def add(x: Int)(y: Int) = x + y
val add5 = add(5) _
add5(3) // 8| Language | Default | Notes |
|---|---|---|
| Scala | Collections immutable by default | val for immutable, var for mutable |
| Erlang | Everything immutable | Cannot modify data after creation |
| Rust | Immutable by default | let immutable, let mut mutable |
Scala:
- Scala Documentation
- Book: "Functional Programming in Scala" (Red Book)
- Scala Exercises
Erlang:
- Learn You Some Erlang
- Erlang Official Docs
- Book: "Programming Erlang" by Joe Armstrong
Rust:
Scala:
- Akka (actors), Cats/Cats Effect (FP), ZIO (effect system)
- Play Framework, http4s (web)
- Spark, Flink (big data)
Erlang/Elixir:
- OTP (framework for building distributed systems)
- Phoenix (web framework, Elixir)
- RabbitMQ, CouchDB (databases)
Rust:
- Tokio (async runtime)
- Actix, Axum, Rocket (web)
- Serde (serialization), Rayon (parallelism)
| Feature | Scala | Erlang | Rust |
|---|---|---|---|
| Speed | ⚡⚡⚡ | ⚡ | ⚡⚡⚡⚡⚡ |
| Concurrency | ⚡⚡⚡ | ⚡⚡⚡⚡⚡ | ⚡⚡⚡⚡ |
| Type Safety | ⚡⚡⚡⚡⚡ | ⚡⚡ | ⚡⚡⚡⚡⚡ |
| FP Features | ⚡⚡⚡⚡⚡ | ⚡⚡⚡⚡ | ⚡⚡⚡ |
| Learning Curve | Medium-Hard | Medium | Hard |
| Ecosystem | Large (JVM) | Specialized | Growing Fast |
| Best For | Backend/Data | Distributed/Telecom | Systems/Performance |
Last Updated: 2025
License: Feel free to share and modify this cheat sheet!