phunkie/phunkie-adt

There is no license information available for the latest version (0.1.0) of this package.

0.1.0 2017-08-20 12:12 UTC

This package is not auto-updated.

Last update: 2024-04-14 02:24:12 UTC


README

"In computer programming, especially functional programming and type theory, an algebraic data type is a kind of composite type, i.e., a type formed by combining other types." — good old Wikipedia

Sum Types

In computer science sum type, is a data structure used to hold a value that could take on several different, but fixed, types. Only one of the types can be in use at any one time, and a tag field explicitly indicates which one is in use.

Let's say we want to create a type Weekday. We want it to be limited to the possible 7 days in the week: Sunday, Monday, Tuesday, Wednesday, Thursday, Friday and Saturday.

We could do that in a number of ways using typical imperative or Object Oriented techniques. We could use enums, constants grouped under an interface or inheritance.

At the moment PHP does not have enums, so that one is not a choice.

Grouping constants in a interface is a popular alternative, but it does not give any type safety.

<?php
interface WeekDay {
    const SUNDAY = 'Sunday',
          MONDAY = 'Monday',
          TUESDAY = 'Tuesday',
          WEDNESDAY = 'Wednesday',
          THURSDAY = 'Thursday',
          FRIDAY = 'Friday',
          SATURDAY = 'Saturday';
}

When passing a weekday to a function I cannot specify the Weekday type as a type hint.

Finally we could try extending a super class or implementing a common interface.

<?php
interface Weekday {}
final class Sunday implements Weekday { }
final class Monday implements Weekday { }
//...

Now we can type hint Weekday we passing it around:

<?php
function orderPizza(Weekday $weekday, Pizza $pizza)
{
    switch (get_class($weekday)) {
        case Wednesday::class : return new WednesdayPromotionOrder($pizza);
        default : return new Order($pizza);
    }
}

There is, however, no way in PHP to guarantee that the interface won't be extended beyond the ones distributed with the library. Nothing stops a Pluto fan from:

<?php
final class Plutoday implements Weekday { }

And all of a sudden we can now order pizza in weird days. But fear not! Sum types is there to save us from that madness.

First we create a type constructor Weekday. PHP only way to create hintable types is through classes. So, let's use that.

<?php
abstract class Weekday implements TypeConstructor { use SumType; }

Then we seal it. Let's use ImmutableSealed to add immutablity while we are at it

<?php
abstract class Weekday extends ImmutableSealed implements TypeConstructor { use SumType; }

ImmutableSealed requires us to tell what is the types the type constructor can create

<?php
abstract class Weekday extends ImmutableSealed implements TypeConstructor, SumTypeTag {
    const sealedTo = [ Sunday::class, Monday::class, Tuesday::class,
        Wednesday::class, Thursday::class, Friday::class, Saturday::class];
    public function __construct() { $this->applySeal(); }
}

We still need to create the sum types:

<?php
final class Sunday extends Weekday { use SumType; const typeConstructor = Weekday::class; }
final class Monday extends Weekday { use SumType; const typeConstructor = Weekday::class; }
//...
final class Saturday extends Weekday { use SumType; const typeConstructor = Weekday::class; }

If we try to extend Weekday outside the seal we get a type error.

<?php
final class Plotoday extends Weekday { use SumType; const typeConstructor = Weekday::class; }

new Plutoday; // results in:
              // TypeError has been thrown with message: "Weekday is sealed and cannot be extended outside seal."

TODO:

  • Exhaustive analysis
  • Generics/Kinds: First order, higher order
  • Implementing Show, Eq, Ord

Final Notes

  • Phunkie is alpha software and may not be used in production