ワンクリックで
typeclass-design
Implement typeclasses with curried signatures and dual APIs for both data-first and data-last usage
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Implement typeclasses with curried signatures and dual APIs for both data-first and data-last usage
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
Implement reactive state management with Effect Atom for React applications
Decide between Context Tag witness and capability patterns for dependency injection, understanding coupling trade-offs
Generate comprehensive predicates and orders for domain types using typeclass patterns
Design and compose Effect layers for clean dependency management
Implement Effect services as fine-grained capabilities avoiding monolithic designs
SOC 職業分類に基づく
| name | typeclass-design |
| description | Implement typeclasses with curried signatures and dual APIs for both data-first and data-last usage |
Use this skill when implementing typeclasses that provide reusable abstractions across multiple types.
All typeclass functions must be fully curried to enable partial application:
import { Function } from "effect"
// 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)
}
Provide both data-first (uncurried) and data-last (curried) variants using Function.dual:
import { Function } from "effect"
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)
}
)
The dual API enables both styles:
import { pipe } from "effect/Function"
import * as Duration from "effect/Duration"
// Data-first: Direct function call
const hasMinimum = isMoreThan(Appointment.Durable)(
appointment,
Duration.hours(1)
)
// Data-last: Pipe-friendly
const hasMinimum = pipe(
appointment,
isMoreThan(Appointment.Durable)(Duration.hours(1))
)
// Partial application for filtering
const longAppointments = appointments.filter(
isMoreThan(Appointment.Durable)(Duration.hours(1))
)
import { Function, Duration, Order } from "effect"
/**
* 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))