xiaohongshu
xiaohongshu
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.
Full skill instructions
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.
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.
meta-convert-devlang-erlang-devlang-elm-devconvert-elm-erlang| Erlang | Elm | Notes |
|---|---|---|
atom() | String or custom type | Atoms become string literals or union types |
binary() | String | UTF-8 encoded strings |
integer() | Int | Arbitrary precision → fixed size |
float() | Float | Direct mapping |
boolean() | Bool | true/false mapping |
list() | List a | Homogeneous typed lists |
tuple() | Custom type or record | Named fields preferred |
map() | Dict k v | Key-value storage |
pid() | N/A | No direct equivalent (use Cmd/Sub) |
undefined | Nothing in Maybe a | Explicit nullability |
{ok, Value} | Just Value or Ok Value | Success wrapper |
{error, Reason} | Err Reason in Result e a | Error wrapper |
| Aspect | Erlang OTP | Elm TEA |
|---|---|---|
| Purpose | Distributed, fault-tolerant backend | Type-safe, reactive frontend |
| Concurrency | Millions of processes | Single-threaded event loop |
| State | Process-local mutable state | Immutable application state |
| Communication | Message passing between processes | Commands/Subscriptions to runtime |
| Error handling | Let-it-crash + supervision trees | Compiler-enforced exhaustive handling |
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" ]
]
| Erlang | Elm | Notes |
|---|---|---|
true / false | True / False | Capitalized in Elm |
42 | 42 | Integer literals |
3.14 | 3.14 | Float literals |
<<"binary">> | "String" | UTF-8 strings |
'atom' | "string" or custom type | Context-dependent |
| Erlang | Elm | Example |
|---|---|---|
[1, 2, 3] | [1, 2, 3] | Homogeneous lists |
#{key => value} | Dict.fromList [("key", value)] | Requires Dict module |
{ok, 42} | Ok 42 | Result type |
{error, "failed"} | Err "failed" | Result type |
undefined | Nothing | Maybe type |
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
}
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"
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)
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"
Erlang:
get_timeout(#config{timeout = undefined}) -> 5000;
get_timeout(#config{timeout = T}) -> T.
Elm:
getTimeout : Config -> Int
getTimeout config =
Maybe.withDefault 5000 config.timeout
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
}
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 )
| Erlang | Elm |
|---|---|
| Let-it-crash: Supervisors restart failed processes | Prevent-all-crashes: Compiler enforces handling all cases |
| Runtime errors are acceptable | Compile-time guarantees eliminate runtime errors |
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)
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
Problem: Erlang's concurrency model doesn't translate to Elm. Solution: Re-architect around TEA with commands/subscriptions.
Problem: Erlang processes have mutable state; Elm is purely functional. Solution: Embrace immutability. Return new model versions from update.
Problem: Erlang's dynamic typing has no direct Elm equivalent. Solution: Use custom types (union types) to model all possibilities.
Problem: Assuming Erlang terms can be directly used in Elm. Solution: Always create explicit JSON encoders/decoders for API contracts.
| Erlang | Elm | Purpose |
|---|---|---|
rebar3 compile | elm make | Build project |
rebar3 eunit | elm-test | Run tests |
rebar3 shell | elm repl | Interactive shell |
dialyzer | elm compiler | Type checking |
observer | Elm debugger | Runtime inspection |
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
}
lang-erlang-dev - Erlang language fundamentalslang-elm-dev - Elm language fundamentalsmeta-convert-dev - General conversion methodologyconvert-elm-erlang - Reverse conversionxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
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...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
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...
Display project status, active tracks, and next actions
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.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience