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>
}
|