Skip to content

Commit

Permalink
add method chaining for term construction
Browse files Browse the repository at this point in the history
  • Loading branch information
Deric-W committed Aug 23, 2022
1 parent 714ff3e commit 6e46997
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 5 deletions.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ python3 -m pip install lambda-calculus

(λy.(λx.(λy. + x y)) y 3) 4

### Nesting

```python
from lambda_calculus import Variable, Abstraction, Application

Expand All @@ -38,7 +40,7 @@ term = Abstraction("y", term)
term = Application(term, Variable("4"))
```

using utility methods:
### Utility Methods

```python
from lambda_calculus import Variable, Abstraction, Application
Expand All @@ -53,7 +55,23 @@ term = Abstraction("y", term)
term = Application(term, Variable.with_valid_name("4"))
```

evaluation:
### Method Chaining

```python
from lambda_calculus import Variable, Abstraction, Application

x = Variable.with_valid_name("x")
y = Variable.with_valid_name("y")

term = Variable("+") \
.apply_to(x, y) \
.abstract("x", "y") \
.apply_to(y, Variable("3")) \
.abstract("y") \
.apply_to(Variable("4"))
```

### Evaluation

```python
from lambda_calculus import Variable, Application
Expand Down
2 changes: 1 addition & 1 deletion lambda_calculus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from .terms import Variable, Abstraction, Application

__version__ = "1.10.2"
__version__ = "1.11.0"
__author__ = "Eric Niklas Wolf"
__email__ = "eric_niklas.wolf@mailbox.tu-dresden.de"
__all__ = (
Expand Down
10 changes: 9 additions & 1 deletion lambda_calculus/terms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ def accept(self, visitor: visitors.Visitor[T, V]) -> T:
"""accept a visitor by calling his corresponding method"""
raise NotImplementedError()

def abstract(self, *variables: V) -> Abstraction[V]:
"""create an Abstraction binding multiple variables"""
return Abstraction.curried(variables, self)

def apply_to(self, *arguments: Term[V]) -> Application[V]:
"""create an Application applying self to multiple arguments"""
return Application.with_arguments(self, arguments)

def substitute(self, variable: V, value: Term[V]) -> Term[V]:
"""substitute a free variable with a Term, possibly raising a CollisionError"""
return self.accept(substitution.SubstitutingVisitor(variable, value))
Expand All @@ -64,7 +72,7 @@ def is_combinator(self) -> bool:

@dataclass(unsafe_hash=True, slots=True)
class Variable(Term[V]):
"""Term consiting of a Variable"""
"""Term consisting of a Variable"""

name: V

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "lambda_calculus"
version = "1.10.2"
version = "1.11.0"
description = "Implementation of the Lambda calculus"
requires-python = ">=3.10"
keywords = []
Expand Down
70 changes: 70 additions & 0 deletions tests/test_readme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/python3

"""Tests for README examples"""

from unittest import TestCase
from lambda_calculus import Variable, Abstraction, Application
from lambda_calculus.terms import Term
from lambda_calculus.visitors.normalisation import BetaNormalisingVisitor


class ExampleTest(TestCase):
"""Test for README example"""

term: Application[str]

def setUp(self) -> None:
"""create a reference term"""
term: Term[str] = Application(Variable("+"), Variable("x"))
term = Application(term, Variable("y"))
term = Abstraction("y", term)
term = Abstraction("x", term)
term = Application(term, Variable("y"))
term = Application(term, Variable("3"))
term = Abstraction("y", term)
self.term = Application(term, Variable("4"))

def test_nesting(self) -> None:
"""test nesting example"""
term: Term[str] = Application(Variable("+"), Variable("x"))
term = Application(term, Variable("y"))
term = Abstraction("y", term)
term = Abstraction("x", term)
term = Application(term, Variable("y"))
term = Application(term, Variable("3"))
term = Abstraction("y", term)
term = Application(term, Variable("4"))
self.assertEqual(term, self.term)

def test_utility_methods(self) -> None:
"""test utility method example"""
x = Variable.with_valid_name("x")
y = Variable.with_valid_name("y")
term: Term[str] = Application.with_arguments(Variable.with_valid_name("+"), (x, y))
term = Abstraction.curried(("x", "y"), term)
term = Application.with_arguments(term, (y, Variable.with_valid_name("3")))
term = Abstraction("y", term)
term = Application(term, Variable.with_valid_name("4"))
self.assertEqual(term, self.term)

def test_method_chaining(self) -> None:
"""test method chaining example"""
x = Variable.with_valid_name("x")
y = Variable.with_valid_name("y")
term = Variable("+") \
.apply_to(x, y) \
.abstract("x", "y") \
.apply_to(y, Variable("3")) \
.abstract("y") \
.apply_to(Variable("4"))
self.assertEqual(term, self.term)

def test_evaluation(self) -> None:
"""test evaluation example"""
self.assertEqual(
BetaNormalisingVisitor().skip_intermediate(self.term),
Application.with_arguments(
Variable("+"),
(Variable("4"), Variable("3"))
)
)

0 comments on commit 6e46997

Please sign in to comment.