Maybe
Maybe is an abstraction in F-Box that encapsulates an optional value. It provides methods to handle presence (Just) and absence (Nothing) of values in a type-safe and composable manner.
The Maybe abstraction helps you handle values that might be absent (e.g., null or undefined). By distinguishing between Just (a value is present) and Nothing (no value), Maybe avoids common pitfalls associated with nullish values.
Key Features
- Encapsulation: Wraps a value to provide a controlled interface for interactions.
- Safety: Ensures null-safe handling of optional values.
- Composability: Provides operators and methods for functional transformations.
- Extended Operators: Includes specialized operators like <?>and<|>for handling optional values.
Just and Nothing
The Maybe type has two specific forms:
Just<T>
Represents a Maybe with a present value. It provides methods and operators to manipulate the value safely.
Example
const justValue = Maybe.just(42)console.log(justValue.getValue()) // Outputs: 42Key Characteristics
- Contains a value of type T.
- Allows transformations using methods like map,flatMap, andapply.
Nothing
Represents a Maybe with no value. It provides a consistent interface, but its methods and operators return itself or default values.
Example
const nothingValue = Maybe.nothing()console.log(nothingValue.getValue()) // Outputs: nullKey Characteristics
- Encapsulates absence (null,undefined, orvoid).
- Safely propagates the absence through methods like map,flatMap, andapply.
Creating a Maybe
To create a Maybe, use the static method Maybe.pack. Depending on the input, it will return either a Just or Nothing.
Example
import { Maybe } from "f-box-core"
// Create a Maybe with a present valueconst justValue = Maybe.pack(42)console.log(justValue.getValue()) // Outputs: 42
// Create a Maybe with an absent valueconst nothingValue = Maybe.pack(null)console.log(nothingValue.getValue()) // Outputs: nullThis demonstrates how to:
- Create a Justwhen a value is present.
- Create a Nothingwhen a value is absent.
Supported Operators
The Maybe abstraction provides the following operators for working with its encapsulated value. Each operator is an alias for a corresponding method.
- <$>: Alias for- map. Applies a transformation function to the value.
- <*>: Alias for- apply. Combines a function in a- Maybewith a value in another- Maybe.
- >>=: Alias for- flatMap. Chains computations that return new- Maybeinstances.
- <?>: Alias for- orElse. Returns the current- Maybeif present, or a default- Maybeif absent.
- <|>: Alias for- getOrElse. Returns the encapsulated value if present, or a default value if absent.
For detailed usage, see API Methods.
API Methods
Maybe.pack
Maybe.pack<T>(value: T | None): Maybe<T>
Creates a new Maybe instance based on the given value. Returns a Just for present values or Nothing for nullish values.
const justValue = Maybe.pack(42)console.log(justValue.getValue()) // Outputs: 42
const nothingValue = Maybe.pack(null)console.log(nothingValue.getValue()) // Outputs: nullMaybe.just
Maybe.just<T>(value: T): Just<T>
Creates a Just instance encapsulating the given value.
const justValue = Maybe.just(42)console.log(justValue.getValue()) // Outputs: 42Maybe.nothing
Maybe.nothing(): Nothing
Returns the singleton instance representing a Nothing value.
const nothingValue = Maybe.nothing()console.log(nothingValue.getValue()) // Outputs: nullMaybe.isNone
Maybe.isNone(value: any): boolean
Checks if a given value is None (i.e., null, undefined, or void).
console.log(Maybe.isNone(null)) // Outputs: trueconsole.log(Maybe.isNone(42)) // Outputs: falseMaybe.isMaybe
Maybe.isMaybe(value: any): boolean
Checks if a given value is a Maybe.
const maybeValue = Maybe.pack(42)console.log(Maybe.isMaybe(maybeValue)) // Outputs: trueconsole.log(Maybe.isMaybe(42)) // Outputs: falseMaybe.isNothing
Maybe.isNothing(value: Maybe<T>): boolean
Checks if a given Maybe is a Nothing.
const nothingValue = Maybe.nothing()console.log(Maybe.isNothing(nothingValue)) // Outputs: trueMaybe.isJust
Maybe.isJust(value: Maybe<T>): boolean
Checks if a given Maybe is a Just.
const justValue = Maybe.just(42)console.log(Maybe.isJust(justValue)) // Outputs: truemap
map<U>(fn: (value: T) => U): Maybe<U>
Applies a transformation function to the encapsulated value, returning a new Maybe.
Alias: <$>
const justValue = Maybe.just(10)const transformed = justValue["<$>"]((x) => x * 2)console.log(transformed.getValue()) // Outputs: 20// Method callconst transformed = justValue.map((x) => x * 2)console.log(transformed.getValue()) // Outputs: 20apply
apply<A, B>(this: Maybe<(a: A) => B>, boxValue: Maybe<A>): Maybe<B>
Applies a function wrapped in one Maybe to a value wrapped in another Maybe.
Alias: <*>
const maybeFn = Maybe.just((x: number) => x + 5)const maybeValue = Maybe.just(10)const result = maybeFn["<*>"](maybeValue)console.log(result.getValue()) // Outputs: 15// Method callconst result = maybeFn.apply(maybeValue)console.log(result.getValue()) // Outputs: 15flatMap
flatMap<U>(fn: (value: T) => Maybe<U>): Maybe<U>
Applies a function returning a Maybe to the value inside this Maybe and flattens the result.
Alias: >>=
const justValue = Maybe.just(10)const transformed = justValue[">>="]((x) => Maybe.just(x * 2))console.log(transformed.getValue()) // Outputs: 20// Method callconst transformed = justValue.flatMap((x) => Maybe.just(x * 2))console.log(transformed.getValue()) // Outputs: 20orElse
orElse(defaultValue: Maybe<T>): Maybe<T>
Returns the current Maybe if it is a Just, or the given default Maybe if it is a Nothing.
Alias: <?>
const justValue = Maybe.just(10)const result = justValue["<?>"](Maybe.just(20))console.log(result.getValue()) // Outputs: 10
const nothingValue = Maybe.nothing()const fallback = nothingValue["<?>"](Maybe.just(20))console.log(fallback.getValue()) // Outputs: 20// Method callconst justValue = Maybe.just(10)const result = justValue.orElse(Maybe.just(20))console.log(result.getValue()) // Outputs: 10
const nothingValue = Maybe.nothing()const fallback = nothingValue.orElse(Maybe.just(20))console.log(fallback.getValue()) // Outputs: 20getOrElse
getOrElse(defaultValue: T): T
Returns the value inside the Maybe if present, or the provided default value if it is a Nothing.
Alias: <|>
const justValue = Maybe.just(10)const result = justValue["<|>"](20)console.log(result.getValue()) // Outputs: 10
const nothingValue = Maybe.nothing()const fallback = nothingValue["<|>"](20)console.log(fallback.getValue()) // Outputs: 20// Method callconst justValue = Maybe.just(10)const result = justValue.getOrElse(20)console.log(result.getValue()) // Outputs: 10
const nothingValue = Maybe.nothing()const fallback = nothingValue.getOrElse(20)console.log(fallback.getValue()) // Outputs: 20match
match<U>(onJust: (value: T) => U, onNothing: () => U): U
Matches the current Maybe instance against the Just or Nothing cases and applies the corresponding function.
const justValue = Maybe.just(42)const result = justValue.match(  (value) => `Value is ${value}`,  () => "No value")console.log(result) // Outputs: Value is 42
const nothingValue = Maybe.nothing()const fallback = nothingValue.match(  (value) => `Value is ${value}`,  () => "No value")console.log(fallback) // Outputs: No valueJust and Nothing Types
Maybe is a discriminated union type composed of two specific variants: Just and Nothing. These types are used to encapsulate values or represent the absence of a value in a functional and type-safe way.
Just
A Just represents a Maybe instance that encapsulates a present, non-null value. It provides methods for mapping, chaining, and safely accessing the encapsulated value.
Nothing
A Nothing represents a Maybe instance that encapsulates the absence of a value. All operations on a Nothing result in a Nothing, ensuring safe handling of optional values.
const justValue = Maybe.just(42)console.log(justValue.getValue()) // Outputs: 42
const nothingValue = Maybe.nothing()console.log(nothingValue.getOrElse(0)) // Outputs: 0Use Cases
Transforming Optional Values
Apply a series of transformations to encapsulated values:
const justValue = Maybe.pack(100)  ["<$>"]((x) => x / 2)  ["<$>"]((x) => x + 10)
console.log(justValue.getValue()) // Outputs: 60Fallback for Missing Values
Provide a default value when a value is missing:
const nothingValue = Maybe.nothing()const fallback = nothingValue["<?>"](Maybe.just(50))
console.log(fallback.getValue()) // Outputs: 50Combining Optional Values
Combine multiple Maybe instances using the <*> operator:
const result = Maybe.just((a: number) => (b: number) => a + b)  ["<*>"](Maybe.just(10))  ["<*>"](Maybe.just(20))
console.log(result.getValue()) // Outputs: 30Matching on Presence and Absence
Handle Just and Nothing cases explicitly with match:
const justValue = Maybe.just(42)const result = justValue.match(  (value) => `Value is ${value}`,  () => "No value")console.log(result) // Outputs: Value is 42
const nothingValue = Maybe.nothing()const fallback = nothingValue.match(  (value) => `Value is ${value}`,  () => "No value")console.log(fallback) // Outputs: No valueWhy Use Maybe?
The Maybe abstraction provides a consistent and type-safe way to handle optional values, avoiding pitfalls like null dereferencing. Its functional interface and composable methods make it ideal for working with uncertain data.
Next Steps
Explore other abstractions built on Maybe: