cretz / pratphall

A typed language targeting PHP (abandoned)
http://cretz.github.io/pratphall/
Other
45 stars 5 forks source link

Object/class/variable language semantics #21

Open mindplay-dk opened 11 years ago

mindplay-dk commented 11 years ago

One of the biggest problems I can see with this idea, is that JS/TS language semantics are radically different from those of php.

The biggest difference seems to be the fact that, in php, we have two namespaces: a variable namespace and a static namespace for classes, interfaces, etc. - whereas in JS/TS we have a single variable namespace, and things that are otherwise immutable in php are mutable.

For example, most existing JS code probably cannot currently be compiled to php, as typical JS relies heavily on things being mutable.

Some examples that produce invalid output:

class Foo
{
  public drink() {
    echo('hep hey!');
  }
}

Foo['nub'] = 'snuff';

var test = new Foo();

test.drink = () => {
  echo('drinking! hep hey!');
}

Supporting the language semantics of JS/TS is not impossible, but you would need a php run-time that implements these semantics.

Is that beyond the scope of this project, or is your (long term) objective to actually run "real" TypeScript on php, effectively using it as a VM?

Impressive project anyhow - at the very least, I could see it being useful for things like a validation library, which you could "write once run anywhere", e.g. both for client-side (real time) and server-side form validation. Pretty cool :-)

mindplay-dk commented 10 years ago

Come to think of it, you're already generating PHP interface and class statements from class and interface statements in TypeScript, and introducing type-hints for things like public function sip(drink: Drink) e.g. public function sip(Drink $drink) in PHP, which is something entirely different from TypeScript semantics, where all type-hints are actually advisory only - meaning, a variable has the type you say it has, so that for example type-casts will work as expected on objects.

I have some experience with TypeScript by now, and real appreciation for this very interesting merger of static and dynamic typing - and I'm starting to think the misunderstanding here, is that the generated PHP code has to resemble PHP... to achieve TypeScript semantics in the generated PHP code, the PHP code should actually resemble the JavaScript code much more than the PHP code.

For example:

interface Account {
    username: string;
    password: string;
}

class User {
    public username: string;
    public password: string;

    constructor (username: string) {
        this.username = username;
    }
}

module LoginService {
    export function login(account: Account) {
        // ...
    }
}

var user = new User('rasmus');

LoginService.login(user);

Compiled to JavaScript:

var User = (function () {
    function User(username) {
        this.username = username;
    }
    return User;
})();

var LoginService;
(function (LoginService) {
    function login(account) {
        // ...
    }
    LoginService.login = login;
})(LoginService || (LoginService = {}));

var user = new User('rasmus');

LoginService.login(user);

As compared to PHP:

<?php
namespace LoginService {

    function login(\Account $account) {
    }
}

namespace  {
    interface Account {
    }

    class User {
        public $username;

        public $password;

        public function __construct($username) {
            $this->username = $username;
        }

    }

    $user = new User('rasmus');
    \LoginService\login->__invoke($user);
}

To point out a few differences:

Modules and functions are objects - a namespace in PHP is not an object. In JS/TS we can attach properties to modules - you can't do that to a namespace or a function/closure in PHP.

Methods are properties of objects - in JS/TS, we can replace/override these properties with new function objects at run-time, which doesn't work in PHP.

Classes are objects, and/or properties of objects (modules) - in JS/TS, can we replace/override entire classes at run-time, which you can do to a regular PHP class.

Modules, classes and variables in TS/JS all occupy the same symbolic scope or "namespace", which is totally different from PHP, where class-names and namespaces occupy a different space from variables which all start with $ - to mimic this behavior with PHP, the generated code would need to use variables for everything, including classes and modules.

Interfaces are implicitly "implemented" by classes in TS if the class satisfies the requirements of the interface - this is a unique and important TS feature, and a big difference in terms of semantics, since in TS the LoginService.login() method will accept any type of object, not just those that explicitly implement that interface. Introducing type-hints in method signatures in the generated PHP code leads to run-time type-checks, which TS/JS does not perform - big difference.

To get meaningful warnings/inspections/compiler errors from the standard TS compiler, essentially all modules, classes and functions need to be "objects" in the JavaScript sense - something closer to stdObject, but most likely derived from a custom base-class of some sort, providing support for TS/JS standard properties.

I don't think there's any way around at least a minimal run-time, if you want to actually run TypeScript on PHP servers - which I think would be totally cool ;-)

If I ever have a spare evening again, I might fork it and play around with it...