Skip to content
Back to skills

Convert Erlang Fsharp

ASecurity

Converts Erlang code to idiomatic F# while preserving functional programming patterns, actor model semantics, and fault-tolerance mechanisms. Use when converting Erlang applications, libraries, OTP behaviors, or concurrent systems to F#.

  • 8 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 8, 2026
developmentexpresstestingperformance

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add aRustyDev/ai --skill convert-erlang-fsharp --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Convert Erlang Fsharp?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Convert Erlang Fsharp
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/arustydev-convert-erlang-fsharp/badge)](https://www.skillsdirectory.com/skills/arustydev-convert-erlang-fsharp)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: convert-erlang-fsharp
description: Converts Erlang code to idiomatic F# while preserving functional programming patterns, actor model semantics, and fault-tolerance mechanisms. Use when converting Erlang applications, libraries, OTP behaviors, or concurrent systems to F#.
---

# Erlang to F# Conversion

## Overview

This skill guides the conversion of Erlang code to idiomatic F# while maintaining functional programming principles, concurrent programming patterns, and fault-tolerance capabilities. F# provides strong functional programming support with .NET integration.

## Key Language Differences

### Type Systems
- **Erlang**: Dynamic typing with pattern matching
- **F#**: Static typing with type inference, algebraic data types

### Concurrency Models
- **Erlang**: Actor model with lightweight processes, message passing
- **F#**: MailboxProcessor (similar to actors), async workflows, Task Parallel Library

### Runtime Environment
- **Erlang**: BEAM VM with hot code swapping, distributed computing
- **F#**: .NET CLR with async/await, comprehensive standard library

---

## Quick Reference

| Erlang | F# | Notes |
|--------|-----|-------|
| `atom()` | `string` or discriminated union | Atoms become strings or DU cases |
| `integer()` | `int` or `int64` | Fixed-size integers |
| `float()` | `float` | 64-bit double |
| `binary()` | `byte[]` | Byte arrays |
| `list()` | `'a list` | Immutable lists |
| `tuple()` | `'a * 'b` | Product types |
| `map()` | `Map<'k,'v>` | Immutable maps |
| `pid()` | `MailboxProcessor<'T>` | Actor-style processing |
| `fun()` | `'a -> 'b` | First-class functions |

---

## Core Conversion Patterns

### 1. Module and Function Definitions

**Erlang:**
```erlang
-module(calculator).
-export([add/2, multiply/2]).

add(X, Y) -> X + Y.
multiply(X, Y) -> X * Y.
```

**F#:**
```fsharp
module Calculator =
    let add x y = x + y
    let multiply x y = x * y
```

### 2. Pattern Matching

**Erlang:**
```erlang
factorial(0) -> 1;
factorial(N) when N > 0 -> N * factorial(N - 1).
```

**F#:**
```fsharp
let rec factorial n =
    match n with
    | 0 -> 1
    | n when n > 0 -> n * factorial (n - 1)
    | _ -> failwith "Negative input not allowed"
```

### 3. Records and Types

**Erlang:**
```erlang
-record(person, {name, age, email}).

create_person(Name, Age, Email) ->
    #person{name=Name, age=Age, email=Email}.
```

**F#:**
```fsharp
type Person = {
    Name: string
    Age: int
    Email: string
}

let createPerson name age email =
    { Name = name; Age = age; Email = email }
```

### 4. Lists and Comprehensions

**Erlang:**
```erlang
double_list(List) -> [X * 2 || X <- List].
filter_even(List) -> [X || X <- List, X rem 2 =:= 0].
```

**F#:**
```fsharp
let doubleList list =
    list |> List.map ((*) 2)

let filterEven list =
    list |> List.filter (fun x -> x % 2 = 0)
```

### 5. Actor Model / Process Communication

**Erlang:**
```erlang
-module(counter).

start() ->
    spawn(fun() -> loop(0) end).

increment(Pid) ->
    Pid ! {increment, self()},
    receive
        {ok, NewValue} -> NewValue
    end.

loop(Count) ->
    receive
        {increment, From} ->
            NewCount = Count + 1,
            From ! {ok, NewCount},
            loop(NewCount)
    end.
```

**F#:**
```fsharp
module Counter =
    type Message =
        | Increment of AsyncReplyChannel<int>

    type CounterAgent() =
        let agent = MailboxProcessor.Start(fun inbox ->
            let rec loop count = async {
                let! msg = inbox.Receive()
                match msg with
                | Increment(reply) ->
                    let newCount = count + 1
                    reply.Reply(newCount)
                    return! loop newCount
            }
            loop 0
        )

        member _.Increment() = agent.PostAndReply(Increment)

    let start() = CounterAgent()
```

### 6. gen_server Pattern

**Erlang:**
```erlang
-module(my_server).
-behaviour(gen_server).

handle_call(get_count, _From, State = #{count := Count}) ->
    {reply, Count, State};
handle_call({increment, N}, _From, State = #{count := Count}) ->
    NewState = State#{count := Count + N},
    {reply, ok, NewState}.
```

**F#:**
```fsharp
module MyServer =
    type Message =
        | GetCount of AsyncReplyChannel<int>
        | Increment of int * AsyncReplyChannel<unit>

    type State = { Count: int }

    type ServerAgent() =
        let agent = MailboxProcessor.Start(fun inbox ->
            let rec loop state = async {
                let! msg = inbox.Receive()
                match msg with
                | GetCount(reply) ->
                    reply.Reply(state.Count)
                    return! loop state
                | Increment(n, reply) ->
                    let newState = { Count = state.Count + n }
                    reply.Reply()
                    return! loop newState
            }
            loop { Count = 0 }
        )

        member _.GetCount() = agent.PostAndReply(GetCount)
        member _.Increment(n) = agent.PostAndReply(fun ch -> Increment(n, ch))
```

### 7. Error Handling

**Erlang:**
```erlang
safe_divide(_, 0) -> {error, division_by_zero};
safe_divide(X, Y) -> {ok, X / Y}.
```

**F#:**
```fsharp
type DivisionError = DivisionByZero

let safeDivide x y =
    match y with
    | 0 -> Error DivisionByZero
    | _ -> Ok (x / y)

// Or using Option
let safeDivide' x y =
    match y with
    | 0 -> None
    | _ -> Some (x / y)
```

### 8. Binary Pattern Matching

**Erlang:**
```erlang
parse_header(<<Type:8, Length:16, Rest/binary>>) ->
    {Type, Length, Rest}.
```

**F#:**
```fsharp
let parseHeader (bytes: byte[]) =
    if bytes.Length < 3 then
        None
    else
        let type' = bytes.[0]
        let length = (uint16 bytes.[1] <<< 8) ||| uint16 bytes.[2]
        let rest = bytes.[3..]
        Some (type', length, rest)
```

### 9. ETS Tables to .NET Collections

**Erlang:**
```erlang
store(Key, Value) ->
    ets:insert(my_table, {Key, Value}).

lookup(Key) ->
    case ets:lookup(my_table, Key) of
        [{Key, Value}] -> {ok, Value};
        [] -> {error, not_found}
    end.
```

**F#:**
```fsharp
module MyTable =
    open System.Collections.Concurrent

    let private table = ConcurrentDictionary<string, obj>()

    let store key value =
        table.[key] <- value

    let lookup key =
        match table.TryGetValue(key) with
        | true, value -> Some value
        | false, _ -> None
```

---

## Common Libraries and Equivalents

| Erlang | F# / .NET Equivalent |
|--------|---------------------|
| gen_server | MailboxProcessor, Akka.NET actors |
| supervisor | Custom supervision, Akka.NET |
| ETS | System.Collections.Concurrent |
| httpc | System.Net.Http, FSharp.Data.Http |
| jsx (JSON) | System.Text.Json, FSharp.Json |
| cowboy (HTTP) | ASP.NET Core, Giraffe, Suave |
| lager (logging) | Serilog, NLog |

---

## Best Practices

### 1. Embrace Static Typing
- Use F#'s type system to catch errors at compile time
- Define explicit types for domain models
- Use discriminated unions for state machines

### 2. Preserve Functional Patterns
- Keep functions pure where possible
- Use immutable data structures
- Leverage F# pipeline operators (|>)

### 3. Adapt Concurrency Models
- Use MailboxProcessor for actor-like behavior
- Consider Akka.NET for complex distributed systems
- Use async workflows for I/O-bound operations

### 4. Error Handling
- Prefer Option and Result types over exceptions
- Use computation expressions for error propagation
- Reserve exceptions for truly exceptional cases

---

## Migration Strategy

### Step 1: Analyze Erlang Codebase
- Identify module structure and dependencies
- Map OTP behaviors (gen_server, supervisor)
- Document message-passing patterns

### Step 2: Design F# Architecture
- Plan module organization
- Design type hierarchy for records and unions
- Choose concurrency approach

### Step 3: Convert Core Logic
- Start with pure functions and data structures
- Convert pattern matching and recursion
- Translate list operations

### Step 4: Implement Concurrency
- Replace spawn/receive with MailboxProcessor
- Convert gen_server to agent-based patterns
- Add async workflows for I/O operations

### Step 5: Testing and Validation
- Port EUnit tests to FsUnit or xUnit
- Test concurrent behaviors
- Performance testing and optimization

---

## See Also

- `lang-erlang-dev` - Erlang development patterns
- `lang-fsharp-dev` - F# development patterns
- `meta-convert-dev` - General conversion methodology
- `convert-fsharp-erlang` - Reverse conversion

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.