PHPackages                             ascetik/hypothetik - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. ascetik/hypothetik

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

ascetik/hypothetik
==================

Home-made Maybe monad

v0.3.0(2y ago)0302MITPHP

Since Jan 11Pushed 2y ago1 watchersCompare

[ Source](https://github.com/ascetik/hypothetik)[ Packagist](https://packagist.org/packages/ascetik/hypothetik)[ RSS](/packages/ascetik-hypothetik/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (2)Versions (4)Used By (2)

Hypothetik
==========

[](#hypothetik)

\[EN\]

Home made OOP "Monad", for an easier management of hypothetical values.

Release notes
-------------

[](#release-notes)

> Version 0.3.0 : still a draft version.

Php version : 8.2.14

- New **Hypothetik** interface, describing the behavior of a monad included in this package.
- **Maybe** class implements **Hypothetik** interface
- New **When**, **Hypothetik** implementation to handle booleans.

Descriptions
------------

[](#descriptions)

### Interfaces

[](#interfaces)

**OptionnalValue** is a general interface shared by both **Hypothetik** an **Option** instances.

- **OptionnalValue**::isValid(): *bool* : check validity of a value (!null &amp; !false)
- **OptionnalValue**::value(): *mixed* : return **Option** raw value

---

The **Hypothetik** interface describes the way to handle a value which may be null or false using callables.

- **Hypothetik**::apply(*callable*, *...mixed*): *mixed* : return the result of given callable using the **Option** value
- **Hypothetik**::either(*callable*, *...mixed*): *Either* : return an **Either** instance according to an **Option**.
- **Hypothetik**::then(*callable*, *...mixed*): *Hypothetik* : return a new **Hypothetic** instance with the result of given callable.
- **Hypothetik**::otherwise(*mixed*): *Hypothetik* : choose an alternative to return if the **Option** value is invalid.

---

The **Option** interface describes the behavior of an instance containing the exepcted value :

- **Option**::apply(*callable*, *?array*): *mixed* : return the result of given function with **Option** value as first parameter
- **Option**::equals(*Option*): bool : Check equality with another **Option**
- **Option**::isValid(): *bool* : see **OptionnalValue** interface
- **Option**::value(): mixed : see **OptionnalValue** interface

This package includes 2 **Option** implementations : *final* class **None** and *final* class **Some**. Anyone can build another implementation of **Option** to replace **Some** class. That's why this interface is exposed here.

> An **Option** is a simple ValueObject with simple behaviors, unuseful outside of an **Hypothetik** instance.

### Available Implementations

[](#available-implementations)

*final* class **Maybe** : The **Maybe** class is the main tool of this package. It handles an **Option** which may contain a value, or may not, and drives different operations on this value, or not...

- **Maybe**::equals(*Maybe*): *bool* : check equality with another **Maybe** instance.
- **Maybe**::apply(*callable*, *...mixed*): *mixed* : see **Hypothetik** interface
- **Maybe**::either(*callable*): *Either* : see **Hypothetik** interface
- **Maybe**::isValid(): *bool* : see **OptionnalValue** interface
- **Maybe**::otherwise(*mixed*): *Hypothetik* : see **Hypothetik** interface
- **Maybe**::then(*callable*, *...mixed*): *Hypothetik* : see **Hypothetik** interface
- **Maybe**::value(): *mixed* : see **OptionnalValue** interface
- static **Maybe**::not(): *Maybe* : return a **Maybe** instance with a **None** option
- static **Maybe**::of(*Option*): *Maybe* : return a **Maybe** instance with given **Option** instance.
- static **Maybe**::some(*mixed*): *Hypothetik* : return a **Hypothetik** instance with given value

> **Maybe** contructor is private. See examples below for instanciation.

---

*final* class **When** : (v.0.3.0) This implementation works almost like **Maybe**. The difference is that **When** contains an **Option** with a bool value and a falsy **Option** is considered as invalid.

- **When**::apply(*callable*, *...mixed*): *mixed* : see **Hypothetik** interface
- **When**::either(*callable*): *Either* : see **Hypothetik** interface
- **When**::isValid(): *bool* : see **OptionnalValue** interface
- **When**::otherwise(*mixed*): *Hypothetik* : see **Hypothetik** interface
- **When**::then(*callable*, *...mixed*): *Hypothetik* : see **Hypothetik** interface
- **When**::value(): *mixed* : see **OptionnalValue** interface
- static **When**::ever(*bool*): *When* : return a **Maybe** instance with given value

> Private constructor. Use **When**::ever(*bool*) or **Maybe**::some(*bool*) methods to build an instance.

---

*final* class **Either** :

The **Either** class handles a function to execute according to a **Maybe** Option value.

- **Either**::or(*callable*, *...mixed*): Either : return an new **Either** instance if **maybe**'s value is null.
- **Either**::try(): *Maybe* : return a new **Maybe** instance with the result of current **Either** function.
- **Either**::value(): *mixed* : retourne la valeur contenue par le Maybe
- **Either**::static use(*Maybe*, *callable*, *...mixed*): *Either* : récupération d'une instance de **Either**, constructeur privé

> An **Either** instance is exposed by a **Hypothetik** implementation for usage, unuseful in any other context.

---

*final* class **None** is a "null value" **Option**. *final* class **Some** is a not null value **Option**.

Usage
-----

[](#usage)

### Construction

[](#construction)

As **Maybe** constructor access is not available, 3 factory methods are provided :

```
$not = Maybe::not(); // Maybe

$some = Maybe::some('my value'); // Maybe
$someobj = Maybe::some(new MyOwnInstance()); // Maybe
$nullAnyway = Maybe::some(null); // Maybe

$any = Maybe::of(new MyOwnStringOption('any string value')); // Maybe
$anyobj = Maybe::of(new MyOwnOption(new MyOwnInstance())); // Maybe
$anyNullObj = Maybe::of(new MyOwnNullOption()); // Maybe

// version 0.3.0
$truthy = Maybe::some(true); // this is a truthy "When" instance
$falsy = Maybe::some(false); // this is a falsy "When" instance
```

### Valid value : mixed value not null

[](#valid-value--mixed-value-not-null)

To retrieve raw optionnal value from the "$some" **Maybe** instance of previous example :

```
echo $some->value(); // "my value"
```

Pass an optionnal value through a function an get the result :

```
echo $some->apply(strtoupper(...)); // "MY VALUE"
```

The **Option** value is always passed to the function as first parameter.

It is possible to add arguments, separated by comas. The order of the arguments is important.

Another example with some added arguments :

```
$pathToAboutPage = Maybe::some('/about');
echo $pathToAboutPage->apply(trim(...), '/'); // "about", without forward slash

$function = fn(string $value, string $separator, string $add)=> trim($value, $separator) . '-' . $add
echo $pathToAboutPage->apply($function, '/','page' ); //"about-page"
```

It is possible to get a new **Hypothetik** instance containing the result of a function. Once again, the **Option** value is always passed to the function as first parameter and arguments can be added :

```
$maybeThen = $some->then(strtoupper(...)); // retourne un nouveau Maybe contenant "MY VALUE"
echo $maybeThen->value(); // affiche "MY VALUE"
```

As a new **Maybe** instance is returned, we can chain calls of this method :

```
echo $some->then(strtoupper(...)) // return a new Maybe containing "MY VALUE"
    ->then(fn(string $value) => $value.' is not null')
    ->value(); // "MY VALUE is not null"
```

### Invalid value : null value

[](#invalid-value--null-value)

With a null value, things are slightly different. Both *apply()* and *value()* methods will return null again. The *then()* method returns a Maybe with a null Option value.

Take a look at the "$not" instance from the first example :

```
echo $not->value(); // prints nothing because null
echo $not->apply(strtoupper(...)); // null too, function is not applied
echo $not->then(strtoupper(...))->value(); // still null
```

**Maybe** provides a way to substitute an "invalid" instance to a valid one by using *otherwise* method :

```
$otherwise = $not->otherwise('nothing');
echo $otherwise->value(); // prints "nothing"
echo $otherwise->apply(strtoupper(...)); // prints "NOTHING"
echo $otherwise->then(strtoupper(...)) // // Maybe
               ->value(); // "NOTHING" again.
```

Some other examples chaining methods :

```
echo $not->then(strtoupper(...)) // run strtoupper with a Maybe won't work
    ->otherwise('i replace null') // new Maybe available after first then() call
    ->then(fn(string $value) => $value . ' for demonstration') // run the function with the new instance
    ->value(); // prints "i replace null for demonstration"

echo $not->otherwise('i replace null') // new Maybe available
    ->then(strtoupper(...)) // now transform initial string to upper case
    ->then(fn(string $value) => $value . ' for demonstration') // and append another string to the previous value
    ->value(); // prints "I REPLACE NULL for demonstration"
```

The *otherwise* method is only applied when the value is null. So :

```
echo $some->otherwise('my other value') // initial $some instance returned
    ->then(strtoupper(...))
    ->then(fn(string $value) => $value . ' for demonstration')
    ->value(); // prints "MY VALUE for demonstration"
```

Of course, we already know the content of the instances of the examples above. During runtime, we just can suppose that our value could be null. Sometimes, *then()* and *otherwise()* are not enough to make the job we want to. Another possibility is to use *either()* :

```
// with Some value
echo $some->either(toUpperCase(...))
    ->or(fn() => 'late value')
    ->value(); // prints "MY VALUE"

// with None value
echo $not->either(toUpperCase(...))
    ->or(fn() => 'late value')
    ->value(); // prints "late value"
```

And to retrieve a new **Hypothetik** instance from **Either** :

```
// with Some value
echo $some->either(toUpperCase(...))
    ->or(fn() => 'late value')
    ->try()    // returns a Maybe from "$some" value
    ->then(fn(string $value) => $value . ' for demonstration')
    ->value(); // prints "MY VALUE for demonstration"

// with None value
echo $not->either(toUpperCase(...)) // won't run this function
    ->or(fn() => 'late value') // returns a new Either instance holding this new function
    ->try() // returns a Maybe with "late value'
    ->then(fn(string $value) => $value . ' for demonstration') // append a string and return another Maybe with new complete string
    ->value(); // prints "late value for demonstration"
```

Boolean value :
---------------

[](#boolean-value-)

An hypothetik boolean value works a different way. It always holds an **Option** with a boolean value where false is the invalid one. The **Hypothetik** interface ensures a fully substitutionnable instance, providing the hability to chain methods from a **Maybe** to a **When** or reverse.

Here's a simple example :

```
$phrase = 'this is just a test';

$when = Maybe::some(str_contains($phrase, 'just')); // truthy When
echo $when->value() ? 'valid' : 'invalid'; // 'valid'
$whenNot = Maybe::some(str_contains($phrase, 'only')); // falsy When
echo $whenNot->value() ? 'valid' : 'invalid'; // 'invalid'
```

**When** class has its own static factory method :

```
$when = When::ever(true); // or false...
```

Methods *apply()* and *then* won't use the boolean value as first function parameter, this time. Additionnal rguments are allowed, separated by comas, just like **Maybe**.

Combining **Maybe** and **When** instance calls :

```
$truthyWhen = Maybe::some('/about') // instance of Maybe
    ->then(fn (string $value) => str_starts_with($value, '/')) // instance of When
    ->either(fn() => 'truthy result') // will be executed
    ->or(fn() => 'falsy result') // won't be executed
    ->try(); // Maybe
echo $when->value(); // 'truthy result'

$falsyWhen = Maybe::some('/about') // instance of Maybe
    ->then(fn (string $value) => trim($value, '/')) // instance of Maybe
    ->then(fn (string $value) => str_starts_with($value, '/')) // instance of When
    ->either(fn() => 'truthy result') // won't be executed
    ->or(fn() => 'falsy result') // will be executed
    ->value(); // raw value
echo $when; // 'falsy result'
```

Notes
-----

[](#notes)

No dependency injection. User has to provide required instances if needed. A **Maybe** cannot carry another **Hypothetik**, an **Option** cannot carry another **Option**. Trying to do so will return the given instance as is.

Issues
------

[](#issues)

I'm still not able to use Php Documentation properly in order to provide autocompletion from any IDE. Problems on generic types handling.

I still don't need any **Hypothetik** container to handle multiple **Hypothetik** instances. I'll think about this kind of implementation only if necessary...

Maybe some tests are still missing. I'm not sure to cover all possible use cases.

###  Health Score

20

—

LowBetter than 14% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity9

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

3

Last Release

851d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4a4d8042606038f6fe934a289278b31406405b77801d3e77142b29341a2614d6?d=identicon)[ascetik](/maintainers/ascetik)

---

Top Contributors

[![ascetik](https://avatars.githubusercontent.com/u/92370226?v=4)](https://github.com/ascetik "ascetik (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ascetik-hypothetik/health.svg)

```
[![Health](https://phpackages.com/badges/ascetik-hypothetik/health.svg)](https://phpackages.com/packages/ascetik-hypothetik)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
