Claude Code Plugins

Community-maintained marketplace

Feedback

convert-erlang-fsharp

@aRustyDev/ai
0
0

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#.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

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:

-module(calculator).
-export([add/2, multiply/2]).

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

F#:

module Calculator =
    let add x y = x + y
    let multiply x y = x * y

2. Pattern Matching

Erlang:

factorial(0) -> 1;
factorial(N) when N > 0 -> N * factorial(N - 1).

F#:

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:

-record(person, {name, age, email}).

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

F#:

type Person = {
    Name: string
    Age: int
    Email: string
}

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

4. Lists and Comprehensions

Erlang:

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

F#:

let doubleList list =
    list |> List.map ((*) 2)

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

5. Actor Model / Process Communication

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#:

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:

-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#:

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:

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

F#:

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:

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

F#:

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:

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#:

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