Skip to content

Instantly share code, notes, and snippets.

@allen-munsch
Created November 25, 2025 00:55
Show Gist options
  • Select an option

  • Save allen-munsch/b3dfb2af7256f2a96981ef87f0bd6879 to your computer and use it in GitHub Desktop.

Select an option

Save allen-munsch/b3dfb2af7256f2a96981ef87f0bd6879 to your computer and use it in GitHub Desktop.
THE ULTIMATE FUNCTIONAL PROGRAMMING CHEAT SHEET Scala • Erlang • Rust

🚀 THE ULTIMATE FUNCTIONAL PROGRAMMING CHEAT SHEET

Scala • Erlang • Rust


Table of Contents

  1. List Operations & Iteration
  2. Folding (Left & Right)
  3. Reduce (No Initial Value)
  4. Scan (Running Totals)
  5. Picking / First Match
  6. Take, Drop & Slice Operations
  7. Zip & Unzip Operations
  8. Grouping & Partitioning
  9. Tree Folding / Structural Recursion
  10. Stream Processing (Infinite/Lazy)
  11. Conditional Branching & Pattern Matching
  12. Error Handling
  13. Concurrency Models
  14. Parallel Processing
  15. Laziness Models
  16. Performance Comparison
  17. When to Choose Which Language?

1. LIST OPERATIONS & ITERATION

1.1 map

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]


1.2 filter

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]


1.3 flatMap

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]


1.4 forEach / Side Effects

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))

2. FOLDING (LEFT & RIGHT)

2.1 Left Fold

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


2.2 Right Fold

Useful for building lists
⚠️ Not stack-safe in Scala/Erlang for huge 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.


3. REDUCE (NO INITIAL VALUE)

3.1 Reduce / ReduceLeft

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.


3.2 ReduceRight

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)

4. SCAN (RUNNING TOTALS)

4.1 scanLeft

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]


4.2 scanRight

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();

5. PICKING / FIRST MATCH

Find the first element matching a condition.

Scala — collectFirst

xs.collectFirst { case x if x > 10 => x }
// Returns: Option[Int]

Erlang — Pattern Matching

pick([H|T]) when H > 10 -> {ok,H};
pick([_|T]) -> pick(T);
pick([]) -> none.

Rust — find

xs.iter().find(|x| **x > 10)
// Returns: Option<&i32>

Alternative: position to get the index instead of the value.


6. TAKE, DROP & SLICE OPERATIONS

6.1 Take (first N elements)

Scala Erlang Rust
xs.take(3) lists:sublist(Xs, 3). xs.iter().take(3).collect()

6.2 Drop (skip first N elements)

Scala Erlang Rust
xs.drop(2) lists:nthtail(2, Xs). xs.iter().skip(2).collect()

6.3 Slice (range of elements)

Scala Erlang Rust
xs.slice(1, 4) lists:sublist(Xs, 2, 3). &xs[1..4] or xs.iter().skip(1).take(3)

7. ZIP & UNZIP OPERATIONS

7.1 Zip (combine two lists)

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')]


7.2 Unzip (split pairs)

Scala Erlang Rust
pairs.unzip lists:unzip(Pairs). pairs.iter().cloned().unzip()

7.3 ZipWithIndex (add indices)

Scala Erlang Rust
xs.zipWithIndex Manual with recursion xs.iter().enumerate()

8. GROUPING & PARTITIONING

8.1 GroupBy

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])


8.2 Partition (split by predicate)

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)


8.3 Distinct (remove duplicates)

Scala Erlang Rust
xs.distinct Use sets: sets:to_list(sets:from_list(Xs)) xs.iter().collect::<HashSet<_>>() then convert back

9. TREE FOLDING / STRUCTURAL RECURSION

Tree Definition

Scala:

sealed trait Tree
case class Leaf(v: Int) extends Tree
case class Node(l: Tree, r: Tree) extends Tree

Erlang:

{leaf, V}.
{node, Left, Right}.

Rust:

enum Tree {
    Leaf(i32),
    Node(Box<Tree>, Box<Tree>)
}

9.1 Generic Tree Fold

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)

9.2 Common Tree Operations

Height:

def height(t: Tree): Int = foldTree(t)(_ => 1)(_ max _ + 1)

Count Leaves:

def countLeaves(t: Tree): Int = foldTree(t)(_ => 1)(_ + _)

10. STREAM PROCESSING (INFINITE OR LAZY)

Scala — LazyList

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 }

Erlang — Generator Functions

% Infinite stream generator
gen(N) -> fun() -> {N, gen(N+1)} end.

% Usage
Stream = gen(1),
{Val1, Stream2} = Stream(),
{Val2, Stream3} = Stream2().

Rust — Infinite Iterators

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);

11. CONDITIONAL BRANCHING & PATTERN MATCHING

Scala

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"
}

Erlang

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}.

Rust

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),
}

12. ERROR HANDLING

Scala — Option / Either / Try

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 + c

Try:

Try(riskyOperation()) match {
  case Success(value) => value
  case Failure(exception) => handleError(exception)
}

Erlang — Tagged Tuples

{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.

Rust — Result / Option

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

13. CONCURRENCY MODELS

✅ Erlang — BEST for Concurrency

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.

Scala — Akka & Effect Systems

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 + b

Rust — Systems-Level Concurrency

Threads:

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();

14. PARALLEL PROCESSING

Scala — Parallel Collections

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 = customTaskSupport

Rust — Rayon

use 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();

Erlang — Spawn Processes

% 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]).

15. LAZINESS MODELS

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

Comparison

Scala:

def lazyComputation: LazyList[Int] = {
  println("Computing...")
  LazyList(1, 2, 3)
}
// "Computing..." only printed when evaluated

Rust:

let lazy_iter = (1..100)
    .map(|x| {
        println!("Processing {}", x);
        x * 2
    });
// Nothing printed until .collect() or iteration

Erlang:

lazy_seq(N) -> 
    fun() -> 
        [N | lazy_seq(N+1)] 
    end.

16. PERFORMANCE COMPARISON

🏆 Rust — Fastest

  • 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


⚡ Scala — Well-Optimized

  • 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


🔄 Erlang — Concurrency Champion

  • 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


17. WHEN TO CHOOSE WHICH LANGUAGE?

✅ Choose Scala When:

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


✅ Choose Erlang When:

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


✅ Choose Rust When:

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)


18. QUICK REFERENCE: FP CONCEPTS

Higher-Order Functions

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

Function Composition

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

Currying

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

Immutability

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

19. ADDITIONAL RESOURCES

Learning Resources

Scala:

Erlang:

Rust:


Community & Ecosystem

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)

Summary Table

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!

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