• Stars
    star
    114
  • Rank 308,031 (Top 7 %)
  • Language
    PHP
  • License
    Other
  • Created almost 5 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A collection of type-safe functional data structures

lamPHPda

A collection of type-safe functional data structures

Aim

The aim of this library is to provide a collection of functional data structures in the most type safe way currently possible within the PHP ecosystem, still providing a generic and consistent API.

Main ideas

The two ideas which differentiate this from other functional libraries in PHP are:

Installation

composer require marcosh/lamphpda

Tools

We use Psalm as a type checker. It basically works as a compilation step, ensuring that all the types are aligned.

To benefit from this library, it is compulsory that your code runs through a Psalm check.

Development

This library includes a flake.nix file, enabling you to access a development environment equipped with PHP 8.1 and Composer. To utilize it, simply execute nix develop within the directory. For those using direnv (with nix-direnv), a preconfigured .envrc file is also provided, which will automatically load the environment upon entering the library directory.

Decision record

The relevant decisions regarding the project are collected in the adr folder, following the Architectural decision record format

Content

The library provides several immutable data structures useful to write applications in a functional style.

Currently, the implemented data structures are:

  • Maybe, which allows modelling data which could be missing;
  • Either, which models the idea of alternative;
  • Identity, which is just a simple wrapper
  • LinkedList, which models the possibility of having multiple values;
  • Pair, which models having two thing at the same time;
  • Reader, which models values which depend on a context;
  • State, which models values which can interact with a global state;
  • IO, which models lazy values.

You can find more details about the implementation and the idea behind each data structure in the docs/data-structures folder.

How to interact with the data structures

The library is built to be extremely abstract and generic to allow extreme composability and reusability.

There are various ways which you can use to interact with the provided data structures.

Typeclasses

You can think of typeclasses as of behaviours which could be attached to a data structure. Since a data structure could in principle have more than one way to implement a specific behaviour (e.g., there's more than one way to use two integers to compute a new integer), we can not use directly interfaces to be implemented by our data structures. Therefore, typeclass instances are implemented as separate independent objects implementing an interface which describes the typeclass itself.

For example, the Semigroup typeclass, which describes the behaviour of putting together two things of the same type to obtain a thing of the same type, could be implemented as

/**
 * @template A
 */
interface Semigroup
{
    /**
     * @param A $a
     * @param A $b
     * @return A
     */
    public function append($a, $b);
}

Now we can implement a Semigroup instance for any type we want, even for native types. For example, we could implement a semigroup for addition between integers

/**
 * @implements Semigruop<int>
 */
final class IntAddition implements Semigroup
{
    /**
     * @param int $a
     * @param int $b
     * @return int
     */
    public function append($a, $b): int
    {
        return $a + $b;
    }
}

Then we could use it to sum two integers

(new IntAddition())->append(1, 2); // returns 3

This specific instance is not that interesting, but the fact that you could write code which depends on a generic Semigroup definitely is!

The typeclasses we are currently exposing are:

  • Functor, which allows lifting functions of one argument to a given context;
  • Apply, which allows lifting functions of any arity to a given context;
  • Applicative, which allows lifting values to a context;
  • Alternative, which models the ability of combining values wrapped in a context;
  • Monad, which allows sequencing functions which return a value in a context;
  • MonadThrow, which allows managing exceptions in a pure way
  • Foldable, which allows to shrink a data structure to a single value;
  • Traversable, which allows transforming a data structure with a function returning values in an applicative context;
  • Semigroup, which allows combining two values of the same type;
  • Monoid, which allows creating an identity element;
  • Bifunctor, which models context which depends on two covariant type variables;
  • Profunctor, which models context which depends on a contravariant and a covariant type variable.

More details on each typeclass can be found in the docs/typeclasses folder.

Typeclasses and data structures

As a design principle for this library, we try to expose on our data structures only methods which come from a typeclass. This means that the provided data structure have a standard common API which makes use of typeclasses instances.

For example, Either has two Apply instances. To choose which one you want to use, Either exposes the iapply method which takes as first argument an instance of an Apply typeclass for Either.

/**
 * @template A
 * @template B
 */
final class Either
{
    /**
     * @template C
     * @param Apply<EitherBrand<A>> $apply
     * @param HK1<EitherBrand<A>, callable(B): C> $f
     * @return Either<A, C>
     */
    public function iapply(Apply $apply, HK1 $f): self
}

We are able to specify that a typeclass instance refers to a specific data structure using the so-called Brands, which are nothing else that tags at the type level which enable us to simulate higher kinded types.

Default typeclass instances

More often than not a data structure admits only one instance of a typeclass, or there exists one which is considered standard in the literature. In such cases it is quite inconvenient to sustain the burden of passing the typeclass instance; to ease the pain, we expose also the method where the default typeclass instance is already provided.

Continuing with the example in the previous section, Either exposes also a method apply where the EitherApply instance is hardcoded.

/**
 * @template A
 * @template B
 */
final class Either
{
    /**
     * @template C
     * @param HK1<EitherBrand<A>, callable(B): C> $f
     * @return Either<A, C>
     */
    public function apply(HK1 $f): self
    {
        return $this->iapply(new EitherApply(), $f);
    }
}

Contributing

If you wish to contribute to the project, please read the CONTRIBUTING notes.

More Repositories

1

crem

Compositional Representable Executable Machines
Haskell
101
star
2

php-validation-dsl

A DSL for validating data in a functional fashion
PHP
48
star
3

existential-optics

A simple optics library based on existential encoding
Haskell
12
star
4

lamphpda-validation

A validation library using Either from marcosh/lamphpda
PHP
11
star
5

elm-escqrs

experiments with elm and es/cqrs
Elm
9
star
6

lamphpda-optics

A functional optic library for PHP
PHP
8
star
7

elm-html-to-unicode

elm library to convert html characters to unicode
Elm
6
star
8

theartofcomposition

slides for my talk about category theory
6
star
9

elm-table-football

an application to connect elm, ddd/es/cqrs and table football
Elm
5
star
10

elm-joindin

A page for displaying links to slides of joindin talks
Elm
4
star
11

typed-state-machine

Haskell
3
star
12

marcosh.github.io

JavaScript
3
star
13

idris-hanoi

a super type safe implementation of the game of the Tower of Hanoi
Idris
3
star
14

fun-with-categories-talk

slides for the `fun with categories` talk
JavaScript
3
star
15

dhall-deptrac

Dhall types and functions to manage Deptrac configurations
Dhall
3
star
16

ddd-machines-dddeurope

Haskell
2
star
17

property-based-testing-examples

Haskell
2
star
18

elm-hanoi

The Hanoi tower game implemented in Elm
JavaScript
2
star
19

effector

A Php library to write effect aware code.
PHP
1
star
20

php-type-checker

Checks which methods are missing a return type hint
PHP
1
star
21

fundic

PHP purely functional dependency injection container
PHP
1
star
22

maybe-php

A safe and generic implementation on Maybe in PHP
PHP
1
star
23

domain-modelling-state-machines-talk

1
star
24

traffic

Experimenting with traffic simulations using [Arduino](https://www.arduino.cc/) and [Copilot](https://copilot-language.github.io/).
Haskell
1
star
25

php-sum-types

another attempt at sum types in PHP
PHP
1
star