From b229eabe8e7fc32102e94034bed6b61d5d969dbf Mon Sep 17 00:00:00 2001 From: Eric Wolf Date: Tue, 4 Oct 2022 18:02:55 +0200 Subject: [PATCH 1/5] increment major version number --- lambda_calculus/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lambda_calculus/__init__.py b/lambda_calculus/__init__.py index d61113e..b43a28a 100644 --- a/lambda_calculus/__init__.py +++ b/lambda_calculus/__init__.py @@ -4,7 +4,7 @@ from .terms import Variable, Abstraction, Application -__version__ = "2.2.3" +__version__ = "3.0.0" __author__ = "Eric Niklas Wolf" __email__ = "eric_niklas.wolf@mailbox.tu-dresden.de" __all__ = ( diff --git a/pyproject.toml b/pyproject.toml index d51a855..e50d577 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "lambda_calculus" -version = "2.2.3" +version = "3.0.0" description = "Implementation of the Lambda calculus" requires-python = ">=3.10" keywords = [] From 1ba2ad9608728fc671b93e27cc9f9d9ed3895689 Mon Sep 17 00:00:00 2001 From: Eric Wolf Date: Tue, 4 Oct 2022 18:03:22 +0200 Subject: [PATCH 2/5] remove unused _free_variables from UnsafeSubstitution --- lambda_calculus/visitors/substitution/unsafe.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lambda_calculus/visitors/substitution/unsafe.py b/lambda_calculus/visitors/substitution/unsafe.py index 1821882..e7b6e77 100644 --- a/lambda_calculus/visitors/substitution/unsafe.py +++ b/lambda_calculus/visitors/substitution/unsafe.py @@ -3,7 +3,6 @@ """Substitutions which dont check if the substitutions are valid""" from __future__ import annotations -from collections.abc import Set from typing import TypeVar, final from ... import terms from . import DeferrableSubstitution @@ -28,18 +27,14 @@ class UnsafeSubstitution(DeferrableSubstitution[V]): value: terms.Term[V] - free_variables: Set[V] - __slots__ = ( "variable", - "value", - "free_variables" + "value" ) - def __init__(self, variable: V, value: terms.Term[V], free_variables: Set[V]) -> None: + def __init__(self, variable: V, value: terms.Term[V]) -> None: self.variable = variable self.value = value - self.free_variables = free_variables @classmethod def from_substitution(cls, variable: V, value: terms.Term[V]) -> UnsafeSubstitution[V]: @@ -50,7 +45,7 @@ def from_substitution(cls, variable: V, value: terms.Term[V]) -> UnsafeSubstitut :param value: value which should be substituted :return: new instance """ - return cls(variable, value, value.free_variables()) + return cls(variable, value) def visit_variable(self, variable: terms.Variable[V]) -> terms.Term[V]: """ From 4f9dd53bee675203e673df9e4dc853550961965f Mon Sep 17 00:00:00 2001 From: Eric Wolf Date: Tue, 4 Oct 2022 18:17:10 +0200 Subject: [PATCH 3/5] mark Term subclasses as final --- lambda_calculus/terms/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lambda_calculus/terms/__init__.py b/lambda_calculus/terms/__init__.py index b9b9df6..f309453 100644 --- a/lambda_calculus/terms/__init__.py +++ b/lambda_calculus/terms/__init__.py @@ -6,7 +6,7 @@ from abc import abstractmethod from collections.abc import Sequence, Set, Iterable, Iterator from dataclasses import dataclass -from typing import TypeVar +from typing import TypeVar, final from .. import visitors from ..errors import CollisionError from ..visitors import walking @@ -128,6 +128,7 @@ def is_combinator(self) -> bool: return not self.free_variables() +@final @dataclass(unsafe_hash=True, slots=True) class Variable(Term[V]): """ @@ -197,6 +198,7 @@ def accept(self, visitor: visitors.Visitor[T, V]) -> T: return visitor.visit_variable(self) +@final @dataclass(unsafe_hash=True, slots=True) class Abstraction(Term[V]): """ @@ -316,6 +318,7 @@ def replace(self, *, bound: V | None = None, body: Term[V] | None = None) -> Abs ) +@final @dataclass(unsafe_hash=True, slots=True) class Application(Term[V]): """ From b69e1ff44100c0bcfce2eca01e96039e3e90e4f4 Mon Sep 17 00:00:00 2001 From: Eric Wolf Date: Tue, 4 Oct 2022 18:18:09 +0200 Subject: [PATCH 4/5] mark predefined terms as final --- lambda_calculus/terms/arithmetic.py | 15 ++++++++------- lambda_calculus/terms/combinators.py | 19 ++++++++++--------- lambda_calculus/terms/logic.py | 13 +++++++------ lambda_calculus/terms/pairs.py | 11 ++++++----- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/lambda_calculus/terms/arithmetic.py b/lambda_calculus/terms/arithmetic.py index 13d830b..7fb4ae9 100644 --- a/lambda_calculus/terms/arithmetic.py +++ b/lambda_calculus/terms/arithmetic.py @@ -2,6 +2,7 @@ """Implementations of natural numbers and arithmetic operators""" +from typing import Final from . import Term, Variable, Abstraction, Application from .logic import TRUE, FALSE @@ -16,13 +17,13 @@ "number" ) -ISZERO = Variable("n").apply_to(FALSE.abstract("x"), TRUE).abstract("n") +ISZERO: Final = Variable("n").apply_to(FALSE.abstract("x"), TRUE).abstract("n") """ Term which evaluates to :const:`lambda_calculus.terms.logic.TRUE` if its argument is zero, :const:`lambda_calculus.terms.logic.FALSE` otherwise """ -SUCCESSOR = Abstraction.curried( +SUCCESSOR: Final = Abstraction.curried( ("n", "f", "x"), Application( Variable("f"), @@ -36,7 +37,7 @@ Term evaluating to its argument incremented by one. """ -PREDECESSOR = Abstraction.curried( +PREDECESSOR: Final = Abstraction.curried( ("n", "f", "x"), Application.with_arguments( Variable("n"), @@ -60,7 +61,7 @@ Term ealuating to its argument decremented by one. """ -ADD = Abstraction.curried( +ADD: Final = Abstraction.curried( ("m", "n", "f", "x"), Application.with_arguments( Variable("m"), @@ -77,7 +78,7 @@ Term evaluating to the sum of its two arguments. """ -SUBTRACT = Abstraction.curried( +SUBTRACT: Final = Abstraction.curried( ("m", "n"), Application.with_arguments( Variable("n"), @@ -88,7 +89,7 @@ Term evaluating to the difference of its two arguments. """ -MULTIPLY = Abstraction.curried( +MULTIPLY: Final = Abstraction.curried( ("m", "n", "f"), Application( Variable("m"), @@ -102,7 +103,7 @@ Term evaluating to the product of its two arguments. """ -POWER = Abstraction.curried( +POWER: Final = Abstraction.curried( ("b", "e"), Application( Variable("e"), diff --git a/lambda_calculus/terms/combinators.py b/lambda_calculus/terms/combinators.py index 0680aca..60edab4 100644 --- a/lambda_calculus/terms/combinators.py +++ b/lambda_calculus/terms/combinators.py @@ -2,6 +2,7 @@ """Common combinators""" +from typing import Final from . import Variable, Application __all__ = ( @@ -16,7 +17,7 @@ "OMEGA" ) -Y = Application( +Y: Final = Application( Variable("g").apply_to( Variable("x").apply_to(Variable("x")) ).abstract("x"), @@ -28,7 +29,7 @@ Y combinator used to define recursive terms. """ -S = Variable("x").apply_to( +S: Final = Variable("x").apply_to( Variable("z"), Variable("y").apply_to(Variable("z")) ).abstract("x", "y", "z") @@ -36,24 +37,24 @@ S combinator of the SKI combinator calculus. """ -K = Variable("x").abstract("x", "y") +K: Final = Variable("x").abstract("x", "y") """ K combinator of the SKI combinator calculus. """ -I = Variable("x").abstract("x") +I: Final = Variable("x").abstract("x") """ I combinator of the SKI combinator calculus. """ -B = Variable("x").apply_to( +B: Final = Variable("x").apply_to( Variable("y").apply_to(Variable("z")) ).abstract("x", "y", "z") """ B combinator of the BCKW combinator calculus. """ -C = Variable("x").apply_to( +C: Final = Variable("x").apply_to( Variable("z"), Variable("y") ).abstract("x", "y", "z") @@ -61,7 +62,7 @@ C combinator of the BCKW combinator calculus. """ -W = Variable("x").apply_to( +W: Final = Variable("x").apply_to( Variable("y"), Variable("y") ).abstract("x", "y") @@ -69,14 +70,14 @@ W combinator of the BCKW combinator calculus. """ -DELTA = Variable("x").apply_to( +DELTA: Final = Variable("x").apply_to( Variable("x") ).abstract("x") """ Term applying its argument to itself. """ -OMEGA = DELTA.apply_to(DELTA) +OMEGA: Final = DELTA.apply_to(DELTA) """ Smallest term with no beta normal form. """ diff --git a/lambda_calculus/terms/logic.py b/lambda_calculus/terms/logic.py index 7bfeddb..b38e8bf 100644 --- a/lambda_calculus/terms/logic.py +++ b/lambda_calculus/terms/logic.py @@ -2,6 +2,7 @@ """Implementations of boolean values and logical operators""" +from typing import Final from . import Variable, Abstraction, Application __all__ = ( @@ -13,17 +14,17 @@ "IF_THEN_ELSE" ) -TRUE = Abstraction.curried(("x", "y"), Variable("x")) +TRUE: Final = Abstraction.curried(("x", "y"), Variable("x")) """ Term representing True. """ -FALSE = Abstraction.curried(("x", "y"), Variable("y")) +FALSE: Final = Abstraction.curried(("x", "y"), Variable("y")) """ Term representing False """ -AND = Abstraction.curried( +AND: Final = Abstraction.curried( ("p", "q"), Application.with_arguments(Variable("p"), (Variable("q"), Variable("p"))) ) @@ -31,7 +32,7 @@ Term implementing logical conjunction between its two arguments. """ -OR = Abstraction.curried( +OR: Final = Abstraction.curried( ("p", "q"), Application.with_arguments(Variable("p"), (Variable("p"), Variable("q"))) ) @@ -39,12 +40,12 @@ Term implementing logical disjunction between its two arguments. """ -NOT = Abstraction("p", Application.with_arguments(Variable("p"), (FALSE, TRUE))) +NOT: Final = Abstraction("p", Application.with_arguments(Variable("p"), (FALSE, TRUE))) """ Term performing logical negation of its argument. """ -IF_THEN_ELSE = Abstraction.curried( +IF_THEN_ELSE: Final = Abstraction.curried( ("p", "a", "b"), Application.with_arguments(Variable("p"), (Variable("a"), Variable("b"))) ) diff --git a/lambda_calculus/terms/pairs.py b/lambda_calculus/terms/pairs.py index 78b965e..4eac9d2 100644 --- a/lambda_calculus/terms/pairs.py +++ b/lambda_calculus/terms/pairs.py @@ -2,6 +2,7 @@ """Implementation of pairs""" +from typing import Final from . import Variable, Abstraction, Application from .logic import TRUE, FALSE @@ -13,7 +14,7 @@ "NULL" ) -PAIR = Abstraction.curried( +PAIR: Final = Abstraction.curried( ("x", "y", "f"), Application.with_arguments( Variable("f"), @@ -24,22 +25,22 @@ Term evaluating to a ordered pair of its two arguments. """ -FIRST = Abstraction("p", Application(Variable("p"), TRUE)) +FIRST: Final = Abstraction("p", Application(Variable("p"), TRUE)) """ Term evaluating to the first value in its argument. """ -SECOND = Abstraction("p", Application(Variable("p"), FALSE)) +SECOND: Final = Abstraction("p", Application(Variable("p"), FALSE)) """ Term evaluating to the second value in its argument. """ -NIL = Abstraction("x", TRUE) +NIL: Final = Abstraction("x", TRUE) """ Special Term encoding an empty pair. """ -NULL = Abstraction( +NULL: Final = Abstraction( "p", Application( Variable("p"), From d587e3351a5d93b6463f58ec5bab1fd16c11b7a9 Mon Sep 17 00:00:00 2001 From: Eric Wolf Date: Tue, 4 Oct 2022 18:21:00 +0200 Subject: [PATCH 5/5] mark DepthFirstVisitor as final and add slots --- lambda_calculus/visitors/walking.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lambda_calculus/visitors/walking.py b/lambda_calculus/visitors/walking.py index ac99738..90d164d 100644 --- a/lambda_calculus/visitors/walking.py +++ b/lambda_calculus/visitors/walking.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Iterator -from typing import TypeVar +from typing import TypeVar, final from .. import terms from . import BottomUpVisitor @@ -15,6 +15,7 @@ V = TypeVar("V") +@final class DepthFirstVisitor(BottomUpVisitor[Iterator["terms.Term[V]"], V]): """ Visitor yielding subterms depth first @@ -24,6 +25,8 @@ class DepthFirstVisitor(BottomUpVisitor[Iterator["terms.Term[V]"], V]): V: represents the type of variables used in terms """ + __slots__ = () + def visit_variable(self, variable: terms.Variable[V]) -> Iterator[terms.Term[V]]: """ Visit a Variable term.