Skip to content
convert-erlang-elm logo

Convert Erlang to Elm

convert-erlang-elm

Convert Erlang code to idiomatic Elm. Use when migrating Erlang backend logic to Elm frontend applications, translating BEAM VM patterns to functional frontend code, or refactoring distributed systems to type-safe UIs. Extends meta-convert-dev with Erlang-to-Elm specific patterns.

SKILL.md

Full skill instructions

Convert Erlang to Elm

Convert Erlang code to idiomatic Elm. This skill extends meta-convert-dev with Erlang-to-Elm specific type mappings, idiom translations, and architectural patterns for moving from distributed backend systems to type-safe frontend applications.

This Skill Extends

  • meta-convert-dev - Foundational conversion patterns (APTV workflow, testing strategies)

For general concepts like the Analyze → Plan → Transform → Validate workflow, testing strategies, and common pitfalls, see the meta-skill first.

This Skill Adds

  • Type mappings: Erlang types → Elm types
  • Idiom translations: Erlang patterns → idiomatic Elm
  • Architecture patterns: OTP behaviors → The Elm Architecture (TEA)
  • Message passing: Process mailboxes → Elm commands/​subscriptions
  • Error handling: let-it-crash → Maybe/​Result types
  • Concurrency: Processes/​gen_server → Elm runtime effects

This Skill Does NOT Cover

  • General conversion methodology - see meta-convert-dev
  • Erlang language fundamentals - see lang-erlang-dev
  • Elm language fundamentals - see lang-elm-dev
  • Reverse conversion (Elm → Erlang) - see convert-elm-erlang
  • Backend-to-backend conversions - see other conversion skills

Quick Reference

ErlangElmNotes
atom()String or custom typeAtoms become string literals or union types
binary()StringUTF-8 encoded strings
integer()IntArbitrary precision → fixed size
float()FloatDirect mapping
boolean()Booltrue/false mapping
list()List aHomogeneous typed lists
tuple()Custom type or recordNamed fields preferred
map()Dict k vKey-value storage
pid()N/​ANo direct equivalent (use Cmd/​Sub)
undefinedNothing in Maybe aExplicit nullability
{ok, Value}Just Value or Ok ValueSuccess wrapper
{error, Reason}Err Reason in Result e aError wrapper

Architectural Paradigm Shift

From OTP to The Elm Architecture (TEA)

AspectErlang OTPElm TEA
PurposeDistributed, fault-tolerant backendType-safe, reactive frontend
ConcurrencyMillions of processesSingle-threaded event loop
StateProcess-local mutable stateImmutable application state
CommunicationMessage passing between processesCommands/​Subscriptions to runtime
Error handlingLet-it-crash + supervision treesCompiler-enforced exhaustive handling

Mapping OTP Behaviors to TEA Components

Erlang gen_server:

-module(counter_server).
-behaviour(gen_server).

-record(state, {count = 0}).

init([]) -> {ok, #state{}}.

handle_call(get, _From, State) ->
    {reply, State#state.count, State};
handle_call({increment, N}, _From, State) ->
    NewCount = State#state.count + N,
    {reply, NewCount, State#state{count = NewCount}}.

Elm equivalent using TEA:

module Counter exposing (Model, Msg, init, update, view)

-- MODEL
type alias Model =
    { count : Int }

init : Model
init =
    { count = 0 }

-- UPDATE
type Msg
    = Increment Int
    | Get

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        Increment n ->
            ( { model | count = model.count + n }, Cmd.none )

        Get ->
            ( model, Cmd.none )

-- VIEW
view : Model -> Html Msg
view model =
    div []
        [ text ("Count: " ++ String.fromInt model.count)
        , button [ onClick (Increment 1) ] [ text "Increment" ]
        ]

Type System Mapping

Primitive Types

ErlangElmNotes
true / falseTrue / FalseCapitalized in Elm
4242Integer literals
3.143.14Float literals
<<"binary">>"String"UTF-8 strings
'atom'"string" or custom typeContext-dependent

Collection Types

ErlangElmExample
[1, 2, 3][1, 2, 3]Homogeneous lists
#{key => value}Dict.fromList [("key", value)]Requires Dict module
{ok, 42}Ok 42Result type
{error, "failed"}Err "failed"Result type
undefinedNothingMaybe type

Structured Types

Erlang Records → Elm Type Aliases

%% Erlang
-record(user, {
    id :: integer(),
    name :: binary(),
    age :: integer() | undefined
}).
-- Elm
type alias User =
    { id : Int
    , name : String
    , age : Maybe Int
    }

Idiom Translation

1. Pattern Matching

Erlang:

classify(N) when N > 0 -> positive;
classify(N) when N < 0 -> negative;
classify(0) -> zero.

Elm:

classify : Int -> String
classify n =
    case compare n 0 of
        GT -> "positive"
        LT -> "negative"
        EQ -> "zero"

2. List Processing

Erlang:

Squares = [X * X || X <- [1, 2, 3, 4, 5], X rem 2 == 0].

Elm:

squares : List Int
squares =
    [1, 2, 3, 4, 5]
        |> List.filter (\x -> modBy 2 x == 0)
        |> List.map (\x -> x * x)

3. Error Handling

Erlang:

parse_int(Str) ->
    try binary_to_integer(Str) of
        Int -> {ok, Int}
    catch
        error:badarg -> {error, invalid_integer}
    end.

Elm:

parseInt : String -> Result String Int
parseInt str =
    String.toInt str
        |> Result.fromMaybe "Invalid integer"

4. Optional Values

Erlang:

get_timeout(#config{timeout = undefined}) -> 5000;
get_timeout(#config{timeout = T}) -> T.

Elm:

getTimeout : Config -> Int
getTimeout config =
    Maybe.withDefault 5000 config.timeout

5. HTTP Requests (Message Passing Replacement)

Erlang:

fetch_data(Url) ->
    Pid = self(),
    spawn(fun() ->
        Response = httpc:request(get, {Url, []}, [], []),
        Pid ! {http_response, Response}
    end).

Elm:

type Msg
    = GotData (Result Http.Error String)

fetchData : String -> Cmd Msg
fetchData url =
    Http.get
        { url = url
        , expect = Http.expectString GotData
        }

6. State Machine

Erlang:

locked(cast, {button, Code}, #{code := Code} = Data) ->
    {next_state, unlocked, Data};
locked(cast, {button, _}, Data) ->
    {keep_state, Data}.

Elm:

type DoorState
    = Locked
    | Unlocked

type Msg
    = ButtonPressed String

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case (msg, model.state) of
        (ButtonPressed code, Locked) ->
            if code == model.correctCode then
                ( { model | state = Unlocked }, Cmd.none )
            else
                ( model, Cmd.none )
        _ ->
            ( model, Cmd.none )

Error Handling Philosophy

Philosophy Shift

ErlangElm
Let-it-crash: Supervisors restart failed processesPrevent-all-crashes: Compiler enforces handling all cases
Runtime errors are acceptableCompile-time guarantees eliminate runtime errors

Practical Translation

Erlang:

safe_divide(_, 0) -> {error, division_by_zero};
safe_divide(X, Y) -> {ok, X / Y}.

Elm:

type DivisionError = DivisionByZero

safeDivide : Float -> Float -> Result DivisionError Float
safeDivide a b =
    if b == 0 then
        Err DivisionByZero
    else
        Ok (a / b)

Migration Strategy

What CAN be converted:

  • Business logic (calculations, validations)
  • Data transformations
  • State machines
  • Request/​response patterns

What CANNOT be converted:

  • Process supervision (no equivalent)
  • Distributed systems (Elm is frontend-only)
  • Hot code reloading
  • Low-level concurrency

Architecture Mapping

Erlang OTP Application
│
├── Supervision Tree ──────────> [Remains in Erlang backend]
├── gen_server (State) ────────> Elm Model + Update
├── handle_call/​cast ──────────> Msg variants + update cases
├── State transitions ─────────> Model updates
└── API endpoints ─────────────> Elm HTTP commands

Result: Hybrid architecture
- Backend: Erlang OTP (supervision, distributed state)
- Frontend: Elm (UI, client state, type-safe interactions)
- Communication: HTTP/​WebSocket APIs

Common Pitfalls

1. Trying to Port Process Concurrency

Problem: Erlang's concurrency model doesn't translate to Elm. Solution: Re-architect around TEA with commands/​subscriptions.

2. Expecting Mutable State

Problem: Erlang processes have mutable state; Elm is purely functional. Solution: Embrace immutability. Return new model versions from update.

3. Over-relying on Dynamic Types

Problem: Erlang's dynamic typing has no direct Elm equivalent. Solution: Use custom types (union types) to model all possibilities.

4. Ignoring JSON Boundaries

Problem: Assuming Erlang terms can be directly used in Elm. Solution: Always create explicit JSON encoders/​decoders for API contracts.


Tooling Translation

ErlangElmPurpose
rebar3 compileelm makeBuild project
rebar3 eunitelm-testRun tests
rebar3 shellelm replInteractive shell
dialyzerelm compilerType checking
observerElm debuggerRuntime inspection

Example: Counter with Backend

Elm Frontend:

module Counter exposing (main)

import Browser
import Html exposing (..)
import Html.Events exposing (onClick)
import Http

type alias Model =
    { count : Int, loading : Bool }

type Msg
    = Increment Int
    | GotCount (Result Http.Error Int)

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        Increment n ->
            ( { model | loading = True }
            , incrementCount n
            )

        GotCount (Ok count) ->
            ( { model | count = count, loading = False }
            , Cmd.none
            )

        GotCount (Err _) ->
            ( { model | loading = False }
            , Cmd.none
            )

incrementCount : Int -> Cmd Msg
incrementCount n =
    Http.post
        { url = "/​api/​counter"
        , body = Http.jsonBody (Encode.object [("increment", Encode.int n)])
        , expect = Http.expectJson GotCount countDecoder
        }

See Also

  • lang-erlang-dev - Erlang language fundamentals
  • lang-elm-dev - Elm language fundamentals
  • meta-convert-dev - General conversion methodology
  • convert-elm-erlang - Reverse conversion

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View