# Filters

A plain predicate answers one question — does this value pass? — and throws
away everything else. When it returns `false` you are left holding the original
value with no record of *why* it was rejected, and TypeScript has not learned
anything new about the values that did pass.

The `Filter` module replaces that boolean with data. A `Filter<Input, Pass, Fail>`
is a function `(input: Input) => Result<Pass, Fail>`: success means the value
passed (and may have been **narrowed** or **transformed** along the way),
failure means it was filtered out — and the rejected value is carried in the
`Result` failure channel as ordinary data, not an exception.

```ts
import { Filter, Predicate, Result } from "effect"

// A plain predicate: returns a boolean, the rejected value vanishes
const isPositivePredicate = (n: number) => n > 0
isPositivePredicate(5) // => true
isPositivePredicate(-3) // => false  (where did -3 go?)

// The same rule as a Filter: the rejected value survives as data
const isPositive = Filter.fromPredicate((n: number) => n > 0)
isPositive(5) // => Result.succeed(5)
isPositive(-3) // => Result.fail(-3)   the rejected value is preserved
```

Because the rejected value is a `Result.fail` rather than a thrown error,
filters **compose** with combinators (`or`, `zip`, `compose`, ...) the same way
`Result` and `Option` do, instead of forcing you into `try`/`catch`.

A refinement predicate makes the win clearer — the passing value is narrowed:

```ts
import { Filter, Result } from "effect"

// Refinement: narrows unknown -> string on the pass side
const onlyStrings = Filter.fromPredicate(
  (x: unknown): x is string => typeof x === "string"
)

onlyStrings("hello") // => Result.succeed("hello")  typed as Result<string, unknown>
onlyStrings(42) // => Result.fail(42)
```
**Filters vs Match:** [`Match`](https://effect.plants.sh/pattern-matching/match/) dispatches one value across many branches
and produces a single answer. A `Filter` is a smaller, reusable unit: a single
accept/reject/transform step that you compose with other filters. Many APIs in
Effect (queues, streams, error handlers) take a `Filter` directly so they can
keep the rejected values.

## The effectful form

When the decision needs async work, can error, or depends on services, use
`FilterEffect` — the function returns an `Effect<Result<Pass, Fail>, E, R>`
instead of a bare `Result`.

```ts
import { Effect, Filter, Result } from "effect"

const validateId: Filter.FilterEffect<string, string, string> = (id) =>
  Effect.gen(function* () {
    const ok = yield* Effect.succeed(id.length > 0)
    return ok ? Result.succeed(id) : Result.fail(id)
  })
```

---

# Reference

## Models

### Filter

`Filter<Input, Pass = Input, Fail = Input>` is a function `(input) => Result<Pass, Fail>`.
Success carries the (possibly narrowed/transformed) passing value; failure
carries the rejected value.

```ts
import { Filter, Result } from "effect"

const positive: Filter.Filter<number> = (n) =>
  n > 0 ? Result.succeed(n) : Result.fail(n)

positive(5) // => Result.succeed(5)
positive(-3) // => Result.fail(-3)
```

### FilterEffect

`FilterEffect<Input, Pass, Fail, E = never, R = never>` is the effectful
counterpart: `(input) => Effect<Result<Pass, Fail>, E, R>`. Use it when the
filter performs effects, can fail with `E`, or needs services `R`.

```ts
import { Effect, Filter, Result } from "effect"

const nonEmpty: Filter.FilterEffect<string, string, string> = (s) =>
  Effect.sync(() => (s.length > 0 ? Result.succeed(s) : Result.fail(s)))
```

## Constructors

### make

Builds a `Filter` from a function returning `Result.succeed(pass)` /
`Result.fail(fail)`. The primary way to write a custom filter, including ones
that transform the passing value.

```ts
import { Filter, Result } from "effect"

const upperIfNonEmpty = Filter.make((s: string) =>
  s.length > 0 ? Result.succeed(s.toUpperCase()) : Result.fail(s)
)

upperIfNonEmpty("hi") // => Result.succeed("HI")
upperIfNonEmpty("") // => Result.fail("")
```

### makeEffect

Builds a `FilterEffect` from a function returning an `Effect<Result<...>>`. Use
for async checks, error handling, or service access.

```ts
import { Effect, Filter, Result } from "effect"

const validate = Filter.makeEffect((id: string) =>
  Effect.gen(function* () {
    const ok = yield* Effect.succeed(id.length > 0)
    return ok ? Result.succeed(id) : Result.fail(id)
  })
)

Effect.runSync(validate("abc")) // => Result.succeed("abc")
Effect.runSync(validate("")) // => Result.fail("")
```

### fromPredicate

Turns a predicate into a filter that passes the input unchanged on `true` and
fails with the input on `false`. When given a **refinement** (`x is B`), the
pass side is narrowed to `B`.

```ts
import { Filter } from "effect"

// Plain predicate: Filter<number>
const positive = Filter.fromPredicate((n: number) => n > 0)
positive(5) // => Result.succeed(5)
positive(-1) // => Result.fail(-1)

// Refinement: narrows the pass type to string
const str = Filter.fromPredicate((x: unknown): x is string => typeof x === "string")
str("ok") // => Result.succeed("ok")  pass typed as string
str(1) // => Result.fail(1)
```

### fromPredicateOption

Builds a filter from a function returning an `Option`. `Some(value)` passes with
`value` (which can be a transformed result); `None` fails with the **original
input**.

```ts
import { Filter, Option } from "effect"

const parsePort = Filter.fromPredicateOption((s: string) => {
  const n = Number(s)
  return Number.isInteger(n) && n > 0 && n < 65536 ? Option.some(n) : Option.none()
})

parsePort("8080") // => Result.succeed(8080)
parsePort("nope") // => Result.fail("nope")  fails with the original string
```

### try

Builds a filter that runs a function and passes its return value, failing with
the **original input** if the function throws.

```ts
import { Filter } from "effect"

const parseJson = Filter.try((s: string) => JSON.parse(s) as unknown)

parseJson('{"a":1}') // => Result.succeed({ a: 1 })
parseJson("{bad}") // => Result.fail("{bad}")  the original string, not the thrown error
```

## Narrowing filters (unknown → T)

Predefined filters that accept an `unknown` and narrow it to a primitive type on
the pass side, failing with the original input otherwise.

### string

Passes string values.

```ts
import { Filter } from "effect"

Filter.string("hello") // => Result.succeed("hello")
Filter.string(42) // => Result.fail(42)
```

### number

Passes number values.

```ts
import { Filter } from "effect"

Filter.number(42) // => Result.succeed(42)
Filter.number("42") // => Result.fail("42")
```

### boolean

Passes boolean values (`true` and `false`).

```ts
import { Filter } from "effect"

Filter.boolean(false) // => Result.succeed(false)
Filter.boolean(0) // => Result.fail(0)
```

### bigint

Passes `bigint` primitives. Does not coerce: `1n` passes, `1` fails.

```ts
import { Filter } from "effect"

Filter.bigint(1n) // => Result.succeed(1n)
Filter.bigint(1) // => Result.fail(1)
```

### symbol

Passes symbol values.

```ts
import { Filter } from "effect"

const sym = Symbol("id")
Filter.symbol(sym) // => Result.succeed(sym)
Filter.symbol("id") // => Result.fail("id")
```

### date

Passes `Date` instances. Uses `instanceof Date`, so an *invalid* `Date` still
passes — the timestamp is not validated.

```ts
import { Filter } from "effect"

const d = new Date("2026-05-30")
Filter.date(d) // => Result.succeed(d)
Filter.date("2026-05-30") // => Result.fail("2026-05-30")
```

## Shape & variant matching

### instanceOf

Passes inputs that are `instanceof` the given constructor, narrowing the pass
side to that instance type.

```ts
import { Filter } from "effect"

const isError = Filter.instanceOf(Error)

const e = new TypeError("boom")
isError(e) // => Result.succeed(e)  narrowed to Error
isError("boom") // => Result.fail("boom")
```

### has

Passes inputs whose own `has(key)` method returns `true` for the given key.
Designed for `Set`, `Map`, and similar containers.

```ts
import { Filter } from "effect"

const hasUserId = Filter.has("user-1")
const present = new Set(["user-1", "user-2"])
const absent = new Set(["user-3"])

hasUserId(present) // => Result.succeed(present)
hasUserId(absent) // => Result.fail(absent)
```

### tagged

Passes only the matching member of a `_tag`-discriminated union, narrowing the
pass side to that variant (`ExtractTag`) and the fail side to the rest
(`ExcludeTag`). Only `_tag` is checked, not the other fields. Use the curried
`tagged<Input>()(tag)` form when you want to fix the input union up front.

```ts
import { Filter } from "effect"

type Event =
  | { readonly _tag: "Click"; readonly x: number }
  | { readonly _tag: "Key"; readonly code: string }

// Fix the input type, then pick a tag — pass narrows to the Click variant
const onClick = Filter.tagged<Event>()("Click")

onClick({ _tag: "Click", x: 10 }) // => Result.succeed({ _tag: "Click", x: 10 })
onClick({ _tag: "Key", code: "Enter" }) // => Result.fail({ _tag: "Key", code: "Enter" })
```

### reason

Extracts a nested `reason` variant from a tagged error: passes when the input's
`_tag` matches `tag` **and** its `reason._tag` matches `reasonTag`, succeeding
with the inner `reason`. Otherwise fails with the original input. See
[Reason errors](https://effect.plants.sh/error-management/reason-errors/).

```ts
import { Filter } from "effect"

type AppError = {
  readonly _tag: "AppError"
  readonly reason:
    | { readonly _tag: "NotFound"; readonly id: string }
    | { readonly _tag: "Forbidden" }
}

const notFound = Filter.reason<AppError>()("AppError", "NotFound")

notFound({ _tag: "AppError", reason: { _tag: "NotFound", id: "u1" } })
// => Result.succeed({ _tag: "NotFound", id: "u1" })

notFound({ _tag: "AppError", reason: { _tag: "Forbidden" } })
// => Result.fail({ _tag: "AppError", reason: { _tag: "Forbidden" } })
```

## Value matching

### equals

Passes inputs **structurally** equal to the expected value, via
[`Equal.equals`](https://effect.plants.sh/traits/equal-and-hash/). Distinct objects with equal contents
pass.

```ts
import { Filter } from "effect"

const isOrigin = Filter.equals({ x: 0, y: 0 })

isOrigin({ x: 0, y: 0 }) // => Result.succeed({ x: 0, y: 0 })  different reference, equal contents
isOrigin({ x: 1, y: 0 }) // => Result.fail({ x: 1, y: 0 })
```

### equalsStrict

Passes inputs equal to the expected value via JavaScript `===`. Objects pass
only when they are the *same reference*, and `NaN` never passes.

```ts
import { Filter } from "effect"

const target = { x: 0, y: 0 }
const isTarget = Filter.equalsStrict(target)

isTarget(target) // => Result.succeed(target)  same reference
isTarget({ x: 0, y: 0 }) // => Result.fail({ x: 0, y: 0 })  equal contents, different reference
```
**equals vs equalsStrict:** `equals` compares by value (`Equal.equals`); `equalsStrict` compares by
reference/`===`. The structural `equals` is what you usually want for plain
records, tuples, and data classes; reach for `equalsStrict` only when identity
matters.

## Combinators — OR

### or

Tries `self`, then `that`; the **first success wins**. If `self` passes, its
result is returned; otherwise `that` is run on the same input.

```ts
import { Filter } from "effect"

// Accept either a string or a number
const stringOrNumber = Filter.or(Filter.string, Filter.number)

stringOrNumber("hi") // => Result.succeed("hi")
stringOrNumber(42) // => Result.succeed(42)
stringOrNumber(true) // => Result.fail(true)
```

## Combinators — AND

These run **both** filters on the same input; if either fails, the combination
fails with that failure.

### zip

Both filters must pass; the passes are combined into a **tuple** `[PassL, PassR]`.

```ts
import { Filter } from "effect"

const positive = Filter.fromPredicate((n: number) => n > 0)
const even = Filter.fromPredicate((n: number) => n % 2 === 0)

const positiveEvenPair = Filter.zip(positive, even)

positiveEvenPair(4) // => Result.succeed([4, 4])
positiveEvenPair(3) // => Result.fail(3)
```

### zipWith

Like `zip`, but combines the two passes with a function `f` instead of building
a tuple.

```ts
import { Filter } from "effect"

const str = Filter.string
const positive = Filter.fromPredicate((n: number) => n > 0)

// Both filters run on the same input; here a label string passes both as-is
const labelled = Filter.zipWith(str, str, (a, b) => `${a}/${b}`)

labelled("x") // => Result.succeed("x/x")
labelled(1) // => Result.fail(1)
```

### andLeft

Both filters must pass; keeps the **left** filter's pass value.

```ts
import { Filter } from "effect"

const positive = Filter.fromPredicate((n: number) => n > 0)
const even = Filter.fromPredicate((n: number) => n % 2 === 0)

const positiveAndEven = Filter.andLeft(positive, even)

positiveAndEven(4) // => Result.succeed(4)  (left pass kept)
positiveAndEven(3) // => Result.fail(3)
```

### andRight

Both filters must pass; keeps the **right** filter's pass value (useful when the
right filter transforms).

```ts
import { Filter, Result } from "effect"

const positive = Filter.fromPredicate((n: number) => n > 0)
const doubled = Filter.make((n: number) =>
  n > 0 ? Result.succeed(n * 2) : Result.fail(n)
)

const positiveDoubled = Filter.andRight(positive, doubled)

positiveDoubled(5) // => Result.succeed(10)  (right pass kept)
positiveDoubled(-1) // => Result.fail(-1)
```

## Sequencing

### compose

Runs `self`, then feeds its **pass value** into `that`. The intermediate failure
value is preserved: if `self` fails you get `self`'s failure, if `that` fails you
get `that`'s failure.

```ts
import { Filter, Result } from "effect"

const nonEmptyUpper = Filter.make((s: string) =>
  s.length > 0 ? Result.succeed(s.toUpperCase()) : Result.fail(s)
)

// unknown -> string -> uppercase
const toUpper = Filter.compose(Filter.string, nonEmptyUpper)

toUpper("hi") // => Result.succeed("HI")
toUpper("") // => Result.fail("")  failure from the second filter (the intermediate "" string)
toUpper(42) // => Result.fail(42)  failure from the first filter
```

### composePassthrough

Like `compose`, but on any failure it fails with the **original input** rather
than the intermediate failure value.

```ts
import { Filter, Result } from "effect"

const nonEmptyUpper = Filter.make((s: string) =>
  s.length > 0 ? Result.succeed(s.toUpperCase()) : Result.fail(s)
)

const toUpper = Filter.composePassthrough(Filter.string, nonEmptyUpper)

toUpper("hi") // => Result.succeed("HI")
toUpper(42) // => Result.fail(42)  original input, same as compose here
// Difference: a value that passes the first filter but fails the second
toUpper("") // => Result.fail("")  the ORIGINAL input "", not an intermediate value
```
**compose vs composePassthrough:** Both run the two filters in sequence. The difference is what shows up in the
failure channel: `compose` preserves whichever filter's failure value triggered
the rejection, while `composePassthrough` always reports the *original input*.
Pick `composePassthrough` when callers should always see the value they handed
in, regardless of which stage rejected it.

## Mapping

### mapFail

Transforms the **failure** value only, leaving passes untouched. Dual, so it
works data-first or in `pipe`.

```ts
import { Filter } from "effect"

const positive = Filter.fromPredicate((n: number) => n > 0)

const withMessage = Filter.mapFail(positive, (n) => `rejected: ${n}`)

withMessage(5) // => Result.succeed(5)
withMessage(-1) // => Result.fail("rejected: -1")
```

## Conversions

Adapt a `Filter` back into the plain shapes other APIs expect.

### toPredicate

Returns `(a) => boolean` — `true` when the filter passes, dropping both
payloads.

```ts
import { Filter } from "effect"

const isPositive = Filter.toPredicate(Filter.fromPredicate((n: number) => n > 0))

isPositive(5) // => true
isPositive(-1) // => false
;[1, -2, 3].filter(isPositive) // => [1, 3]
```

### toOption

Returns `(a) => Option<Pass>` — `Some(pass)` when the filter passes, `None` when
it fails (the failure value is discarded).

```ts
import { Filter } from "effect"

const asOption = Filter.toOption(Filter.string)

asOption("hi") // => Option.some("hi")
asOption(42) // => Option.none()
```

### toResult

Returns `(a) => Result<Pass, Fail>` — preserves both the pass and the failure
value. Effectively the identity view of the filter as a plain `Result`-returning
function.

```ts
import { Filter } from "effect"

const asResult = Filter.toResult(Filter.number)

asResult(42) // => Result.succeed(42)
asResult("x") // => Result.fail("x")
```
**Gotchas to remember:** - A failed filter is **data** in the `Result` failure channel, not a thrown exception.
- `compose` preserves the intermediate failure value; `composePassthrough` fails with the **original input**.
- `equals` is structural (`Equal.equals`); `equalsStrict` is `===` (reference for objects, and `NaN` never passes).
- `fromPredicateOption` (and `try`) fail with the **original input** when the `Option` is `None` (or the function throws).
- Prefer **refinement** predicates with `fromPredicate` when you want TypeScript to narrow the passing value's type.