Skip to content

Tagged unions / sealed classes #107

Open

Description

@thekid

See https://wiki.php.net/rfc/tagged_unions:

// Declaration
enum Distance {
  case Kilometers(public int $km);
  case Miles(public int $miles);
}

// Usage
$walk= Distance::Miles(500);
print $walk->miles; // prints "500"

The famous "Maybe" nomad:

// Declaration
enum Optional {
  case None {
    public function bind(callable $f) { return $this; }
    public function value(): mixed { return null; }
  };
 
  case Some(private mixed $value) {
    public function bind(callable $f): Optional { return Optional::of($f($this->value)); }
    public function value(): mixed { return $this->value; }
  };

  public static function of($value): self {
    return $value instanceof self ? $value : ($value ? self::Some($value) : self::None);
  }
}

// Usage
$value= Optional::of($record)->bind(fn($r) => $r['distance'] + 1)->value();

These are very similar to Kotlin's sealed classes: In some sense, sealed classes are similar to enum classes: the set of values for an enum type is also restricted, but each enum constant exists only as a single instance, whereas a subclass of a sealed class can have multiple instances, each with its own state..

See also https://www.dotnetcurry.com/patterns-practices/1510/maybe-monad-csharp

Activity

  1. thekid commented on Mar 13, 2021

    @thekid
    MemberAuthor

    First implementation idea:

    // What the user writes
    enum Distance {
      case Miles(public int $miles);
    }
    
    // What gets emitted
    abstract class Distance implements \TaggedUnion {
    
      public static function Miles(int $miles): self {
        return new class('Miles', $miles) extends self {
          public $name, $miles;
    
          private function __construct($name, $miles) {
            $this->name= $name;
            $this->miles= $miles;
          }
        };
      }
    }
  2. thekid commented on Dec 21, 2025

    @thekid
    MemberAuthor

    To match on these, xp-lang/php-is-operator#2 will come in handy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions