Complete overview of data structures and interfaces
Explore which functional programming interfaces are supported by each data structure in functype
This matrix shows which interfaces are supported by each data structure in the functype library.
All base typeclasses (Traversable, Extractable, Functor, Applicative, Monad, AsyncMonad, ContainerOps, CollectionOps, Foldable, Serializable, Pipe, Matchable, Promisable, Doable, Reshapeable) declared <out T> / <out A>.
Traversable.reduce / reduceRight are guarded by Widen<A, B> (src/typeclass/variance.ts), TypeScript’s equivalent of Scala’s [B >: A] lower-bound constraint. See variance-guide.md for the full contributor reference.
Collections (List, Set, LazyList, Stack, Map, Obj) keep Traversable. Sum types (Either, Try) extend the lighter FunctypeSum base with no collection-style methods.
All types follow the Companion pattern (inspired by Scala), combining constructor functions with static utility methods. Each type provides:
Collection creation - multiple options available:
// List creation
List([1, 2, 3]) // from array
List.of(1, 2, 3) // variadic factory
List.empty<number>() // typed empty list
// Set creation
Set([1, 2, 3]) // from array
Set.of(1, 2, 3) // variadic factory
Set.empty<number>() // typed empty set
// Map creation
Map([
["a", 1],
["b", 2],
]) // from key-value pairs
Map.of<string, number>(["a", 1], ["b", 2]) // variadic factory
Map.empty<string, number>() // typed empty map
// Http — fetch wrapper returning IO<never, HttpError, HttpResponse<unknown>> by default
// Provide a validate function to get typed responses (BYOV: bring your own validator)
Http.get(url, opts?) // GET → HttpResponse<unknown>
Http.get(url, { validate }) // GET → HttpResponse<T> (T inferred from validate)
Http.post(url, opts?) // POST with auto JSON body serialization
Http.put(url, opts?) // PUT
Http.patch(url, opts?) // PATCH
Http.delete(url, opts?) // DELETE
Http.request(fullOpts) // Full control
Http.client(config) // Create configured client with baseUrl, defaultHeaders, custom fetch, beforeRequest (effectful request transformer)
// Example with validator (works with Zod, TypeBox, Valibot, or manual validators)
Http.get("/api/users", { validate: (data) => z.array(UserSchema).parse(data) })
Http.post("/api/users", { body: { name: "Alice" }, validate: (data) => UserSchema.parse(data) })
// HttpError — three-variant ADT
HttpError.networkError(url, method, cause)
HttpError.httpStatusError(url, method, status, statusText, body)
HttpError.decodeError(url, method, body, cause)
HttpError.match(error, { NetworkError, HttpStatusError, DecodeError })
HttpError.isNetworkError(e) / .isHttpStatusError(e) / .isDecodeError(e)
Error-accumulating validation — functype’s “Validated” applicative role. Validation
collects all field errors rather than short-circuiting on the first (unlike
Either.sequence, which fails fast). Not a typeclass-grid container, so it lives
here rather than in the interface matrix above (same as HttpError / DecoderError).
// FormValidation<T> — the accumulating result type
type FormValidation<T> = Either<List<TypedError<"VALIDATION_FAILED">>, T>
// TypedError — code-tagged structured error (extends Throwable)
TypedError.validation(field, value, rule) // → TypedError<"VALIDATION_FAILED">, context { field, value, rule }
TypedError.isTypedError(v) / TypedError.hasCode(e, code)
// Validation — rule DSL + form validator
Validation.rule<T>("min:18" | "email" | "required" | "pattern:..." | "in:a,b" | ...)
Validation.combine(...validators) // all must pass (fail-fast per field)
Validation.custom<T>(predicate, message) // custom per-field rule
Validation.validators.email / .url / .required / .positiveNumber / .nonEmptyString
Validation.form(schema, data) // → FormValidation<T>, accumulates every field error
// Validator<T> = (value: unknown) => Either<TypedError<"VALIDATION_FAILED">, T>
// form() is flat/per-field. For cross-field (lo < hi) or dynamic-key (weights.*) checks,
// hand-accumulate TypedError.validation(...) into a List and return the same FormValidation shape.
Static type guards for narrowing types:
| Data Structure | Type Guard Methods |
|---|---|
| Option |
isSome(option), isNone(option) |
| Either<L,R> | isLeft(either), isRight(either) |
| Try |
isSuccess(tryValue), isFailure(tryValue) |
All Serializable types provide static deserialization methods:
fromJSON(json: string): T - Deserialize from JSONfromYAML(yaml: string): T - Deserialize from YAMLfromBinary(binary: string): T - Deserialize from base64-encoded binaryNote: See docs/companion-pattern.md for complete guide on the Companion pattern.
map<B>(f: (value: A) => B): Functor<B>ap<B>(ff: Applicative<(value: A) => B>): Applicative<B>flatMap<B>(f: (value: A) => Monad<B>): Monad<B>flatMapAsync<B>(f: (value: A) => PromiseLike<AsyncMonad<B>>): PromiseLike<AsyncMonad<B>>foldLeft<B>(z: B): (op: (b: B, a: A) => B) => BfoldRight<B>(z: B): (op: (a: A, b: B) => B) => BNote: fold has different semantics per type category:
fold<B>(onEmpty: () => B, onValue: (value: A) => B): B — pattern matchfold<B>(initial: B, fn: (acc: B, a: A) => B): B — left-reduce accumulatorfoldAsync (Option, Either, Try) — same shape as fold but accepts sync or async handlers and always returns Promise<B>. Use when at least one branch performs async work to avoid T | Promise<T> unions.
match<R>(patterns: Record<Tags, (value: A) => R>): Rserialize(): SerializationMethods<T>
toJSON(): stringtoYAML(): stringtoBinary(): Uint8Arraysize: numberisEmpty: booleancontains(value: A): booleanreduce<B>(f: (acc: B, value: A) => B, initial: B): BreduceRight<B>(f: (value: A, acc: B) => B, initial: B): BorThrow(error?: Error): TorElse(defaultValue: T): Tor(alternative: Extractable<T>): Extractable<T>orNull(): T | nullorUndefined(): T | undefinedpipe<U>(f: (value: T) => U): UtoList(): List<A>toSet(): Set<A>toString(): stringcount(p: (value: A) => boolean): numberfind(p: (value: A) => boolean): A | undefinedexists(p: (value: A) => boolean): booleanforEach(f: (value: A) => void): voiddrop(n: number): SelfdropRight(n: number): SelfdropWhile(p: (value: A) => boolean): Selfflatten(): Selfhead: A | undefinedheadOption: Option<A>take(n: number): SelftakeWhile(p: (value: A) => boolean): SelftakeRight(n: number): Selflast: A | undefinedlastOption: Option<A>tail: Selfinit: SelftoArray(): A[]reverse(): List<A>indexOf(value: A): numberprepend(item: A): List<A>distinct(): List<A>sorted(compareFn?): List<A>sortBy(f, compareFn?): List<A>zip(other: List<B>): List<[A, B]>zipWithIndex(): List<[A, number]>groupBy(f: (a: A) => K): Map<K, List<A>>partition(p): [List<A>, List<A>]span(p): [List<A>, List<A>]slice(start, end): List<A>Enables Scala-like for-comprehensions using JavaScript generators:
Do(function* () { ... }): Synchronous monadic comprehensionsDoAsync(async function* () { ... }): Async monadic comprehensions$(monad): Helper for type inference with yield*Provides type conversion between monadic types:
toOption(): Option<T>toEither<E>(leftValue: E): Either<E, T>toList(): List<T>toTry(): Try<T>Provides conversion to Promise for async interop:
toPromise(): Promise<T>Functype<A, Tag>: Implemented by single-value containers (Option, Try, Lazy). Provides full functional programming support.
FunctypeCollection<A, Tag>: Implemented by collection containers (List, Set). Extends FunctypeBase with collection-specific operations.
Special Cases:
Do-notation: Provides generator-based monadic comprehensions similar to Scala’s for-comprehensions. Supports Option, Either, Try, and List with automatic short-circuiting and cartesian products.
Reshapeable: Enables conversion between different monad types, allowing flexible composition in Do-notation when mixing types.
Promisable: Provides conversion to Promise for async interoperability. Supported by Option, Either, Try, and TaskOutcome.
Utility Types (not in matrix):
IO<R,E,A>: Lazy, composable effect type with typed errors and dependency injection.
IO.gen) and builder do-notation (IO.Do)Exit<E, A> = Success | Failure | Die | Interrupted, returned by runExit().
Failure carries a value from the declared E channel; Die carries a defect —
a value that is not an E (a throwing IO.sync thunk, a throwing map/flatMap/mapError
callback, or IO.die). run() returns Either, which has no branch for either a defect
or an interruption, so both arrive in the Left; runExit() is what keeps them apart.
Defects stay recoverable — recover/recoverWith/fold/mapError treat Die as Failure.
fold’s onDie and match’s Die are optional and fall back to the failure handler.