一键导入
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))