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…