Coder Social home page Coder Social logo

algebraic-types's Introduction

PHP RFC: Algebraic Types

Introduction

The PHP already provides a set of types that are intersections of existing ones (pseudo-types), but they do not regulate their hierarchy and the possibility of overload. In addition, attempts to implement intersections that have been rejected or not completed to varying degrees have been repeatedly made:

In addition, there are several RFCs that have already been taken, for example object or iterable type hints.

This RFC proposes to regulate the type hierarchy and allow declaring them by yourself using type keyword.

For example, take the existing implementation of iterable type:

interface UsersRepositoryInterface
{
    public function all(): iterable;
}

In this example, we see a reference to iterable, which can be decomposed into two different types: array and \Traversable. In this case, we can define this type using the proposed solution:

type iterable = array | \Traversable;

Proposal

Add the ability to describe types using the keyword type:

type scalar = bool | int | float | string | null;

class User
{
    private string $name;

    public function rename(scalar $newName): self
    {
        $this->name = (string)$newName;
    }
}

In addition, a similar declaration must allow both disjunction and conjunction operators including grouping conditions:

type arrayable = array | (\Traversable & \ArrayAccess & \Countable);

class ArrayHelper
{
    public static function first(arrayable $subject, $default = null)
    {
        if (\is_array($subject)) {
            return $subject[\array_key_first($subject)] ?? $default;
        }
    
        foreach ($subject as $item) {
            return $item;
        }
        
        return $default;
    }
}

Implicit Declaration

Each class or interface declaration implicitly declares a type:

class DatabaseUsersRepository extends Repository implements UsersRepositoryInterface
{
}

//
// Same as implicit type declaration:
//

type DatabaseUsersRepository = Repository | UsersRepositoryInterface | object;

The existence of traits does not affect the final type, since they have the ability to change their signatures while injecting into an class:

class Request implements RequestInterface
{
    use BodyTrait;
    use CookiesTrait;
    use HeadersTrait;
}

//
// Same as implicit type declaration:
//

type Request = RequestInterface | object;

Namespace Influence

Types should be affected by namespaces:

namespace App\Helpers;

type scalar = bool | int | float | string | null;
namespace App\Console;

class Application
{
    public function __construct(App\Helpers\scalar $name)
    {
    }
}

Namespace Imports

Types allowed to import in the current namespace and the ability to rename:

namespace App;

use type App\Helpers\scalar;
use type callable as alias;

Overriding

TODO reconcile builtin types hierarchy:

type mixed = [native code]; // "mixed" type-hint is an alias for type-hint absence

    type object = mixed & [native code];
    
        type \stdClass = object & [native code "instance of"]
        
    type scalar = mixed & [native code];
    
        type string = scalar & [native code];
        
        type int = scalar & [native code];
        
        type float = scalar & [native code];
        
        type bool = scalar & [native code];
        
    type resource = mixed & [native code];
    
    type array = mixed & [native code];

Note:

  • From right to left ( <- ) for arguments.
  • From left to right ( -> ) for return types.
interface ParentInterface
{
    public function example(string $message): scalar;
}

//
// @see https://wiki.php.net/rfc/parameter-no-type-variance
//
interface ValidChildAInterface extends ParentInterface
{
    public function example(mixed $message): scalar;
    // public function example($message): scalar;
}

interface ValidChildInterface extends ParentInterface
{
    public function example(scalar $message): string; // OK
}

interface InvalidChildAInterface extends ParentInterface
{
    public function example(int $message): string; // Error: string $message -> int $message
}

interface InvalidChildBInterface extends ParentInterface
{
    public function example(string $message): mixed; // Error: scalar -> mixed retunt type hint
}

Discussion

I would like to offer additional options for the development of this idea

Runtime Instructions

Because at the language level, type-hint assertions are executed in runtime, this does not prevent adding the ability to add runtime (anonymous functions):

type callableArray = array & 
    // Runtime assertion
    fn(array $callable): bool => 
        \count($callable) === 2 && \method_exists(...$callable);

type callableString = string & 
    // Runtime assertion
    fn(string $callable): bool => \function_exists($callable);

type callable = \Closure | callableArray | callableString;

I do not know how to make friends with overload =)

Critique

TBD

Backward Incompatible Changes

None.

algebraic-types's People

Contributors

serafimarts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

algebraic-types's Issues

Trait types

class Request implements RequestInterface
{
    use BodyTrait;
    use CookiesTrait;
    use HeadersTrait;
}

//
// Same as implicit type declaration:
//

type Request = RequestInterface | BodyTrait | CookiesTrait | HeadersTrait | object;

What about trait conflicts and method visibility?
https://www.php.net/manual/en/language.oop5.traits.php#language.oop5.traits.conflict

trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

class Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.