/** * The `Invariant` typeclass is a higher-order abstraction over types that allow mapping the contents of a type in both directions. * It is similar to the `Covariant` typeclass but provides an `imap` opration, which allows transforming a value in both directions. * This typeclass is useful when dealing with data types that can be converted to and from some other types. * The `imap` operation provides a way to convert such data types to other types that they can interact with while preserving their invariants. * * @since 0.24.0 */ import { dual } from "effect/Function" import type { Kind, TypeClass, TypeLambda } from "effect/HKT" /** * @category type class * @since 0.24.0 */ export interface Invariant extends TypeClass { readonly imap: { ( to: (a: A) => B, from: (b: B) => A ): (self: Kind) => Kind ( self: Kind, to: (a: A) => B, from: (b: B) => A ): Kind } } /** * Returns a default ternary `imap` composition. * * @since 0.24.0 */ export const imapComposition = ( F: Invariant, G: Invariant ) => ( self: Kind>, to: (a: A) => B, from: (b: B) => A ): Kind> => F.imap(self, G.imap(to, from), G.imap(from, to)) /** * @category do notation * @since 0.24.0 */ export const bindTo = (F: Invariant): { ( name: N ): (self: Kind) => Kind ( self: Kind, name: N ): Kind } => dual(2, ( self: Kind, name: N ): Kind => F.imap(self, (a) => ({ [name]: a } as any), ({ [name]: a }) => a)) /** * Convert a value in a singleton array in a given effect. * * @since 0.24.0 */ export const tupled = ( F: Invariant ): (self: Kind) => Kind => F.imap((a) => [a], ([a]) => a)