Pracuję nad kompilatorem dla języka konkatenatywnego i chciałbym dodać obsługę wnioskowania typu. Rozumiem Hindleya-Milnera, ale nauczyłem się teorii typów, więc nie jestem pewien, jak ją dostosować. Czy następujący system jest dźwiękowy i można go w sposób zdecydowanie wywnioskować?
Termin jest literałem, kompozycją terminów, cytatem terminu lub prymitywem.
Wszystkie terminy oznaczają funkcje. Dla dwóch funkcji i e 2 , e 1 , to znaczy zestawienie oznacza skład odwrotny. Literały oznaczają funkcje niladyczne.
Terminy inne niż skład mają podstawowe zasady dotyczące typów:
Szczególnie nie ma zasad stosowania, ponieważ brakuje im języków konkatenatywnych.
Typ jest literałem, zmienną typu lub funkcją od stosów do stosów, gdzie stos jest zdefiniowany jako krotka zagnieżdżona z prawej strony. Wszystkie funkcje są domyślnie polimorficzne w odniesieniu do „reszty stosu”.
To pierwsza rzecz, która wydaje się podejrzana, ale nie wiem dokładnie, co jest z nią nie tak.
Aby pomóc czytelności i obniżyć nawiasach, będę zakładać, że w schematach typów. Użyję również dużej litery do zmiennej oznaczającej stos, a nie do pojedynczej wartości.
Istnieje sześć prymitywów. Pierwsze pięć jest dość nieszkodliwe. dup
przyjmuje najwyższą wartość i tworzy dwie jej kopie. swap
zmienia kolejność dwóch najwyższych wartości. pop
odrzuca najwyższą wartość. quote
przyjmuje wartość i tworzy cytat (funkcję), który ją zwraca. apply
stosuje ofertę do stosu.
Ostatni kombinator compose
powinien wziąć dwa cytaty i zwrócić rodzaj ich konkatenacji, czyli . W statycznie typowanym języku konkatenatywnymCattypjest bardzo prosty.compose
Jednak ten typ jest zbyt restrykcyjny: wymaga, aby produkcja pierwszej funkcji dokładnie odpowiadała zużyciu drugiej. W rzeczywistości musisz założyć różne typy, a następnie je zunifikować. Ale jak byś napisał ten typ?
Jeśli pozwolisz oznaczać różnicę dwóch typów, to myślę, że możesz napisać typ poprawnie.compose
Jest wciąż stosunkowo prosta: compose
wykonuje funkcję i jeden f 2 : D → E . Jego wynik zużywa B na szczycie zużycia f 2 niewyprodukowanego przez f 1 , i daje D na szczycie produkcji f 1 niewykorzystanego przez f 2 . Daje to regułę dla zwykłego składu.
Jednak nie wiem, czy ta hipotetyczna rzeczywistości coś odpowiada, i gonię ją w kółko na tyle długo, że myślę, że źle skręciłem. Czy może to być zwykła różnica krotek?
Is there something horribly broken about this that I’m not seeing, or am I on something like the right track? (I’ve probably quantified some of this stuff wrongly and would appreciate fixes in that area as well.)
compose
is too restrictive? I have the impression that this is fine like this. (e.g. the restrictiontwice
defined asdup compose apply
, which takes a quotation and applies it twice.[1 +] twice
is fine: you’re composing two functions of type[pop] twice
is not: ifcompose
without some circular definition.Odpowiedzi:
The following rank-2 type
Greek letters are used for the rest-of-the-stack variables for clarity only.
It expresses the constraints that the output stack of the first element on the stack needs to be the same as the input stack of the second element. Appropriately instantiating the variableB for the two actually arguments is the way of getting the constraints to work properly, rather than defining a new operation, as you propose in the question.
Type checking rank-2 types is undecidable in general, I believe, though some work has been done that gives good results in practice (for Haskell):
The type rule for composition is simply:
To get the type system to work in general, you need the following specialisation rule:
źródło
dup +
should have type+
has typedup +
, as that does not use compose, as you defined it above.[dup] [+] compose
. But I read