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: 42
Key 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: null
Key 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: null
This demonstrates how to:
- Create a
Just
when a value is present. - Create a
Nothing
when 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 formap
. Applies a transformation function to the value.<*>
: Alias forapply
. Combines a function in aMaybe
with a value in anotherMaybe
.>>=
: Alias forflatMap
. Chains computations that return newMaybe
instances.<?>
: Alias fororElse
. Returns the currentMaybe
if present, or a defaultMaybe
if absent.<|>
: Alias forgetOrElse
. 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: null
Maybe.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: 42
Maybe.nothing
Maybe.nothing(): Nothing
Returns the singleton instance representing a Nothing
value.
const nothingValue = Maybe.nothing()console.log(nothingValue.getValue()) // Outputs: null
Maybe.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: false
Maybe.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: false
Maybe.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: true
Maybe.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: true
map
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: 20
apply
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: 15
flatMap
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: 20
orElse
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: 20
getOrElse
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: 20
match
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 value
Just 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: 0
Use 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: 60
Fallback 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: 50
Combining 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: 30
Matching 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 value
Why 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
: