Algorithm W - implementation continues
authorwidmogrod <widmogrod@gmail.com>
Fri, 1 Jun 2018 19:49:55 +0000 (21:49 +0200)
committerwidmogrod <widmogrod@gmail.com>
Fri, 1 Jun 2018 19:49:55 +0000 (21:49 +0200)
example/AlgorithmW.php [new file with mode: 0644]

diff --git a/example/AlgorithmW.php b/example/AlgorithmW.php
new file mode 100644 (file)
index 0000000..b5f088c
--- /dev/null
@@ -0,0 +1,597 @@
+<?php
+
+declare(strict_types=1);
+
+namespace example;
+
+/*
+data Exp
+    = EVar String
+    | ELit Lit
+    | EApp Exp Exp
+    | EAbs String Exp
+    | ELet String Exp Exp
+    deriving (Eq,Ord)
+
+data Lit
+    = LInt Integer
+    | LBool Bool deriving (Eq,Ord)
+
+data Type
+    = TVar String
+    | TInt
+    | TBool
+    | TFun Type Type
+    deriving (Eq,Ord)
+
+data Scheme = Scheme [ String ] Type
+
+*/
+
+use FunctionalPHP\FantasyLand\Monoid;
+use FunctionalPHP\FantasyLand\Semigroup;
+use Widmogrod\Monad\Maybe\Just;
+use Widmogrod\Monad\Maybe\Maybe;
+use Widmogrod\Monad\Maybe\Nothing;
+use Widmogrod\Primitive\Listt;
+use Widmogrod\Useful\PatternMatcher;
+use function Widmogrod\Functional\constt;
+use function Widmogrod\Functional\curryN;
+use function Widmogrod\Functional\foldr;
+use function Widmogrod\Functional\map;
+use function Widmogrod\Useful\match;
+use const Widmogrod\Functional\identity;
+
+interface Exp
+{
+}
+
+class EVar implements Exp
+{
+    private $string;
+
+    public function __construct($string)
+    {
+        $this->string = $string;
+    }
+
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->string);
+    }
+}
+
+class ELit implements Exp
+{
+    private $lit;
+
+    public function __construct(Lit $lit)
+    {
+        $this->lit = $lit;
+    }
+
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->lit);
+    }
+}
+
+class EApp implements Exp
+{
+    private $exp1;
+    private $exp2;
+
+    public function __construct(Exp $exp1, Exp $exp2)
+    {
+        $this->exp1 = $exp1;
+        $this->exp2 = $exp2;
+    }
+
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->exp1, $this->exp2);
+    }
+}
+
+class EAbs implements Exp
+{
+    private $string;
+    private $exp;
+
+    public function __construct($string, Exp $exp)
+    {
+        $this->string = $string;
+        $this->exp = $exp;
+    }
+
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->string, $this->exp);
+    }
+}
+
+class ELet implements Exp
+{
+    private $string;
+    private $exp1;
+    private $exp2;
+
+    public function __construct($string, Exp $exp1, Exp $exp2)
+    {
+        $this->string = $string;
+        $this->exp1 = $exp1;
+        $this->exp2 = $exp2;
+    }
+
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->string, $this->exp1, $this->exp2);
+    }
+}
+
+interface Lit
+{
+}
+
+class LInt implements Lit
+{
+    private $int;
+
+    public function __construct(int $int)
+    {
+        $this->int = $int;
+    }
+
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->int);
+    }
+}
+
+class LBool implements Lit
+{
+    private $bool;
+
+    public function __construct(bool $bool)
+    {
+        $this->bool = $bool;
+    }
+
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->bool);
+    }
+}
+
+interface Type
+{
+}
+
+class TVar implements Type
+{
+    private $string;
+
+    public function __construct($string)
+    {
+        $this->string = $string;
+    }
+
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->string);
+    }
+}
+
+class TInt implements Type
+{
+    public function patternMatched(callable $fn)
+    {
+        return $fn();
+    }
+}
+
+class TBool implements Type
+{
+    public function patternMatched(callable $fn)
+    {
+        return $fn();
+    }
+}
+
+class TFun implements Type
+{
+    private $type1;
+    private $type2;
+
+    public function __construct(Type $type1, Type $type2)
+    {
+        $this->type1 = $type1;
+        $this->type2 = $type2;
+    }
+
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->type1, $this->type2);
+    }
+}
+
+// data Scheme = Scheme [ String ] Type
+class Scheme
+{
+    /**
+     * @var array
+     */
+    private $strings;
+    private $type;
+
+    public function __construct(Listt $strings, Type $type)
+    {
+        $this->strings = $strings;
+        $this->type = $type;
+    }
+
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->strings, $this->type);
+    }
+}
+
+class Set implements Monoid
+{
+    /**
+     * @inheritdoc
+     */
+    public static function mempty()
+    {
+        // TODO: Implement mempty() method.
+    }
+
+    public static function fromList(Listt $l)
+    {
+    }
+
+    public static function toList(Set $set): Listt
+    {
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function concat(Semigroup $value): Semigroup
+    {
+        // TODO: Implement concat() method.
+    }
+}
+
+function union(Set $a, Set $b): Set
+{
+    // TODO: Implement union() method.
+}
+
+function difference(Set $a, Set $b): Set
+{
+    // TODO: Implement difference() method.
+}
+
+class Map
+{
+    const delete = 'Map::delete';
+
+    public static function elems(Map $map): Listt
+    {
+    }
+
+    /**
+     * @return $this
+     */
+    public static function union(Map $a, Map $b)
+    {
+    }
+
+    /**
+     * @return $this
+     */
+    public static function map(callable $fn, Map $map)
+    {
+    }
+
+    public static function lookup($key): Maybe
+    {
+    }
+
+    /**
+     * @return $this
+     */
+    public static function delete($key, Map $map)
+    {
+    }
+}
+
+function lookup(Map $map, $key): Maybe
+{
+    return $map->lookup($key);
+}
+
+// type Subst = Map.Map String Type
+class Subst extends Map implements PatternMatcher
+{
+    private $vars;
+    private $type;
+
+    public function __construct(Listt $vars, Type $type)
+    {
+        $this->vars = $vars;
+        $this->type = $type;
+    }
+
+
+    /**
+     * @inheritdoc
+     */
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->vars, $this->type);
+    }
+}
+
+
+// nullSubst :: Subst
+// nullSubst = Map.empty
+function nullSubst(): Subst
+{
+    return Subst::mempty();
+}
+
+// composeSubst :: Subst → Subst → Subst
+// composeSubst s1 s2 = (Map.map (apply s1) s2) ‘Map.union‘ s1
+function composeSubst(Subst $s1, Subst $s2): Subst
+{
+    return Subst::union(
+        Subst::map(apply($s1), $s2),
+        $s1
+    );
+}
+
+// newtype TypeEnv = TypeEnv (Map.Map String Scheme)
+class TypeEnv implements PatternMatcher
+{
+    private $env;
+
+    public function __construct(Map $env)
+    {
+        $this->env = $env;
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function patternMatched(callable $fn)
+    {
+        return $fn($this->env);
+    }
+}
+
+// remove :: TypeEnv → String → TypeEnv
+// remove (TypeEnv env) var = TypeEnv (Map.delete var env)
+
+function remove(TypeEnv $env, $var): TypeEnv
+{
+    return new TypeEnv(Map::delete($var, $env));
+}
+
+// generalize :: TypeEnv → Type → Scheme
+// generalize env t = Scheme vars t
+//      where vars = Set.toList ((ftv t) \ (ftv env))
+function generalize(TypeEnv $env, Type $t): Scheme
+{
+    return new Scheme(
+        Set::toList(difference(ftv($t), ftv($env))),
+        $t
+    );
+}
+
+const ftv = 'example\ftv';
+
+/**
+ * ftv :: a → Set.Set String
+ *
+ * @param  Type|Listt|Scheme|TypeEnv                $t
+ * @return Set
+ * @throws \Widmogrod\Useful\PatternNotMatchedError
+ */
+function ftv($t): Set
+{
+    return match([
+        // instance Types Type where
+        TVar::class => function ($n): Set {
+            return new Set($n);
+        },
+        TBool::class => function (): Set {
+            return Set::mempty();
+        },
+        TInt::class => function (): Set {
+            return Set::mempty();
+        },
+        TFun::class => function (Type $a, Type $b): Set {
+            return union(ftv($a), ftv($b));
+        },
+        // instance Types Scheme where
+        Scheme::class => function (Listt $vars, Type $t): Set {
+            // ftv (Scheme vars t) = (ftv t) \ (Set.fromList vars)
+            return difference(ftv($t), Set::fromList($vars));
+        },
+        // instance Types a ⇒ Types [a] where
+        Listt::class => function (Listt $l): Set {
+            // ftv l = foldr Set.union ∅ (map ftv l)
+            return foldr(Set::union, Set::mempty(), map(ftv, $l));
+        },
+        // instance Types TypeEnv where
+        TypeEnv::class => function (Map $env): Set {
+            // ftv (TypeEnv env) = ftv (Map.elems env)
+            return ftv(Map::elems($env));
+        },
+    ], $t);
+}
+
+/**
+ * // apply :: Subst → a → a
+ *
+ * @param Subst $s
+ * @param Type|Scheme|Listt|TypeEnv ?$a
+ * @return mixed
+ * @throws \Widmogrod\Useful\PatternNotMatchedError
+ */
+function apply(Subst $s, $a = null)
+{
+    return curryN(2, function (Subst $s, $a) {
+        return match([
+            // instance Types Type where
+            TVar::class => function ($n) use ($s, $a) {
+                return match([
+                    Just::class => identity,        // Justt → t
+                    Nothing::class => constt($a),   // Nothing → TVar n
+                ], lookup($s, $n));
+            },
+            TFun::class => function (Type $t1, Type $t2) use ($s) {
+                return new TFun(apply($s, $t1), apply($s, $t2));
+            },
+            TBool::class => constt($a),
+            TInt::class => constt($a),
+            // instance Types Scheme where
+            Scheme::class => function (Listt $vars, Type $t) use ($s) {
+                // apply s (Scheme vars t) = Scheme vars (apply (foldr Map.delete s vars) t)
+                return new Scheme(
+                    $vars,
+                    apply(foldr(Map::delete, $s, $vars), $t)
+                );
+            },
+            // instance Types a ⇒ Types [a] where
+            Listt::class => function (Listt $l) use ($s) {
+                // apply s = map (apply s)
+                return map(apply($s), $l);
+            },
+            // instance Types TypeEnv where
+            TypeEnv::class => function (Map $env) use ($s) {
+                // apply s (TypeEnv env) = TypeEnv (Map.map (apply s) env)
+                return new TypeEnv(Map::map(apply($s), $env));
+            },
+        ], $a);
+    })(...func_get_args());
+}
+
+class FreeMonadTest extends \PHPUnit\Framework\TestCase
+{
+    /**
+     * @dataProvider provideExamples
+     */
+    public function test(Exp $expression)
+    {
+        $this->assertInstanceOf(Exp::class, $expression);
+    }
+
+    public function provideExamples()
+    {
+        return [
+            // e0 = ELet "id" (EAbs "x" (EVar "x")) (EVar "id")
+            'let id = (x -> x) in id)' => [
+                'expression' => new ELet(
+                    'id',
+                    new EAbs("x", new EVar("x")),
+                    new EVar("id")
+                ),
+            ],
+            // e1 = ELet "id" (EAbs "x" (EVar "x")) (EApp (EVar "id") (EVar "id"))
+            'let id = (x -> x) in id id' => [
+                'expression' => new ELet(
+                    'id',
+                    new EAbs(
+                        'x',
+                        new EVar('x')
+                    ),
+                    new EApp(
+                        new EVar('id'),
+                        new EVar('id')
+                    )
+                ),
+            ],
+            // e2 = ELet "id" (EAbs "x" (ELet "y" (EVar "x") (EVar "y"))) (EApp (EVar "id") (EVar "id"))
+            'let id = (x -> let y = x in y) in id id ' => [
+                'expression' => new ELet(
+                    'x',
+                    new EAbs(
+                        'x',
+                        new ELet(
+                            'y',
+                            new EVar('x'),
+                            new EVar('y')
+                        )
+                    ),
+                    new EApp(
+                        new EVar('id'),
+                        new EVar('id')
+                    )
+                ),
+            ],
+            // e3 = ELet "id" (EAbs "x" (ELet "y" (EVar "x") (EVar "y"))) (EApp (EApp (EVar "id") (EVar "id")) (ELit (LInt 2)))
+            'let id = (x -> let y = x in y) in ((id id) 2)' => [
+                'expression' => new ELet(
+                    'id',
+                    new EAbs(
+                        'x',
+                        new ELet(
+                            'y',
+                            new EVar('x'),
+                            new EVar('y')
+                        )
+                    ),
+                    new EApp(
+                        new EApp(
+                            new EVar('id'),
+                            new EVar('id')
+                        ),
+                        new ELit(new LInt(2))
+                    )
+                ),
+            ],
+            // e4 = ELet "id" (EAbs "x" (EApp (EVar "x") (EVar "x"))) (EVar "id")
+            'let id = (x -> (x x)) in id' => new ELet(
+                'id',
+                new EAbs(
+                    'x',
+                    new EApp(
+                        new EVar('x'),
+                        new EVar('x')
+                    )
+                ),
+                new EVar('id')
+            ),
+            // e5 = EAbs "m" (ELet "y" (EVar "m")
+            //       (ELet "x" (EApp (EVar "y") (ELit (LBool True)))
+            //          (EVar "x")))
+            '(m -> let y = m in let x = (y true) in x)' => [
+                'expression' => new EAbs(
+                    'm',
+                    new ELet(
+                        'y',
+                        new EVar('m'),
+                        new ELet(
+                            'x',
+                            new EApp(
+                                new EVar('y'),
+                                new ELit(new LBool(true))
+                            ),
+                            new EVar('x')
+                        )
+                    )
+                )
+            ],
+            // e6 = EApp (ELit (LInt 2)) (ELit (LInt 2))
+            '(2 2)' => [
+                'expression' => new EApp(
+                    new ELit(new LInt(2)),
+                    new ELit(new LInt(2))
+                ),
+            ],
+        ];
+    }
+}