スキル一覧に戻る
front-depiction

typeclass-design

by front-depiction

Reusable Claude Code configuration for Effect TypeScript projects with specialized agents and skills

9🍴 4📅 2026年1月18日
GitHubで見るManusで実行

SKILL.md


name: typeclass-design description: Implement typeclasses with curried signatures and dual APIs for both data-first and data-last usage

Typeclass Design Skill

Use this skill when implementing typeclasses that provide reusable abstractions across multiple types.

Pattern: Curried Typeclass Functions

All typeclass functions must be fully curried to enable partial application:

import { Duration } from "effect"

declare interface Durable<A> {
  readonly getDuration: (self: A) => Duration.Duration
}

// Create a typeclass function that takes the typeclass instance first,
// then curries out all other parameters
export const isMoreThan =
  <A>(D: Durable<A>) =>
  (minimum: Duration.DurationInput) =>
  (self: A): boolean => {
    const current = D.getDuration(self)
    return Duration.greaterThanOrEqualTo(current, minimum)
  }

Pattern: Dual APIs

Provide both data-first (uncurried) and data-last (curried) variants using Function.dual:

import { Duration } from "effect"
import * as Function from "effect/Function"

declare interface Durable<A> {
  readonly getDuration: (self: A) => Duration.Duration
}

export const isMoreThan = <A>(D: Durable<A>) =>
  Function.dual<
    // Data-last (curried) - pipe-friendly
    (minimum: Duration.DurationInput) => (self: A) => boolean,
    // Data-first (uncurried) - direct call
    (self: A, minimum: Duration.DurationInput) => boolean
  >(
    2,  // Number of arguments for data-first form
    (self: A, minimum: Duration.DurationInput): boolean => {
      const current = D.getDuration(self)
      return Duration.greaterThanOrEqualTo(current, minimum)
    }
  )

Usage Patterns

The dual API enables both styles:

import { pipe } from "effect/Function"
import * as Duration from "effect/Duration"
import * as Function from "effect/Function"

declare interface Durable<A> {
  readonly getDuration: (self: A) => Duration.Duration
}

declare const isMoreThan: <A>(D: Durable<A>) => {
  (minimum: Duration.DurationInput): (self: A) => boolean
  (self: A, minimum: Duration.DurationInput): boolean
}

declare interface Appointment {
  duration: Duration.Duration
}

declare const Appointment: {
  Durable: Durable<Appointment>
}

declare const appointment: Appointment
declare const appointments: Appointment[]

// Data-first: Direct function call
const hasMinimum = isMoreThan(Appointment.Durable)(
  appointment,
  Duration.hours(1)
)

// Data-last: Pipe-friendly
const hasMinimum2 = pipe(
  appointment,
  isMoreThan(Appointment.Durable)(Duration.hours(1))
)

// Partial application for filtering
const longAppointments = appointments.filter(
  isMoreThan(Appointment.Durable)(Duration.hours(1))
)

Complete Typeclass Example

import { Duration, Order } from "effect"
import * as Function from "effect/Function"

/**
 * Typeclass for types that have a duration.
 */
export interface Durable<A> {
  readonly getDuration: (self: A) => Duration.Duration
  readonly setDuration: (self: A, duration: Duration.DurationInput) => A
}

/**
 * Create a Durable instance.
 */
export const make = <A>(
  getDuration: (self: A) => Duration.Duration,
  setDuration: (self: A, duration: Duration.DurationInput) => A
): Durable<A> => ({
  getDuration,
  setDuration
})

/**
 * Check if duration is more than minimum.
 */
export const isMoreThan = <A>(D: Durable<A>) =>
  Function.dual<
    (minimum: Duration.DurationInput) => (self: A) => boolean,
    (self: A, minimum: Duration.DurationInput) => boolean
  >(
    2,
    (self: A, minimum: Duration.DurationInput): boolean =>
      Duration.greaterThanOrEqualTo(
        D.getDuration(self),
        Duration.decode(minimum)
      )
  )

/**
 * Order by duration.
 */
export const OrderByDuration = <A>(D: Durable<A>): Order.Order<A> =>
  Order.mapInput(Duration.Order, (self: A) => D.getDuration(self))

When to Use

  • Creating reusable abstractions (Schedulable, Durable, Priceable)
  • Implementing operations that work across multiple types
  • Providing composable, pipe-friendly APIs
  • Enabling partial application for filtering/mapping

Key Principles

  1. Curry everything - Enable partial application
  2. Dual APIs always - Support both usage styles
  3. Typeclass first - First parameter is always the typeclass instance
  4. Type lambda for HKT - Use TypeLambda pattern when needed

スコア

総合スコア

60/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です