agentsclimarketplace

Fp refactor

Skill ranbot-ai/awesome-skills/skills/fp-refactor

Awesome Claude Skills, Tools for Customizing Claude AI workflows

Install
npx -y skills add ranbot-ai/awesome-skills --skill fp-refactor

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Comprehensive guide for refactoring imperative TypeScript code to fp-ts functional patterns

SKILL.md

5.2 KB, as published. Nobody here has run it

Refactoring Imperative Code to fp-ts

This skill provides comprehensive patterns and strategies for migrating existing imperative TypeScript code to fp-ts functional programming patterns.

When to Use

  • You are refactoring an existing imperative TypeScript codebase toward fp-ts patterns.
  • The task involves converting try/catch, null checks, callbacks, DI, or loops into functional equivalents.
  • You need migration guidance and tradeoffs, not just isolated fp-ts examples.

Table of Contents

  1. Converting try-catch to Either/TaskEither
  2. Converting null checks to Option
  3. Converting callbacks to Task
  4. Converting class-based DI to Reader
  5. Converting imperative loops to functional operations
  6. Migrating Promise chains to TaskEither
  7. Common Pitfalls
  8. Gradual Adoption Strategies
  9. When NOT to Refactor

1. Converting try-catch to Either/TaskEither

The Problem with try-catch

Traditional try-catch blocks have several issues:

  • Error handling is implicit and easy to forget
  • The type system doesn't track which functions can throw
  • Control flow is non-linear and harder to reason about
  • Composing multiple fallible operations is verbose

Pattern: Synchronous try-catch to Either

Before (Imperative)

function parseJSON(input: string): unknown {
  try {
    return JSON.parse(input);
  } catch (error) {
    throw new Error(`Invalid JSON: ${error}`);
  }
}

function validateUser(data: unknown): User {
  try {
    if (!data || typeof data !== 'object') {
      throw new Error('Data must be an object');
    }
    const obj = data as Record<string, unknown>;
    if (typeof obj.name !== 'string') {
      throw new Error('Name is required');
    }
    if (typeof obj.age !== 'number') {
      throw new Error('Age must be a number');
    }
    return { name: obj.name, age: obj.age };
  } catch (error) {
    throw error;
  }
}

// Usage with nested try-catch
function processUserInput(input: string): User | null {
  try {
    const data = parseJSON(input);
    const user = validateUser(data);
    return user;
  } catch (error) {
    console.error('Failed to process user:', error);
    return null;
  }
}

After (fp-ts Either)

import * as E from 'fp-ts/Either';
import * as J from 'fp-ts/Json';
import { pipe } from 'fp-ts/function';

interface User {
  name: string;
  age: number;
}

// Use Json.parse which returns Either<Error, Json>
const parseJSON = (input: string): E.Either<Error, unknown> =>
  pipe(
    J.parse(input),
    E.mapLeft((e) => new Error(`Invalid JSON: ${e}`))
  );

// Validation returns Either, making errors explicit in types
const validateUser = (data: unknown): E.Either<Error, User> => {
  if (!data || typeof data !== 'object') {
    return E.left(new Error('Data must be an object'));
  }
  const obj = data as Record<string, unknown>;
  if (typeof obj.name !== 'string') {
    return E.left(new Error('Name is required'));
  }
  if (typeof obj.age !== 'number') {
    return E.left(new Error('Age must be a number'));
  }
  return E.right({ name: obj.name, age: obj.age });
};

// Compose with pipe and flatMap - errors propagate automatically
const processUserInput = (input: string): E.Either<Error, User> =>
  pipe(
    parseJSON(input),
    E.flatMap(validateUser)
  );

// Handle both cases explicitly
pipe(
  processUserInput('{"name": "Alice", "age": 30}'),
  E.match(
    (error) => console.error('Failed to process user:', error.message),
    (user) => console.log('User:', user)
  )
);

Step-by-Step Refactoring Guide

  1. Identify the error type: Determine what errors can occur and create appropriate error types
  2. Change return type: From T to Either<E, T> where E is your error type
  3. Replace throw statements: Convert throw new Error(...) to E.left(new Error(...))
  4. Replace return statements: Convert return value to E.right(value)
  5. Remove try-catch blocks: They're no longer needed
  6. Update callers: Use pipe with E.flatMap to chain operations

Pattern: Async try-catch to TaskEither

Before (Imperative)

async function fetchUser(id: string): Promise<User> {
  try {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    const data = await response.json();
    return validateUser(data);
  } catch (error) {
    throw new Error(`Failed to fetch user: ${error}`);
  }
}

async function fetchUserPosts(userId: string): Promise<Post[]> {
  try {
    const response = await fetch(`/api/users/${userId}/posts`);
    if (!response.ok) {
    

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.