marcosh/lamphpda
Composer 安装命令:
composer require marcosh/lamphpda
包简介
A collection of functional programming data structures
README 文档
README
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:
- a safe usage of sum types, following http://marcosh.github.io/post/2017/10/27/maybe-in-php-2.html
- the usage of higher kinded types to increase reusability and type safety (see http://marcosh.github.io/post/2020/04/15/higher-kinded-types-php-issue.html and http://marcosh.github.io/post/2020/04/15/higher-kinded-types-php-solution.html)
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.
marcosh/lamphpda 适用场景与选型建议
marcosh/lamphpda 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 14.35k 次下载、GitHub Stars 达 122, 最近一次更新时间为 2020 年 07 月 14 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「data structures」 「monad」 「functional programming」 「functor」 「monoid」 「Applicative」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 marcosh/lamphpda 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 marcosh/lamphpda 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 marcosh/lamphpda 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Shoot aims to make providing data to your templates more manageable
Option type (Some/None) replacing nullable types with explicit presence semantics
Result type capturing success or failure as a value for controlled error handling
Structure-aware migration helpers for Laravel Enso packages
Adds the EDTF data type to Wikibase
A simple library that allows transform any kind of data to native php data or whatever
统计信息
- 总下载量: 14.35k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 122
- 点击次数: 6
- 依赖项目数: 4
- 推荐数: 0
其他信息
- 授权协议: Hippocratic-2.1
- 更新时间: 2020-07-14