Skip to content

Latest commit

 

History

History
249 lines (198 loc) · 31.7 KB

README.md

File metadata and controls

249 lines (198 loc) · 31.7 KB

⚖ effect-ts-laws

Law Testing for effect-ts Instances

A library for testing effect-ts typeclass laws using fast-check. API is documented here, and the laws are listed here.

  1. About
    1. Synopsis
    2. Overview
  2. Usage
  3. Project
    1. Play
    2. Status
    3. More Information
    4. Open Questions
    5. Roadmap
  4. See Also
    1. Based On

About

Synopsis

Run all typeclass law tests relevant to the effect-ts Option datatype:

import {Alternative, Applicative, getOptionalMonoid, Monad, Traversable} from '@effect/typeclass/data/Option'
import {Option as OP} from 'effect'
import {monoEquivalence, monoOrder, monoSemigroup, option} from 'effect-ts-laws'
import {testTypeclassLaws} from 'effect-ts-laws/vitest'
import {OptionTypeLambda} from 'effect/Option'

describe('@effect/typeclass/data/Option', () => {
  testTypeclassLaws<OptionTypeLambda>({
    getEquivalence: OP.getEquivalence,
    getArbitrary: option,
  })({
    Alternative,
    Applicative,
    Equivalence: OP.getEquivalence(monoEquivalence),
    Monad,
    Monoid: getOptionalMonoid(monoSemigroup),
    Order: OP.getOrder(monoOrder),
    Traversable,
  })
})

Vitest reporter shows test results for the Option datatype:

synopsis output


New datatype example.👈 click

You wrote a new datatype: MyTuple, and an instance of the effect-ts Covariant typeclass. Lets test it for free:

import {Covariant as CO} from '@effect/typeclass'
import {Array as AR} from 'effect'
import {dual} from 'effect/Function'
import {TypeLambda} from 'effect/HKT'
import fc from 'fast-check'
import {testTypeclassLaws} from 'effect-ts-laws/vitest'

describe('MyTuple', () => {
  type MyTuple<A> = [A]

  interface MyTupleTypeLambda extends TypeLambda {
    readonly type: MyTuple<this['Target']>
  }

  const map: CO.Covariant<MyTupleTypeLambda>['map'] = dual(
    2,
    <A, B>([a]: MyTuple<A>, ab: (a: A) => B): MyTuple<B> => [ab(a)],
  )
  const Covariant: CO.Covariant<MyTupleTypeLambda> = {
    imap: CO.imap<MyTupleTypeLambda>(map),
    map,
  }

  testTypeclassLaws<MyTupleTypeLambda>({
    getEquivalence: AR.getEquivalence,
    getArbitrary: fc.tuple,
  })({Covariant})
})

fast-check will try to find a counter example that breaks the laws. Because it is quite impossible to find one in this case you should see:

synopsis output


Overview

Law testing is useful when you are building your own datatype and its associated effect-ts instances. Law tests help you verify your instances are lawful. This is a library of the effect-ts typeclass laws, and some law testing infrastructure.

The implementation features:

  • effect-ts datatype typeclass law tests. Because:
    • It could help effect-ts.
    • Serves as an excellent self-test suite and as an example for testing your own instances.
    • See status for details on what is ready.
  • Typeclass instances and typeclass law tests for the fast-check Arbitrary type.
  • Randomness. Uses fast-check property testing. For parameterized type typeclass laws, all functions are randomly generated as well.
  • Minimal work to test instances for your own datatype: it can all be done with single function that takes the instances under test and a pair of functions: getEquivalence and getArbitrary.
    • Meaningful test coverage improvement for the price of writing two functions. You probably have them somewhere already.

Usage

To install, replace pnpm with your package manager:

# Peer dependencies:
pnpm i effect-ts @effect/typeclass
pnpm i -D vitest fast-check @fast-check/vitest

# Install effect-ts-laws
pnpm i -D effect-ts-laws

API is documented here.

Project

Play

You can run the project tests online at any of these online sandboxes by opening a terminal and calling pnpm install && pnpm test-run. pnpm coverage will give you the always 100% coverage report.

  1. StackBlitz. Note coverage does not work on StackBlitz.
  2. replit requires you fork the repository first by clicking the green Fork button.
  3. CodeSandbox.

Status

Matrix showing data-types (in columns) vs. typeclass law tests (in rows). Each intersection of datatype and typeclass can be either: ready (✅), not ready (❌), or not relevant (☐). First data row show the typeclass laws implementation status, and first data column shows datatype tests implementation status. Note Equivalence and Order are both typeclasses and datatypes. You can also view this in an online spreadsheet.

Typeclass→ Equivalence Order Bounded Semigroup Monoid Invariant Contravariant Covariant SemiAlternative Alternative Applicative Monad Bicovariant Traversable Foldable
↓Datatype
1. Array
2. BigDecimal
3. BigInt
4. Boolean
5. Cause
6. DateTime
7. Duration
8. Effect
9. Either
10. Equivalence
11. Identity
12. List
13. Number
14. Option
15. Order
16. Predicate
17. Record
18. String
19. Struct
20. Tuple

More Information

Open Questions

  1. Rename to zio-style names? E.g.: traverse laws become foreach laws?
  2. Match the typeclass graph of effect-ts more accurately? E.g.: split Product laws out of Applicative.
  3. What is the role of typeclass laws in effect-ts where typeclasses are deemphasized?

Roadmap

  • Tests

    • More datatypes.
    • Bicovariant should do Invariant laws twice.
    • Schema decode/encode laws
    • Sink Contravariant laws
  • Harness

    • API should let you use any catalog
  • Composition

    • Test composition flipped.
    • Compose arbitrary instances in composition tests.
    • Nest three levels.
    • Brand composition: refine(b₂) ∘ refine(b₁) = refine.all(b₁, b₂).
  • Arbitraries

    • oneof arbitrary chosen from built-in instances.

See Also

  1. fast-check
  2. effect-ts
  3. zio-prelude laws
  4. On the importance of typeclass laws

Based On

  1. fp-ts-laws by Giulio Canti
  2. Scala's Discipline