PHPackages                             scalp/scalp - 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. scalp/scalp

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

scalp/scalp
===========

Some Scala useful classes ported to PHP.

v0.2.0(8y ago)171091[8 issues](https://github.com/pawaclawczyk/scalp/issues)MITPHPPHP ~7.1CI failing

Since Sep 10Pushed 8y ago6 watchersCompare

[ Source](https://github.com/pawaclawczyk/scalp)[ Packagist](https://packagist.org/packages/scalp/scalp)[ RSS](/packages/scalp-scalp/feed)WikiDiscussions master Synced 2w ago

READMEChangelogDependencies (2)Versions (5)Used By (0)

[![Build Status](https://camo.githubusercontent.com/92ecc7aa886e0825c5e3428df1caa7f8767fa97d18c9c7fbb857bb0c161754f3/68747470733a2f2f7472617669732d63692e6f72672f70617761636c6177637a796b2f7363616c702e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/pawaclawczyk/scalp)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/4f95579b713ef998b7f32b09a19a6d3e2ee4b67aaee91bca9b5b4e66dbbbce5c/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f70617761636c6177637a796b2f7363616c702f6261646765732f7175616c6974792d73636f72652e706e67)](https://scrutinizer-ci.com/g/pawaclawczyk/scalp/)

Scalp
=====

[](#scalp)

Scalp
-----

[](#scalp-1)

### Option

[](#option)

The `Option` type represents optional value. It can be either `Some` value or `None`.

```
function divide(int $x, int $y): Option
{
    return $y === 0 ? None() : Some(intdiv($x, $y));
}

println(divide(42, 6));
println(divide(42, 0));
```

```
Some[int](7)
None
```

`Option` can be used as collection with `map`, `flatMap` or `filter`.

```
$option = Option(42);

$square = function (int $x): int {
   return $x ** 2;
};

println($option->map($square));

$isOdd = function (int $x): bool {
   return $x % 2 === 1;
};

println($option->filter($isOdd));

$squareRoot = function (int $x): Option {
   return $x >= 0 ? Some(sqrt($x)) : None();
};

println($option->flatMap($squareRoot));
```

```
Some[integer](1764)
None
Some[double](6.4807406984079)
```

Computation performed on `Some` can also be performed on `None` without any side effect. The only difference is that the result is always `None`.

```
println(None()->map($square));
println(None()->filter($isOdd));
println(None()->flatMap($squareRoot));
```

```
None
None
None
```

### Function Currying

[](#function-currying)

Function currying allows to use function as a function of lower arity by providing arguments in separated steps.

```
use function Scalp\curry;
use function Scalp\println;

$match = curry('preg_match');

$containsFoo = $match('/foo/');
$containsBar = $match('/bar/');

println($containsFoo('foobar'));   // 1
println($containsFoo('foofoo'));   // 1
println($containsFoo('barbar'));   // 0

println($containsBar('foobar'));   // 1
println($containsBar('foofoo'));   // 0
println($containsBar('barbar'));   // 1
```

### Partial Function Application

[](#partial-function-application)

Partial function application lets to apply some of function arguments immediately, while rest of them can be applied later.

```
$isEven = function (int $x): bool {
    return $x % 2 === 0;
};

$filterEven = papply(array_filter, __, $isEven);

println(AnyToString(
    $filterEven([-2, -1, 0, 1, 2])
));

println(AnyToString(
    $filterEven([11, 13, 17, 19])
));
```

```
Array(-2, 0, 2)
Array()
```

### Tuple

[](#tuple)

`Tuple` is a data structure that holds elements of different types. `Pair` is factory function for `Tuple` with two elements.

```
$singleton = Tuple(42);

$pair      = Pair('Life', 42);

$triple    = Tuple('text', 27, false);
```

`Tuple` exposes its elements by properties with names `_1`, `_2` to `_N`.

*Elements of Tuple cannot be set.*

Scalp\\Conversion
-----------------

[](#scalpconversion)

### AnyToString

[](#anytostring)

Converts any type to string. In case of value type looks for implicit conversion function and if not found casts to value string. In case of object type at first looks if object implements `toString` or `__toString` method, then looks for implicit conversion and if none of then has been found return object hash id.

```
echo AnyToString(null) . "\n";
echo AnyToString(false) . "\n";
echo AnyToString(36.6) . "\n";
echo AnyToString(printAny(new class { function toString(): string { return 'Hello World!'; }});) . "\n";
```

```
null
false
36.6
Hello World!
```

### Implicit conversion

[](#implicit-conversion)

Implicit conversion is a function that convert value of one type into another.

*Current version does not provide support for implicit conversion. Very simplified version is used by `AnyToString`. Implicit conversion should have name following convention `[TypeA]To[TypeB]`. In example in case of conversions able to convert value of some type to string, `AnyToString` will look for functions with name \[Type\]ToString.*

Scalp\\PatternMatching
----------------------

[](#scalppatternmatching)

Pattern matching is a mechanism for checking value against a pattern. You can think of it as of advanced switch statement. In opposition to `switch` and `if` statements, pattern matching is an expression (it returns value, like ternary operator `?:`). General use of pattern matching expression:

```
$result = match($subject)
    ->case($pattern1, $callableToRunForPattern1)
    ->case($pattern2, $callableToRunForPattern2)
    ...
    ->case($patternN, $callableToRunForPatternN)
    ->done();
```

Case patterns are checked in order of declaration. When more general pattern is declared before specific one, it will always fall into the general case.

### Case classes and type deconstruction

[](#case-classes-and-type-deconstruction)

`CaseClass` is an interface that ensures existing of `deconstruct` method. `Deconstruct` method should return arguments used to construct instance of given type. You can use trait `Deconstruction` to provide the ability to deconstruct. It enables pattern matching to compare immutable complex type values.

In example the `Option` type is implemented as case class.

```
abstract class Option implements CaseClass
{
    use Deconstruction;

    ...
}

final class Some extends Option
{
    private $value;

    public function __construct($value)
    {
        $this->construct($value);

        $this->value = $value;
    }

    ...
}
```

### Patterns

[](#patterns)

The most basic pattern is `Any`, it will match anything.

```
$res0 = match(42)
    ->case(Any(), function (): string { return 'Anything'; })
    ->done();

// $res0 === 'Anything'

$res1 = match(Some(42))
    ->case(Any(), function (): string { return 'Anything'; })
    ->done();

// $res1 === 'Anything'
```

`Value` pattern does regular comparison with `===` for primitive types or `==` for objects. When loose comparison is used, objects properties are also compared with `==` (see 3rd example).

```
$res2 = match(42)
    ->case(Value(13), function (): string { return 'Number 13'; })
    ->case(Value('42'), function (): string { return 'String "42"'; })
    ->case(Value(42), function (): string { return 'Number 42'; })
    ->case(Any(), function (): string { return 'Fallback'; })
    ->done();

// $res2 === 'Number 42'

$res3 = match(Some(42))
    ->case(Value(Some(13)), function (): string { return 'Some 13'; })
    ->case(Value(Some(42)), function (): string { return 'Some 42'; })
    ->case(Any(), function (): string { return 'Fallback'; })
    ->done();

// $res3 === 'Some 42'

$res4 = match(Some(42))
    ->case(Value(Some('42')), function (): string { return 'Some 42'; })
    ->case(Any(), function (): string { return 'Fallback'; })
    ->done();

// $res4 === 'Some 42'
```

The `Type` can be used as simple pattern that checks value type.

```
$res5 = match(42)
    ->case(Type('string'), function (): string { return 'String'; })
    ->case(Type('integer'), function (): string { return 'Integer'; })
    ->case(Any(), function (): string { return 'Not integer'; })
    ->done();

// $res5 === 'integer'

$res6 = match(Some(42))
    ->case(Type(None::class), function (): string { return 'None'; })
    ->case(Type(Some::class), function (): string { return 'Some'; })
    ->case(Any(), function (): string { return 'Neither'; })
    ->done();

// $res6 === 'Some'
```

`Type` pattern works with `CaseClass` deconstruction. It gives tha ability to look inside type construction and pattern match its arguments.

```
$res7 = match(Some(42))
    ->case(Type(Some::class, Value('42')), returnString('Inner value is string'))
    ->case(Type(Some::class, Value(42)), returnString('Inner value is integer'))
    ->case(Any(), returnString('Fallback'))
    ->done();

// $res7 === 'Inner value is integer'
```

### Value binding

[](#value-binding)

Every value matched by a pattern can be bound and used as handler argument.

```
$res8 = match(new Tuple('2 * 7 = ', 14))
    ->case(
        Type(Tuple::class, Any()->bind(), Any()->bind()),
        function (string $question, int $answer): string { return concat('Solution: ', $question, AnyToString($answer)); }
    )
    ->case(Any(), returnString('Fallback'))
    ->done();

// $res8 === 'Solution: 2 * 7 = 14'
```

### Example

[](#example)

```
abstract class Notification implements CaseClass {};

final class Email extends Notification
{
    public function __construct(string $sender, string $title, string $body) { ... }
}

final class SMS extends Notification
{
    public function __construct(string $caller, string $message) { ... }
}

final class VoiceRecording extends Notification
{
    public function __construct(string $contactName, string $link) { ... }
}

function showNotification(Notification $notification): string
{
    return match($notification)
        ->case(
            Type(Email::class, Type('string')->bind(), Type('string')->bind(), Any()),
            papply(concat, 'You got an email from ', __, 'with title: ', __)
        )
        ->case(
            Type(SMS::class, Type('string')->bind(), Type('string')->bind()),
            papply(concat, 'You got an SMS from ', __, '! Message: ', __)
        )
        ->case(
            Type(VoiceRecording::class, Type('string')->bind(), Type('string')->bind()),
            papply(concat, 'You received a Voice Recording from ', __, '! Click the link to hear it: ', __)
        )
        ->done();
}

$someSms = new SMS('12345', 'Are you there?');
$someVoiceRecording = new VoiceRecording('Tom', 'voicerecording.org/id/123');

println(showNotification($someSms));
println(showNotification($someVoiceRecording));
```

```
You got an SMS from 12345! Message: Are you there?
You received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123

```

Scalp\\Utils
------------

[](#scalputils)

### Delayed

[](#delayed)

The `Delayed` type represents a postponed computation. It is created from a callable -- the computation and its run arguments. The `Delayed` type is callable type, when called it executes the postponed computation. In order to create delayed computation use factory method `dalay(callable $f, ...$args)`.

```
use function Scalp\Utils\delay;

$delayed = delay(function (int $x): int { return $x * $x; }, 2);

echo $delayed();
```

```
4
```

### TryCatch

[](#trycatch)

The `TryCatch` type represents computation that may either result in an exception, or return successful value.

```
use function Scalp\Utils\delay;
use function Scalp\Utils\TryCatch;

$computation = function (int $divisor): int {
    return intdiv(42, $divisor);
};

$success = TryCatch(delay($computation, 7));
$failure = TryCatch(delay($computation, 0));

echo "Success: $success\n";
echo "Failure: $failure\n";
```

```
Success: Success[integer](6)
Failure: Failure[DivisionByZeroError]("Division by zero")
```

###  Health Score

25

—

LowBetter than 36% of packages

Maintenance11

Infrequent updates — may be unmaintained

Popularity18

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 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 ~5 days

Total

2

Last Release

3209d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ed522f6fc3505e285f6e2183effd2f67015b39888b08539386506778868d976b?d=identicon)[pawaclawczyk](/maintainers/pawaclawczyk)

---

Top Contributors

[![pawaclawczyk](https://avatars.githubusercontent.com/u/1135845?v=4)](https://github.com/pawaclawczyk "pawaclawczyk (61 commits)")

---

Tags

immutablemaybeoptionpattern-matchingphp7trytry-catchtypes

###  Code Quality

TestsPHPUnit

### Embed Badge

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

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

###  Alternatives

[jblond/php-diff

A comprehensive library for generating differences between two hashable objects (strings or arrays).

36128.6k1](/packages/jblond-php-diff)

PHPackages © 2026

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