Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 200x 200x 200x 200x 200x 200x 2x 2x 2x 2x 2x 2x 200x 200x 200x 200x 200x 200x 2x 2x 2x | import {inverse} from '#algebra' import {Law, LawSet} from '#law' import {Isomorphism} from '#typeclass' import {pipe} from 'effect' import {type Equivalence} from 'effect/Equivalence' import fc from 'fast-check' import {defineConcreteLaws} from './given.js' /** * Build typeclass laws for isomorphisms that share their `A` type parameter. * @category typeclass laws */ export const buildIsomorphismLaws = <A>(base: BaseIsomorphismGiven<A>) => <Encodings extends BaseEncoding<A>>(encodings: Encodings) => { type Entry<K extends keyof Encodings> = [K & string, Encodings[K]] const results: LawSet[] = [] for (const entry of Object.entries(encodings)) { const [suffix, entryGiven] = entry as Entry<keyof Encodings> const given: IsomorphismGivenFor<A, any> & BaseIsomorphismGiven<A> = { ...base, ...entryGiven, } results.push(isomorphismLaws(suffix, given)) } return results } /** * Build typeclass laws for `Isomorphism`. * @category typeclass laws */ export const isomorphismLaws = <A, B>( suffix: string, {a, b, equalsA, equalsB, F}: IsomorphismGiven<A, B>, ): LawSet => { const [encode, decode] = [Isomorphism.encode(F), Isomorphism.decode(F)] return defineConcreteLaws( 'Isomorphism', inverse<A, B>( {f: encode, g: decode, a, equals: equalsA}, 'F.encode ⚬ F.decode = id', 'decode/encode identity', ), inverse<B, A>( {f: decode, g: encode, a: b, equals: equalsB}, 'F.decode ⚬ F.encode = id', 'encode/decode identity', ), Law( 'reverse compose encode identity', 'compose(F, reverse(F)).encode = id', a, )(a => equalsA( Isomorphism.encode( pipe(F, Isomorphism.reverse, Isomorphism.compose(F)), )(a), a, ), ), Law( 'reverse compose decode identity', 'compose(F, reverse(F)).decode = id', a, )(a => equalsA( Isomorphism.decode( pipe(F, Isomorphism.reverse, Isomorphism.compose(F)), )(a), a, ), ), )(suffix) } declare module './given.js' { interface ConcreteLambdas { Isomorphism: Isomorphism.IsomorphismTypeLambda } } export type IsomorphismGiven<A, B> = IsomorphismGivenFor<A, B> & BaseIsomorphismGiven<A> /** * Record of arguments required to test several encodings of the type `A`. Each * `Isomorphism` requires an equivalence and an arbitrary for the encoded type, * where the key is the encoding name. As all the encodings share a decoded * type, only a single arbitrary/equivalence of `A` is required. * @category typeclass laws */ export type BaseEncoding<A> = Record<string, IsomorphismGivenFor<A, any>> /** * Arguments required to test a single `Isomorphism<A,B>`. * @category typeclass laws */ export interface IsomorphismGivenFor<A, B> { /** An equivalence for the decoded values of the isomorphism. */ equalsB: Equivalence<B> /** An arbitrary for the decoded values of the isomorphism. */ b: fc.Arbitrary<B> /** Instance of the typeclass under test. */ F: Isomorphism.Isomorphism<A, B> } /** * Shared arguments required for testing an `Isomorphism` with a decoded type * `A`. * @category typeclass laws */ export interface BaseIsomorphismGiven<A> { /** An equivalence for the decoded value of the `Isomorphism` of type `A`. */ equalsA: Equivalence<A> /** An arbitrary for the decoded value of the `Isomorphism` of type `A`. */ a: fc.Arbitrary<A> } |