Agent Skills: Language Conversion Skill Development

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.

UncategorizedID: arustydev/ai/meta-convert-dev

Repository

aRustyDevLicense: AGPL-3.0
72

Install this agent skill to your local

pnpm dlx add-skill https://github.com/aRustyDev/agents/tree/HEAD/content/skills/meta-convert-dev

Skill Files

Browse the full folder contents for meta-convert-dev.

Download Skill

Loading file tree…

content/skills/meta-convert-dev/SKILL.md

Skill Metadata

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 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 for detailed patterns and examples.

Additional Reference Materials

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


Conversion Skill Naming Convention

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:

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

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

4. Document Idiom Translations

Show idiomatic translations, not literal ports:

// 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):

(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):

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

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

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

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

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

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

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

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:

---
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)

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