# Functional Core, Imperative Shell — Functional Programming Concepts

Source: https://www.geekswithgeeks.com/en/functional-programming/ab-shell

> Effects at the edges.

## An architecture, not just a technique

The **functional core, imperative shell** pattern (popularised by Gary Bernhardt) puts decisions in a core of pure functions over immutable data and keeps all I/O in a thin shell. The shell gathers inputs (HTTP request, database rows, current time), calls the core, and executes the outputs (writes, emails, responses). The core can be unit-tested exhaustively and quickly; the shell has little logic and is covered by a few integration tests. Similar ideas appear as hexagonal architecture (ports and adapters), the Elm architecture and Redux (pure reducers, effects in middleware).

## Core returns decisions, shell executes them

Effects are described as data.

```typescript
// ---- functional core (pure) ----
type Account = { id: string; balance: number; overdraftLimit: number };
type Command =
  | { kind: 'debit'; accountId: string; amount: number }
  | { kind: 'notify'; accountId: string; message: string };

function withdraw(acc: Account, amount: number, now: Date): Command[] {
  if (amount <= 0) return [];
  if (acc.balance - amount < -acc.overdraftLimit) {
    return [{ kind: 'notify', accountId: acc.id, message: `Declined at ${now.toISOString()}` }];
  }
  return [{ kind: 'debit', accountId: acc.id, amount }];
}

// ---- imperative shell (effects) ----
async function handleWithdraw(repo: AccountRepo, notifier: Notifier, id: string, amount: number) {
  const acc = await repo.load(id);
  for (const cmd of withdraw(acc, amount, new Date())) {
    if (cmd.kind === 'debit') await repo.debit(cmd.accountId, cmd.amount);
    else await notifier.send(cmd.accountId, cmd.message);
  }
}
interface AccountRepo { load(id: string): Promise<Account>; debit(id: string, amt: number): Promise<void> }
interface Notifier { send(id: string, msg: string): Promise<void> }
```

## A judge and a bailiff

The judge (core) only decides based on the evidence presented; the bailiff (shell) gathers people and carries out the ruling. You can review a ruling without re-running the arrest.

**Quiz:** In a functional core / imperative shell design, where does the database call belong?

- [ ] Inside every core function
- [x] In the shell, which passes loaded data into the pure core
- [ ] In the type definitions
- [ ] Nowhere; databases are not allowed in FP

*Answer:* In the shell, which passes loaded data into the pure core. The core decides; the shell performs.
