Functional programmer focused on type-driven design, lawful abstractions, and effectful systems.
My work sits at the intersection of Scala, category theory, and systems programming with Rust. I design software where types carry meaning, effects are explicit, and composition is the primary tool.
- If an invariant is not encoded in the type system, it is merely a convention.
I write on my blog about functional programming, Scala, and software architecture, focusing on practical FP rather than academic exercises.
Available for freelance work and technical consulting. See my website, CV or my LinkedIn. If your system is growing faster than your confidence in it, we should talk.
- Typeclasses over inheritance
- Algebras over implementations
- Effects are values, not side effects
- Explicit dependencies via parametric polymorphism
- Laws first, optimizations second (but never ignored)
- Make illegal states unrepresentable
I don’t see Functional Programming as a style or a set of tools, but as a discipline for reasoning about software.
My goal is not to write “clever” code, but to build systems that remain understandable, correct, and evolvable under pressure.
Types are executable constraints. If an invariant matters, it belongs in the type system. If a function accepts more states than it can handle, it is lying.
Effects are not something that happens — they are something that is described. An effectful program is a value. Interpretation is a separate concern. Hidden effects are technical debt.
An abstraction without laws is just an interface. Laws are what allow refactoring without fear and composition without surprise. If an abstraction cannot be reasoned about algebraically, it is probably the wrong abstraction.
Large systems are not built by adding features, but by composing smaller, well-behaved parts.
Composition works when:
- Dependencies are explicit
- Effects are controlled
- Data flows are unidirectional
Inheritance does not scale. Composition does.
Every runtime check is a failure to model the domain correctly. The earlier an invalid state is rejected, the cheaper it is. The best error is the one that cannot be expressed.
Abstractions are allowed. Accidental complexity is not. I care about performance, but never at the cost of correctness or clarity. Good FP and high performance are not opposites — they are often aligned.
Fewer concepts do not automatically mean simpler systems. True simplicity comes from clear boundaries, precise models, and predictable behavior. A small but leaky abstraction is worse than a larger but honest one.
The following Scala snippet shows how these principles translate into a real design: algebras expressed as typeclasses, effect-polymorphic programs, and interpreter-free composition.
- No inheritance trees. No hidden effects. Only algebras, interpreters, and laws.
// Domain
final case class Timeline[A](at: Date, value: A)
// Algebra
trait PersonAlg[F[_]] {
def about: F[Info]
def experience: F[List[Timeline[Experience]]]
def education: F[List[Timeline[Education]]]
def skills: F[Stack]
}
// Program
trait CurriculumVitae[F[_]] {
def about: F[Info]
def experience(at: Date): F[List[Experience]]
def education(at: Date): F[List[Education]]
def skills: F[Stack]
}
// Interpreter-free construction
object CurriculumVitae {
def apply[F[_]: MonadFilter](P: PersonAlg[F]): CurriculumVitae[F] =
new CurriculumVitae[F] {
def about: F[Info] =
P.about
def experience(at: Date): F[List[Experience]] =
P.experience.map(_.mapFilter(collectUntil(at)))
def education(at: Date): F[List[Education]] =
P.education.map(_.mapFilter(collectUntil(at)))
def skills: F[Stack] =
P.skills
}
def collectUntil[A](at: Date): Timeline[A] => Option[A] = {
case Timeline(date, value) if date <= at => Some(value)
case _ => None
}
}This Rust example mirrors the same architectural ideas: traits as algebras, explicit effects via Result, and correctness enforced by the type system.
- Ownership enforces correctness. Traits define behavior. Abstractions compile away.
Show Scala example
use std::collections::HashMap;
// Domain
#[derive(Clone)]
pub struct Timeline<A> {
pub at: Date,
pub value: A,
}
// Algebra
pub trait PersonAlg {
type Error;
fn about(&self) -> Result<Info, Self::Error>;
fn experience(&self) -> Result<Vec<Timeline<Experience>>, Self::Error>;
fn education(&self) -> Result<Vec<Timeline<Education>>, Self::Error>;
fn skills(&self) -> Result<Stack, Self::Error>;
}
// Program
pub struct CurriculumVitae<P> {
person: P,
}
impl<P> CurriculumVitae<P>
where
P: PersonAlg,
{
pub fn new(person: P) -> Self {
Self { person }
}
fn collect_until<A>(at: Date, xs: Vec<Timeline<A>>) -> Vec<A> {
xs.into_iter()
.filter(|t| t.at <= at)
.map(|t| t.value)
.collect()
}
pub fn experience(&self, at: Date) -> Result<Vec<Experience>, P::Error> {
self.person.experience().map(|xs| Self::collect_until(at, xs))
}
pub fn education(&self, at: Date) -> Result<Vec<Education>, P::Error> {
self.person.education().map(|xs| Self::collect_until(at, xs))
}
}







