Skip to content
Back to skills

Meta Convert Dev

ASecurity

Create language conversion skills for translating code from language A to language B. Use when building 'convert-X-Y' skills, designing type mappings between languages, establishing idiom translation patterns, or defining conversion methodologies. Provides foundational patterns that specific conversion skills extend.

  • 8 stars
  • 0 votes
  • 2 copies
  • 4 views
  • Added June 2, 2026
developmentjavascripttypescriptpythonrustgojavanodeexpresstestingdebugging

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 11 files and shows the line behind each finding

Scanned June 2, 2026

npx -y skills add aRustyDev/agents --skill meta-convert-dev --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Meta Convert Dev?

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

Security grade badge for Meta Convert Dev
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/arustydev-meta-convert-dev/badge)](https://www.skillsdirectory.com/skills/arustydev-meta-convert-dev)

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: meta-convert-dev
description: Create language conversion skills for translating code from language A to language B. Use when building 'convert-X-Y' skills, designing type mappings between languages, establishing idiom translation patterns, or defining conversion methodologies. Provides foundational patterns that specific conversion skills extend.
---

# Language Conversion Skill Development

Foundational patterns for creating one-way language conversion skills. This meta-skill guides the creation of `convert-X-Y` skills (e.g., `convert-typescript-rust`, `convert-python-golang`) that assist in translating/refactoring code from a source language to a target language.

## When to Use This Skill

- Creating a new `convert-X-Y` skill for language translation
- Designing type system mappings between languages
- Establishing idiom translation strategies
- Defining conversion workflows and validation approaches
- Building tooling recommendations for transpilation

## This Skill Does NOT Cover

- Actual code conversion (use specific `convert-X-Y` skills or `meta-convert-guide`)
- Language tutorials (see `lang-*-dev` skills)
- Bidirectional translation (each direction is a separate skill)
- Runtime interop/FFI (see language-specific interop skills)

## Related Skills

- `meta-convert-guide` - Patterns and strategies for performing conversions
- `convert-*` skills - Language-pair specific conversion skills

---

## Existing Conversion Skills

For concrete, language-pair-specific examples, see these skills:

| Skill | Description |
|-------|-------------|
| `convert-typescript-rust` | TypeScript → Rust (GC → ownership, exceptions → Result) |
| `convert-typescript-python` | TypeScript → Python (static → dynamic typing) |
| `convert-typescript-golang` | TypeScript → Go (OOP → simplicity, Promise → goroutine) |
| `convert-golang-rust` | Go → Rust (GC → ownership, interface → trait) |
| `convert-python-rust` | Python → Rust (dynamic → static, GC → ownership) |
| `convert-python-fsharp` | Python → F# (dynamic → static, OOP → functional) |
| `convert-python-erlang` | Python → Erlang (single-threaded → BEAM actors) |
| `convert-python-clojure` | Python → Clojure (imperative → functional, OOP → data-oriented) |
| `convert-clojure-roc` | Clojure → Roc (JVM → native, dynamic → static) |
| `convert-clojure-elixir` | Clojure → Elixir (JVM → BEAM, STM → actors) |
| `convert-clojure-haskell` | Clojure → Haskell (dynamic → static, practical → pure) |

**Note:** This list may not be complete. Search `content/skills/convert-*` for all available conversion skills.

### Skill Categories

| Category | Examples | Key Challenges |
|----------|----------|----------------|
| **Static → Static** | TypeScript→Go, Rust→Go | Type system mapping, idiom differences |
| **Dynamic → Static** | Python→Rust, Clojure→Haskell | Add types, handle runtime flexibility |
| **Static → Dynamic** | TypeScript→Python, Go→Elixir | Remove type annotations, embrace flexibility |
| **Dynamic → Dynamic** | Python→Clojure, Clojure→Elixir | Paradigm and runtime differences |
| **OOP → Functional** | Java→Clojure, Python→Haskell | Replace classes with data+functions |
| **Functional → Functional** | Clojure→Elixir, Haskell→Scala | FP dialect differences |
| **GC → Ownership** | Any→Rust | Add explicit lifetimes and borrowing |
| **Platform Migration** | JVM→BEAM, JVM→Native | Runtime semantics, library ecosystem |

When creating a new conversion skill, refer to the specific `convert-X-Y` skills for production-ready examples.

### Platform Ecosystem Considerations

When converting between languages on different platforms (JVM, BEAM, .NET, Native), consider:

| Platform | Key Characteristics |
|----------|---------------------|
| **JVM** | Bytecode, JIT, GC, thread-based concurrency, rich stdlib |
| **BEAM** | Lightweight processes, preemptive scheduling, fault tolerance, hot reload |
| **.NET** | Similar to JVM, async/await primitives, strong Windows integration |
| **Native** | Direct compilation, manual/ownership memory, no runtime overhead |
| **Scripting** | Interpreted/JIT, dynamic typing, GIL (Python) |

**See:** [Platform Ecosystem Reference](references/platform-ecosystem.md) for detailed runtime characteristics, stdlib mapping strategies, transpiler options, and FFI considerations.

### Paradigm Translation Patterns

Cross-paradigm conversions require mental model shifts:

| Source → Target | Key Transformation |
|-----------------|-------------------|
| **OOP → FP** | Classes → data + functions, mutation → immutable updates |
| **Imperative → Declarative** | Loops → higher-order functions, statements → expressions |
| **Dynamic → Static** | Add type annotations, handle runtime flexibility statically |
| **Mutable-first → Immutable-first** | State threading, persistent data structures |

**See:** [Paradigm Translation Reference](references/paradigm-translation.md) for detailed patterns and examples.

### Additional Reference Materials

| Topic | Reference File |
|-------|---------------|
| **Numeric edge cases** | [references/numeric-edge-cases.md](references/numeric-edge-cases.md) - Overflow handling, division semantics, float precision |
| **Stdlib mapping** | [references/stdlib-mapping.md](references/stdlib-mapping.md) - Finding equivalent functions across languages |
| **Build systems** | [references/build-system-mapping.md](references/build-system-mapping.md) - Package managers and project structure |
| **Module systems** | [references/module-system-comparison.md](references/module-system-comparison.md) - Import/export patterns |

---

## Conversion Skill Naming Convention

```text
convert-<source>-<target>
```

| Component | Description | Example |
|-----------|-------------|---------|
| `convert` | Fixed prefix | `convert-` |
| `<source>` | Source language (lowercase) | `typescript`, `python`, `golang` |
| `<target>` | Target language (lowercase) | `rust`, `python`, `golang` |

**Examples:**

- `convert-typescript-rust` - TypeScript → Rust
- `convert-python-golang` - Python → Go
- `convert-golang-rust` - Go → Rust

**Note:** Each skill is ONE-WAY. `convert-A-B` and `convert-B-A` are separate skills with different patterns.

---

## Conversion Skill Structure

Every `convert-X-Y` skill should follow this structure:

```markdown
# Convert <Source> to <Target>

## Overview

Brief description of the conversion, common use cases, and what to expect.

## When to Use

- Scenarios where this conversion makes sense
- Benefits of the target language for this use case

## When NOT to Use

- Scenarios where conversion is not recommended
- Better alternatives

## Type System Mapping

Complete mapping table from source types to target types.

## Idiom Translation

Source patterns and their idiomatic target equivalents.

## Error Handling

How to convert error handling patterns.

## Concurrency Patterns

How async/threading models translate.

## Memory & Ownership

If applicable, how memory models differ and translate.

## Testing Strategy

How to verify functional equivalence.

## Tooling

Available tools, transpilers, and validation helpers.

## Common Pitfalls

Mistakes to avoid during conversion.

## Examples

Concrete before/after conversion examples.
```

---

## The 8 Pillars Framework

Every conversion skill should address these 8 pillars for comprehensive coverage:

| Pillar | What to Document |
|--------|------------------|
| **1. Module System** | Import/export, visibility, package structure |
| **2. Error Handling** | Exception/Result/Option patterns, error propagation |
| **3. Concurrency** | Async/await, threads, processes, channels |
| **4. Metaprogramming** | Macros, decorators, reflection, code generation |
| **5. Zero/Default Values** | Null handling, default initialization, optional types |
| **6. Serialization** | JSON, binary formats, validation patterns |
| **7. Build System** | Package managers, build tools, project structure |
| **8. Testing** | Test frameworks, property-based testing, verification |

### 9th Pillar: Dev Workflow (for REPL-centric languages)

For conversions involving REPL-centric languages (Clojure, Elixir, Erlang, Haskell, Lisp, F#), add:

| Pillar | What to Document |
|--------|------------------|
| **9. Dev Workflow & REPL** | REPL patterns, hot reload, interactive debugging |

See `meta-convert-guide` for detailed guidance on each pillar.

---

## Skill Creation Workflow

When creating a new `convert-X-Y` skill:

### 1. Assess the Conversion

- **Check for existing skill**: Search `content/skills/convert-*`
- **Check for reverse skill**: If creating `convert-A-B`, check if `convert-B-A` exists
- **Identify category**: Static→Dynamic, GC→Ownership, etc.
- **Estimate difficulty**: See [difficulty-matrix.md](../meta-convert-guide/reference/difficulty-matrix.md) for pre-calculated ratings

### 2. Gather Language Knowledge

- Read `lang-X-dev` skill for source language
- Read `lang-Y-dev` skill for target language
- Identify key differences in the 8 pillars

### 3. Create Type Mapping Tables

For each pillar, create comprehensive mapping tables:

```markdown
| Source (X) | Target (Y) | Notes |
|------------|------------|-------|
| `string`   | `String`   | ...   |
```

### 4. Document Idiom Translations

Show idiomatic translations, not literal ports:

```markdown
// Source: X
[source code]

// Target: Y (idiomatic)
[target code]

// Avoid: Y (transliterated)
[non-idiomatic code]
```

### 5. Include Testing Strategy

- Port existing tests first
- Add property-based tests for invariants
- Use golden testing for output comparison

### 6. Self-Review Checklist

Before finalizing the skill:

- [ ] All 8 pillars addressed (9 if REPL-centric)
- [ ] Type mappings are complete
- [ ] Examples are idiomatic, not transliterated
- [ ] Common pitfalls documented
- [ ] Testing strategy included
- [ ] Cross-references to related skills

---

## Concurrency Pattern Translation

When converting code between languages, concurrency models are often the most challenging aspect. Different languages embody fundamentally different concurrency philosophies, from shared-memory threads to isolated processes, from promises to channels, from Software Transactional Memory (STM) to actor models.

This section provides a comprehensive guide to translating concurrency patterns across languages.

### Concurrency Model Matrix

Understanding the mapping between source and target concurrency models is crucial for successful conversion:

| Source Model | Target Model | Translation Strategy | Key Considerations |
|--------------|--------------|---------------------|-------------------|
| **Actors (BEAM)** | **STM (Clojure)** | Use atoms/refs for state, core.async for message-passing | BEAM processes → atoms for simple state, refs+dosync for coordinated updates |
| **Actors (BEAM)** | **IO Monad (Haskell)** | Use TVar for shared state, async for spawning | GenServers → IORef or TVar, supervision → exception handling |
| **Actors (BEAM)** | **Goroutines (Go)** | Channels for message passing, structs for state | GenServer → goroutine with channel, supervisor → error handling |
| **STM (Clojure)** | **Actors (BEAM)** | Wrap refs in GenServer, use supervisor for coordination | dosync transactions → GenServer serializes updates |
| **STM (Clojure)** | **Goroutines (Go)** | Channels + select for coordination, mutexes for shared state | refs → channels or sync.Mutex, atoms → atomic package |
| **Channels (Go)** | **Actors (BEAM)** | GenStage/Flow for backpressure, GenServer for state | Buffered channels → GenServer queue, select → receive |
| **Channels (Go)** | **Promises (JS/TS)** | Promise wraps channel receive, async/await for coordination | Goroutine → async function, channel receive → await |
| **IO Monad (Haskell)** | **Tasks (Roc)** | Map IO actions to Task effects | IO-based concurrency → Task-based effects system |
| **Promises (JS/TS)** | **Goroutines (Go)** | Goroutines for async execution, channels for communication | async function → goroutine, Promise → channel or direct return |
| **Promises (JS/TS)** | **Futures (Rust)** | Tokio runtime for async, futures for lazy evaluation | Promise (eager) → Future (lazy), await → .await |
| **Threads (Python)** | **Actors (BEAM)** | Spawn processes for each thread, message passing for communication | GIL limitations → true parallelism, threading.Thread → spawn |
| **Asyncio (Python)** | **Async/Await (Rust)** | Tokio runtime, async functions, futures | Event loop → runtime, coroutines → async fn |

**Key Insight**: Most conversions require not just syntactic translation but a fundamental shift in how you think about concurrency. For example:

- **Shared memory (threads) → Message passing (actors)**: Stop thinking about locks; start thinking about process isolation
- **Eager execution (Promises) → Lazy evaluation (Futures)**: Understand when work actually starts
- **Preemptive scheduling (OS threads) → Cooperative scheduling (async/await)**: Be aware of blocking operations

### Pattern Translation Examples

#### Example 1: Clojure STM → Elixir Actors (Coordinated State Updates)

**Problem**: Clojure's STM allows multiple refs to be updated atomically in a transaction. Elixir's actor model has isolated processes.

**Clojure (Source):**

```clojure
(def account-a (ref 1000))
(def account-b (ref 500))

(defn transfer [from to amount]
  (dosync
    (alter from - amount)
    (alter to + amount)))

;; Atomic bank transfer
(transfer account-a account-b 100)
;; account-a: 900, account-b: 600
```

**Elixir (Target - Idiomatic Translation):**

```elixir
defmodule Bank do
  use GenServer

  # Client API
  def start_link(initial_accounts) do
    GenServer.start_link(__MODULE__, initial_accounts, name: __MODULE__)
  end

  def transfer(from_account, to_account, amount) do
    GenServer.call(__MODULE__, {:transfer, from_account, to_account, amount})
  end

  def get_balance(account) do
    GenServer.call(__MODULE__, {:get_balance, account})
  end

  # Server Callbacks
  @impl true
  def init(initial_accounts) do
    {:ok, initial_accounts}
  end

  @impl true
  def handle_call({:transfer, from, to, amount}, _from, accounts) do
    case accounts do
      %{^from => from_balance, ^to => to_balance} when from_balance >= amount ->
        updated_accounts = accounts
        |> Map.update!(from, &(&1 - amount))
        |> Map.update!(to, &(&1 + amount))
        {:reply, :ok, updated_accounts}

      _ ->
        {:reply, {:error, :insufficient_funds}, accounts}
    end
  end

  @impl true
  def handle_call({:get_balance, account}, _from, accounts) do
    {:reply, Map.get(accounts, account, 0), accounts}
  end
end

# Usage
{:ok, _pid} = Bank.start_link(%{account_a: 1000, account_b: 500})
:ok = Bank.transfer(:account_a, :account_b, 100)
# account_a: 900, account_b: 600
```

**Translation Strategy**:

1. **Centralize coordinated state**: All accounts in single GenServer (serializes transactions)
2. **Transaction semantics**: GenServer's synchronous call provides atomicity
3. **Pattern matching**: Use guards to validate sufficient funds before update
4. **Immutable updates**: Map.update! returns new map, pattern matches for validation

**Edge Cases & Gotchas**:

- **Deadlock prevention**: Single GenServer eliminates distributed deadlocks
- **Scalability tradeoff**: STM allows concurrent reads; GenServer serializes all operations
- **Alternative**: For high throughput, shard accounts across multiple GenServers with distributed coordination

**Performance Implications**:

- Clojure STM: Optimistic concurrency (retry on conflict), parallel reads
- Elixir GenServer: Pessimistic serialization, all operations sequential
- For read-heavy workloads, consider Agent for each account + coordinator for transfers

**Cross-reference**: See `convert-clojure-elixir` for complete STM → Actor translation patterns.

---

#### Example 2: Go Channels → TypeScript Promises (Backpressure-Aware Pipeline)

**Problem**: Go channels provide natural backpressure through blocking sends/receives. JavaScript Promises execute eagerly and don't support backpressure natively.

**Go (Source):**

```go
func processStream(items []string) <-chan string {
    out := make(chan string, 10) // Buffered channel

    go func() {
        defer close(out)
        for _, item := range items {
            // Simulate slow processing
            time.Sleep(100 * time.Millisecond)
            processed := strings.ToUpper(item)
            out <- processed // Blocks if buffer full (backpressure!)
        }
    }()

    return out
}

func main() {
    results := processStream([]string{"a", "b", "c", "d", "e"})

    for result := range results {
        fmt.Println("Got:", result)
        time.Sleep(200 * time.Millisecond) // Slow consumer
    }
}
```

**TypeScript (Target - Idiomatic Translation):**

```typescript
async function* processStream(items: string[]): AsyncGenerator<string> {
  for (const item of items) {
    // Simulate slow processing
    await new Promise(resolve => setTimeout(resolve, 100));
    const processed = item.toUpperCase();
    yield processed; // Yields control back to consumer
  }
}

async function main() {
  const items = ["a", "b", "c", "d", "e"];

  for await (const result of processStream(items)) {
    console.log("Got:", result);
    await new Promise(resolve => setTimeout(resolve, 200)); // Slow consumer
  }
}

main();
```

**Alternative (Explicit Promise Queue with Backpressure):**

```typescript
class Channel<T> {
  private queue: T[] = [];
  private waiting: ((value: T) => void)[] = [];
  private maxSize: number;

  constructor(bufferSize: number = 10) {
    this.maxSize = bufferSize;
  }

  async send(value: T): Promise<void> {
    // Backpressure: wait if queue is full
    while (this.queue.length >= this.maxSize) {
      await new Promise(resolve => setTimeout(resolve, 10));
    }

    if (this.waiting.length > 0) {
      const resolve = this.waiting.shift()!;
      resolve(value);
    } else {
      this.queue.push(value);
    }
  }

  async receive(): Promise<T> {
    if (this.queue.length > 0) {
      return this.queue.shift()!;
    }

    return new Promise<T>(resolve => {
      this.waiting.push(resolve);
    });
  }
}

async function processStreamWithChannel(items: string[]): Promise<Channel<string>> {
  const ch = new Channel<string>(10);

  (async () => {
    for (const item of items) {
      await new Promise(resolve => setTimeout(resolve, 100));
      await ch.send(item.toUpperCase());
    }
  })();

  return ch;
}
```

**Translation Strategy**:

1. **Async generators**: Most idiomatic for streaming data in TypeScript
2. **Yield control**: `yield` allows consumer to control pace (like channel receive blocking)
3. **Custom Channel class**: For explicit buffering and backpressure semantics
4. **Event-driven alternative**: Use Node.js streams for larger data pipelines

**Edge Cases & Gotchas**:

- **No native backpressure**: Promises execute eagerly; must implement queue manually
- **Memory growth**: Without backpressure, fast producer overwhelms slow consumer
- **Cancellation**: Go channels close; async generators must handle cleanup explicitly
- **Error propagation**: Go errors via multiple returns; TypeScript uses try/catch or rejected promises

**Performance Implications**:

- Go channels: OS-level blocking, true concurrency across cores
- TypeScript async: Event loop, single-threaded unless using worker threads
- For CPU-bound work, consider worker threads or external processing

**Cross-reference**: See `convert-typescript-golang` and `convert-golang-rust` for channel translation patterns.

---

#### Example 3: Python Asyncio → Erlang Processes (Event Loop → Actor Model)

**Problem**: Python's asyncio uses a single-threaded event loop with coroutines. Erlang uses lightweight processes with true preemptive concurrency.

**Python (Source):**

```python
import asyncio
from typing import Dict

class ConnectionPool:
    def __init__(self, max_connections: int = 10):
        self.max_connections = max_connections
        self.active_connections: Dict[str, asyncio.Task] = {}
        self.semaphore = asyncio.Semaphore(max_connections)

    async def connect(self, conn_id: str):
        async with self.semaphore:
            print(f"Connecting {conn_id}")
            await asyncio.sleep(1)  # Simulate connection
            self.active_connections[conn_id] = asyncio.current_task()
            print(f"Connected {conn_id}")
            return conn_id

    async def disconnect(self, conn_id: str):
        if conn_id in self.active_connections:
            print(f"Disconnecting {conn_id}")
            del self.active_connections[conn_id]

async def main():
    pool = ConnectionPool(max_connections=3)

    # Start 5 connections (only 3 concurrent due to semaphore)
    tasks = [pool.connect(f"conn-{i}") for i in range(5)]
    await asyncio.gather(*tasks)

asyncio.run(main())
```

**Erlang (Target - Idiomatic Translation):**

```erlang
-module(connection_pool).
-behaviour(gen_server).

%% API
-export([start_link/1, connect/2, disconnect/2, stop/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).

-record(state, {
    max_connections :: integer(),
    active_connections :: #{binary() => pid()},
    waiting_queue :: queue:queue()
}).

%% Client API
start_link(MaxConnections) ->
    gen_server:start_link(?MODULE, MaxConnections, []).

connect(PoolPid, ConnId) ->
    gen_server:call(PoolPid, {connect, ConnId}, infinity).

disconnect(PoolPid, ConnId) ->
    gen_server:cast(PoolPid, {disconnect, ConnId}).

stop(PoolPid) ->
    gen_server:stop(PoolPid).

%% Server Callbacks
init(MaxConnections) ->
    State = #state{
        max_connections = MaxConnections,
        active_connections = #{},
        waiting_queue = queue:new()
    },
    {ok, State}.

handle_call({connect, ConnId}, From, State) ->
    #state{
        max_connections = Max,
        active_connections = Active,
        waiting_queue = Queue
    } = State,

    case maps:size(Active) < Max of
        true ->
            %% Spawn connection process
            Pid = spawn_link(fun() -> connection_worker(ConnId) end),
            io:format("Connecting ~s~n", [ConnId]),
            NewActive = maps:put(ConnId, Pid, Active),
            {reply, {ok, ConnId}, State#state{active_connections = NewActive}};

        false ->
            %% Queue is full, add to waiting queue
            NewQueue = queue:in({ConnId, From}, Queue),
            {noreply, State#state{waiting_queue = NewQueue}}
    end.

handle_cast({disconnect, ConnId}, State) ->
    #state{
        active_connections = Active,
        waiting_queue = Queue
    } = State,

    io:format("Disconnecting ~s~n", [ConnId]),
    NewActive = maps:remove(ConnId, Active),

    %% Process waiting queue
    case queue:out(Queue) of
        {{value, {NextConnId, From}}, NewQueue} ->
            Pid = spawn_link(fun() -> connection_worker(NextConnId) end),
            gen_server:reply(From, {ok, NextConnId}),
            FinalActive = maps:put(NextConnId, Pid, NewActive),
            {noreply, State#state{
                active_connections = FinalActive,
                waiting_queue = NewQueue
            }};

        {empty, _} ->
            {noreply, State#state{active_connections = NewActive}}
    end.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

%% Internal Functions
connection_worker(ConnId) ->
    timer:sleep(1000),  %% Simulate connection
    io:format("Connected ~s~n", [binary_to_list(ConnId)]),
    receive
        disconnect -> ok
    end.

%% Usage example
%% 1> {ok, Pool} = connection_pool:start_link(3).
%% 2> [connection_pool:connect(Pool, list_to_binary("conn-" ++ integer_to_list(I))) || I <- lists:seq(1, 5)].
```

**Translation Strategy**:

1. **Event loop → GenServer**: Central coordinator manages connection state
2. **Semaphore → Capacity check**: Manual tracking of active connections vs max
3. **Coroutines → Processes**: Each connection is a separate process (true isolation)
4. **Async tasks → spawn_link**: Lightweight processes with supervision
5. **Queue for backpressure**: Explicitly manage waiting clients when pool full

**Edge Cases & Gotchas**:

- **Blocking vs message passing**: Python blocks on semaphore; Erlang queues requests
- **Error handling**: Python exceptions in event loop; Erlang links and monitors for crash detection
- **Process cleanup**: Must handle process termination and queue cleanup
- **Timeout handling**: Add receive timeout patterns for connection timeouts

**Performance Implications**:

- Python asyncio: Single thread, cooperative scheduling, GIL limitations
- Erlang: True concurrency, processes scheduled across cores, no GIL
- Erlang's preemptive scheduling handles blocking better (processes don't block each other)
- Consider supervision tree for fault tolerance in Erlang

**Cross-reference**: See `convert-python-erlang` for complete asyncio → BEAM translation patterns.

---

### Supervision and Fault Tolerance Translation

Supervision is a key pattern in fault-tolerant systems. Different concurrency models provide different supervision mechanisms:

| Source Model | Target Model | Translation Pattern |
|--------------|--------------|---------------------|
| **BEAM Supervision Trees** | **Go error handling** | Explicit error returns, retry loops, health checks |
| **BEAM Supervision Trees** | **Rust Result + retry crate** | Result types for errors, tokio::task for spawning, manual restart |
| **BEAM let-it-crash** | **Clojure agents + error handlers** | Agent error-handler and error-mode, restart-agent |
| **Go defer/panic/recover** | **BEAM try/catch + supervisor** | Convert panic → exit, recover → supervisor restart |
| **Python try/except** | **BEAM let-it-crash** | Remove defensive error handling, use supervisor for recovery |
| **Rust panic=abort** | **BEAM supervision** | Change panic strategy to supervised process restart |

#### Supervision Pattern: Erlang Supervision Tree → Go Error Handling

**Erlang (Source - Let It Crash):**

```erlang
-module(worker_sup).
-behaviour(supervisor).

-export([start_link/0, init/1]).

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    SupFlags = #{
        strategy => one_for_one,      %% Restart individual workers
        intensity => 5,                %% Max 5 restarts
        period => 60                   %% In 60 seconds
    },

    ChildSpecs = [
        #{
            id => worker1,
            start => {worker, start_link, [worker1]},
            restart => permanent,      %% Always restart
            shutdown => 5000,
            type => worker
        }
    ],

    {ok, {SupFlags, ChildSpecs}}.

%% Worker module (crashes are OK, supervisor restarts)
-module(worker).
-behaviour(gen_server).

handle_call({process, Data}, _From, State) ->
    Result = risky_operation(Data),  %% May crash - that's fine!
    {reply, Result, State}.

risky_operation(Data) ->
    case Data of
        invalid -> error(badarg);     %% Crash - supervisor will restart
        _ -> process_data(Data)
    end.
```

**Go (Target - Explicit Error Handling + Retry):**

```go
package main

import (
    "context"
    "errors"
    "fmt"
    "log"
    "time"
)

// WorkerSupervisor manages worker lifecycle with restart logic
type WorkerSupervisor struct {
    maxRestarts int
    period      time.Duration
    restartLog  []time.Time
    workerFunc  func(context.Context) error
    ctx         context.Context
    cancel      context.CancelFunc
}

func NewWorkerSupervisor(
    maxRestarts int,
    period time.Duration,
    workerFunc func(context.Context) error,
) *WorkerSupervisor {
    ctx, cancel := context.WithCancel(context.Background())
    return &WorkerSupervisor{
        maxRestarts: maxRestarts,
        period:      period,
        workerFunc:  workerFunc,
        restartLog:  make([]time.Time, 0),
        ctx:         ctx,
        cancel:      cancel,
    }
}

func (s *WorkerSupervisor) Start() {
    go s.supervise()
}

func (s *WorkerSupervisor) Stop() {
    s.cancel()
}

func (s *WorkerSupervisor) supervise() {
    for {
        select {
        case <-s.ctx.Done():
            log.Println("Supervisor shutting down")
            return
        default:
            // Check restart intensity
            if s.exceedsRestartIntensity() {
                log.Println("Max restart intensity exceeded, shutting down")
                return
            }

            // Run worker
            err := s.workerFunc(s.ctx)
            if err != nil {
                log.Printf("Worker crashed: %v, restarting...", err)
                s.logRestart()
                time.Sleep(1 * time.Second) // Backoff before restart
            }
        }
    }
}

func (s *WorkerSupervisor) exceedsRestartIntensity() bool {
    now := time.Now()
    cutoff := now.Add(-s.period)

    // Remove old restart entries
    validRestarts := make([]time.Time, 0)
    for _, t := range s.restartLog {
        if t.After(cutoff) {
            validRestarts = append(validRestarts, t)
        }
    }
    s.restartLog = validRestarts

    return len(s.restartLog) >= s.maxRestarts
}

func (s *WorkerSupervisor) logRestart() {
    s.restartLog = append(s.restartLog, time.Now())
}

// Worker implementation
type Worker struct {
    id string
}

func (w *Worker) Run(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return ctx.Err()
        default:
            // Simulate work with potential errors
            if err := w.riskyOperation(); err != nil {
                return fmt.Errorf("worker %s failed: %w", w.id, err)
            }
            time.Sleep(1 * time.Second)
        }
    }
}

func (w *Worker) riskyOperation() error {
    // Simulate occasional failures
    if time.Now().Unix()%10 == 0 {
        return errors.New("random failure")
    }
    log.Printf("Worker %s processing...", w.id)
    return nil
}

func main() {
    worker := &Worker{id: "worker1"}

    supervisor := NewWorkerSupervisor(
        5,                    // Max 5 restarts
        60*time.Second,       // In 60 seconds
        worker.Run,
    )

    supervisor.Start()

    // Run for demo duration
    time.Sleep(120 * time.Second)
    supervisor.Stop()
}
```

**Translation Strategy**:

1. **Supervisor behavior → Supervisor struct**: Encapsulate restart logic in struct
2. **Restart strategies**: Implement one-for-one manually (each worker independent)
3. **Intensity tracking**: Log restart times, check against intensity limits
4. **Context for cancellation**: Use context.Context for graceful shutdown
5. **Error propagation**: Workers return errors instead of crashing

**Supervision Translation Rules**:

- `one_for_one` → Independent goroutines with individual supervisors
- `one_for_all` → Cancel all workers if one fails, restart all
- `rest_for_one` → Cancel dependent workers (those started after failed worker)
- Restart intensity → Track restart timestamps, check against limits

**Error Isolation Strategies**:

| Language | Isolation Mechanism | Fault Containment |
|----------|---------------------|-------------------|
| **Erlang/Elixir** | Separate processes, supervision trees | Process crash doesn't affect others |
| **Go** | Goroutines + defer/recover, error returns | Panic in goroutine kills goroutine only (with recover) |
| **Rust** | Result/Option types, panic=abort or unwind | Thread panic isolated, Result forces error handling |
| **Clojure** | Agents with error-mode, futures with deref | Agent errors caught in error-handler |
| **Haskell** | Async with catches, STM for atomicity | Exceptions in async isolated, catchable |
| **Python** | Threading/asyncio with try/except | Thread exceptions isolated, asyncio tasks can be cancelled |

**Cross-reference**:

- See `convert-python-erlang` for asyncio → supervision patterns
- See `convert-golang-rust` for panic/recover → Result type patterns
- See `convert-clojure-elixir` for agent error handling → OTP patterns

---

### Common Concurrency Anti-Patterns to Avoid

When translating concurrency patterns, avoid these common mistakes:

1. **Literal Translation of Synchronization Primitives**
   - ❌ Don't translate `lock()` in Python to `Mutex` in every target language
   - ✓ Use target's idiomatic concurrency: actors for Erlang, STM for Clojure, channels for Go

2. **Ignoring Backpressure**
   - ❌ Don't translate bounded channels to unbounded Promises (memory leak!)
   - ✓ Implement explicit backpressure with async generators or custom queues

3. **Mixing Concurrency Models**
   - ❌ Don't mix threads + async/await without understanding implications
   - ✓ Choose one concurrency model and use it consistently

4. **Over-Serialization**
   - ❌ Don't funnel all parallel operations through a single actor/GenServer
   - ✓ Shard state across multiple actors/processes for scalability

5. **Under-Isolation**
   - ❌ Don't share mutable state across goroutines without synchronization
   - ✓ Use message passing or proper synchronization primitives

6. **Ignoring Ordering Guarantees**
   - ❌ Don't assume message ordering when target language doesn't guarantee it
   - ✓ Add explicit sequence numbers or use ordered channels if needed

---

## Skill Template

When creating a new `convert-X-Y` skill, use this template:

```markdown
---
name: convert-<source>-<target>
description: Convert <Source> code to <Target>. Use when migrating <Source> projects to <Target>, translating <Source> patterns to idiomatic <Target>, or refactoring <Source> codebases into <Target>. Extends meta-convert-dev with <Source>-to-<Target> specific patterns.
---

# Convert <Source> to <Target>

Convert <Source> code to idiomatic <Target>. This skill extends `meta-convert-dev` with <Source>-to-<Target> specific type mappings, idiom translations, and tooling.

## This Skill Extends

- `meta-convert-dev` - Skill creation patterns
- `meta-convert-guide` - Conversion methodology (APTV workflow, testing strategies)

## This Skill Adds

- **Type mappings**: <Source> types → <Target> types
- **Idiom translations**: <Source> patterns → idiomatic <Target>
- **Error handling**: <Source> exceptions/errors → <Target> approach
- **Async patterns**: <Source> async → <Target> async
- **Tooling**: <Source>-to-<Target> specific tools

## Quick Reference

| <Source> | <Target> | Notes |
|----------|----------|-------|
| `<type1>` | `<type1>` | ... |
| `<type2>` | `<type2>` | ... |

## [Continue with 8 Pillar sections...]
```

---

## References

### Reference Materials (in `references/` directory)

- [platform-ecosystem.md](references/platform-ecosystem.md) - Platform families, runtime characteristics, FFI, stdlib mapping
- [paradigm-translation.md](references/paradigm-translation.md) - OOP↔FP, imperative↔declarative, type system shifts
- [ownership-translation.md](references/ownership-translation.md) - GC↔ownership, borrowing patterns, lifetime translation
- [numeric-edge-cases.md](references/numeric-edge-cases.md) - Overflow, division semantics, float precision, arbitrary precision
- [stdlib-mapping.md](references/stdlib-mapping.md) - Common stdlib function equivalents across languages
- [build-system-mapping.md](references/build-system-mapping.md) - Package managers and build tools
- [migration-strategies.md](references/migration-strategies.md) - Migration approaches and strategies
- [module-system-comparison.md](references/module-system-comparison.md) - Import/export and visibility patterns
- [naming-conventions.md](references/naming-conventions.md) - Naming conventions across languages
- [performance-considerations.md](references/performance-considerations.md) - Performance patterns and considerations

### Conversion Methodology

- `meta-convert-guide` - APTV workflow, type system strategies, idiom patterns, testing approaches, common pitfalls

### Skills That Extend This Meta-Skill

These skills provide concrete, language-pair-specific implementations:

- `convert-typescript-rust` - TypeScript → Rust conversion
- `convert-typescript-python` - TypeScript → Python conversion
- `convert-typescript-golang` - TypeScript → Go conversion
- `convert-golang-rust` - Go → Rust conversion
- `convert-python-rust` - Python → Rust conversion

### Related Meta-Skills

- `meta-library-dev` - Library development patterns

### Language Skills

For language-specific fundamentals (not conversion):

- `lang-typescript-dev` - TypeScript development patterns
- `lang-python-dev` - Python development patterns
- `lang-golang-dev` - Go development patterns
- `lang-rust-dev` - Rust development patterns

### Commands

- `/create-lang-conversion-skill <source> <target>` - Create a new conversion skill using this meta-skill as foundation

Files in this skill

  • SKILL.md36.3 KB
  • references/build-system-mapping.md7 KB
  • references/migration-strategies.md7.1 KB
  • references/module-system-comparison.md9 KB
  • references/naming-conventions.md7.4 KB
  • references/numeric-edge-cases.md10 KB
  • references/ownership-translation.md12.1 KB
  • references/paradigm-translation.md10.2 KB
  • references/performance-considerations.md8.1 KB
  • references/platform-ecosystem.md7.5 KB
  • references/stdlib-mapping.md12.3 KB

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.