Skip to main content Link Search Menu Expand Document (external link)

Match overview

Simple matcher that can only match against predicates and refinements. Fast and simple but sufficient for most use cases. As the matcher is so simple, it needs no compilation which really simplifies its use.


Table of contents


Constructors

make

Builds a new matcher

Signature

export declare const make: <Input>(input: Input) => Type<Input, never, Input>

Guards

has

Type guard

Signature

export declare const has: (u: unknown) => u is Type<unknown, unknown, unknown>

Models

Type (interface)

Type that represents a matcher

Signature

export interface Type<out Input, out Output, out Rest extends Input>
  extends Inspectable.Inspectable,
    Pipeable.Pipeable {
  /** The input to match */
  readonly input: Input
  /** The output of the matcher when it has been found */
  readonly output: Option.Option<Output>
  /** @internal */
  readonly [_TypeId]: {
    readonly _Input: Types.Covariant<Input>
    readonly _Output: Types.Covariant<Output>
    readonly _Rest: Types.Covariant<Rest>
  }
}

moduleTag

Module tag

Signature

export declare const moduleTag: "@parischap/effect-lib/Match/"

Utils

exhaustive

Returns the output of the matcher and shows a type error if Rest is not never

Signature

export declare const exhaustive: <Input, Output>(self: Type<Input, Output, never>) => Output

Example

import { MMatch, MTypes } from "@parischap/effect-lib"
import { pipe } from "effect"

const handlePrimitive = (value: MTypes.Primitive) => value
const handleNonPrimitive = (value: MTypes.NonPrimitive) => value

export const testMatcher = (value: MTypes.Unknown) =>
  pipe(
    value,
    MMatch.make,
    MMatch.when(MTypes.isPrimitive, handlePrimitive),
    MMatch.when(MTypes.isNonPrimitive, handleNonPrimitive),
    MMatch.exhaustive
  )

orElse

Returns self.output if self already has an output. Otherwise, returns the result of f applied to self.input.

Signature

export declare const orElse: <Input, Rest extends Input, Output1>(
  f: (value: NoInfer<Rest>) => Output1
) => <Output>(self: Type<Input, Output, Rest>) => Output | Output1

tryFunction

Returns a copy of self if self already has an output. Otherwise, tries f on self.input and matches if f returns a some whose value becomes the output of the matcher. Returns a copy of self if f returns a none. From a type perspective, the Rest will not be refined.

Signature

export declare const tryFunction: <Input, Rest extends Input, Output1>(
  f: (value: NoInfer<Rest>) => Option.Option<Output1>
) => <Output>(self: Type<Input, Output, Rest>) => Type<Input, Output | Output1, Rest>

Example

import { MMatch } from "@parischap/effect-lib"
import { pipe, Array } from "effect"

export const testMatcher = (value: ReadonlyArray<number>) =>
  pipe(
    value,
    MMatch.make,
    MMatch.tryFunction(Array.get(1)),
    MMatch.tryFunction(Array.get(5)),
    MMatch.orElse(() => 0)
  )

unsafeWhen

Same as orElse but we pass a predicate (useless from a javascript perspective) to tell the compiler what Rest should be. Useful when we know better than Typescript.

Signature

export declare const unsafeWhen: <Input, Rest extends Input, Refined extends Rest, Output1>(
  _predicate: Predicate.Refinement<Rest, Refined>,
  f: (value: NoInfer<Refined>) => Output1
) => <Output>(self: Type<Input, Output, Rest>) => Output | Output1

Example

import { MMatch, MTypes } from "@parischap/effect-lib"
import { pipe } from "effect"

const handlePrimitive = (value: MTypes.Primitive) => value
const handleNonPrimitive = (value: MTypes.NonPrimitive) => value

export const testMatcher = (value: MTypes.Unknown) =>
  pipe(
    value,
    MMatch.make,
    MMatch.when(MTypes.isNonPrimitive, handleNonPrimitive),
    MMatch.unsafeWhen(MTypes.isPrimitive, handlePrimitive)
  )

when

Matches against a refinement or a predicate. Returns a copy of self if self already has an output. Otherwise, applies the predicate/refinement to self.input. Returns a copy of self if the predicate returns false. Otherwise, returns a copy of self with the output set to f(self.input). From a type perspective, the Rest is only refined if the predicate is a refinement.

Signature

export declare const when: {
  <Input, Rest extends Input, Refined extends Rest, Output1>(
    predicate: Predicate.Refinement<NoInfer<Rest>, Refined>,
    f: (value: Refined) => Output1
  ): <Output>(self: Type<Input, Output, Rest>) => Type<Input, Output | Output1, Exclude<Rest, Refined>>
  <Input, Rest extends Input, Output1>(
    predicate: Predicate.Predicate<NoInfer<Rest>>,
    f: (value: Rest) => Output1
  ): <Output>(self: Type<Input, Output, Rest>) => Type<Input, Output | Output1, Rest>
}

Example

import { MMatch, MTypes } from "@parischap/effect-lib"
import { pipe } from "effect"

const handlePrimitive = (value: MTypes.Primitive) => value
const handleNonPrimitive = (value: MTypes.NonPrimitive) => value

export const testMatcher = (value: MTypes.Unknown) =>
  pipe(
    value,
    MMatch.make,
    MMatch.when(MTypes.isPrimitive, handlePrimitive),
    MMatch.when(MTypes.isNonPrimitive, handleNonPrimitive),
    MMatch.exhaustive
  )

whenAnd

Same as when but several predicates can be provided. The match occurs if all the predicates return true. From a type perspective, the Rest will only be refined if all predicates are refinements.

Signature

export declare const whenAnd: <
  Input,
  Rest extends Input,
  R extends MTypes.ReadonlyOverTwo<Predicate.Predicate<Rest>>,
  Output1
>(
  ...args: readonly [
    ...refinements: R,
    f: (
      value: NoInfer<MTypes.IntersectAndSimplify<MTypes.ToKeyIntersection<MPredicate.PredicatesToTargets<R>>, Rest>>
    ) => Output1
  ]
) => <Output>(
  self: Type<Input, Output, Rest>
) => Type<Input, Output | Output1, Exclude<Rest, MTypes.ToKeyIntersection<MPredicate.PredicatesToCoverages<R>>>>

whenIs

Matches against a primitive value sparing you the need to define a type guard. Returns a copy of self if self already has an output. Otherwise, self.input is compared to the provided value using strict equality. Returns a copy of self if the two values are not equal. Otherwise, returns a copy of self with the output set to the result of f(self.input). From a type perspective, the Rest will only be ‘refined if its a finite type like an Enum (but not number or string)

Signature

export declare const whenIs: <Input extends MTypes.Primitive, Rest extends Input, const A extends Rest, Output1>(
  value: A,
  f: (value: A) => Output1
) => <Output>(self: Type<Input, Output, Rest>) => Type<Input, Output | Output1, Exclude<Rest, A>>

Example

import { MMatch } from "@parischap/effect-lib"
import { pipe } from "effect"

enum TestEnum {
  A = `a`,
  B = `b`,
  C = `c`
}

export const testMatcher = (value: TestEnum) =>
  pipe(
    value,
    MMatch.make,
    MMatch.whenIs(TestEnum.A, () => `a`),
    MMatch.whenIs(TestEnum.B, () => `b`),
    MMatch.whenIs(TestEnum.C, () => `c`),
    MMatch.exhaustive
  )

whenIsOr

Same as whenIs but you can pass several values to match against.

Signature

export declare const whenIsOr: <
  Input extends MTypes.Primitive,
  Rest extends Input,
  R extends MTypes.ReadonlyOverTwo<Rest>,
  Output1
>(
  ...args: readonly [...values: R, f: (value: R[number]) => Output1]
) => <Output>(self: Type<Input, Output, Rest>) => Type<Input, Output | Output1, Exclude<Rest, R[number]>>

Example

import { MMatch } from "@parischap/effect-lib"
import { pipe } from "effect"

enum TestEnum {
  A = `a`,
  B = `b`,
  C = `c`
}

export const testMatcher = (value: TestEnum) =>
  pipe(
    value,
    MMatch.make,
    MMatch.whenIs(TestEnum.A, () => `a`),
    MMatch.whenIs(TestEnum.B, () => `b`),
    MMatch.whenIs(TestEnum.C, () => `c`),
    MMatch.exhaustive
  )

whenOr

Same as when but several predicates can be provided. The match occurs if one of the predicate returns true. From a type perspective, the Rest will only be refined if all predicates are refinements.

Signature

export declare const whenOr: <
  Input,
  Rest extends Input,
  R extends MTypes.ReadonlyOverTwo<Predicate.Predicate<Rest>>,
  Output1
>(
  ...args: readonly [
    ...refinements: R,
    f: (value: NoInfer<MTypes.IntersectAndSimplify<MPredicate.PredicatesToTargets<R>[number], Rest>>) => Output1
  ]
) => <Output>(
  self: Type<Input, Output, Rest>
) => Type<Input, Output | Output1, Exclude<Rest, MPredicate.PredicatesToCoverages<R>[number]>>