Skip to content

Commit 6e46997

Browse files
committed
add method chaining for term construction
1 parent 714ff3e commit 6e46997

File tree

5 files changed

+101
-5
lines changed

5 files changed

+101
-5
lines changed

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ python3 -m pip install lambda-calculus
2525

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

28+
### Nesting
29+
2830
```python
2931
from lambda_calculus import Variable, Abstraction, Application
3032

@@ -38,7 +40,7 @@ term = Abstraction("y", term)
3840
term = Application(term, Variable("4"))
3941
```
4042

41-
using utility methods:
43+
### Utility Methods
4244

4345
```python
4446
from lambda_calculus import Variable, Abstraction, Application
@@ -53,7 +55,23 @@ term = Abstraction("y", term)
5355
term = Application(term, Variable.with_valid_name("4"))
5456
```
5557

56-
evaluation:
58+
### Method Chaining
59+
60+
```python
61+
from lambda_calculus import Variable, Abstraction, Application
62+
63+
x = Variable.with_valid_name("x")
64+
y = Variable.with_valid_name("y")
65+
66+
term = Variable("+") \
67+
.apply_to(x, y) \
68+
.abstract("x", "y") \
69+
.apply_to(y, Variable("3")) \
70+
.abstract("y") \
71+
.apply_to(Variable("4"))
72+
```
73+
74+
### Evaluation
5775

5876
```python
5977
from lambda_calculus import Variable, Application

lambda_calculus/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from .terms import Variable, Abstraction, Application
66

7-
__version__ = "1.10.2"
7+
__version__ = "1.11.0"
88
__author__ = "Eric Niklas Wolf"
99
__email__ = "[email protected]"
1010
__all__ = (

lambda_calculus/terms/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ def accept(self, visitor: visitors.Visitor[T, V]) -> T:
5353
"""accept a visitor by calling his corresponding method"""
5454
raise NotImplementedError()
5555

56+
def abstract(self, *variables: V) -> Abstraction[V]:
57+
"""create an Abstraction binding multiple variables"""
58+
return Abstraction.curried(variables, self)
59+
60+
def apply_to(self, *arguments: Term[V]) -> Application[V]:
61+
"""create an Application applying self to multiple arguments"""
62+
return Application.with_arguments(self, arguments)
63+
5664
def substitute(self, variable: V, value: Term[V]) -> Term[V]:
5765
"""substitute a free variable with a Term, possibly raising a CollisionError"""
5866
return self.accept(substitution.SubstitutingVisitor(variable, value))
@@ -64,7 +72,7 @@ def is_combinator(self) -> bool:
6472

6573
@dataclass(unsafe_hash=True, slots=True)
6674
class Variable(Term[V]):
67-
"""Term consiting of a Variable"""
75+
"""Term consisting of a Variable"""
6876

6977
name: V
7078

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "lambda_calculus"
3-
version = "1.10.2"
3+
version = "1.11.0"
44
description = "Implementation of the Lambda calculus"
55
requires-python = ">=3.10"
66
keywords = []

tests/test_readme.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/usr/bin/python3
2+
3+
"""Tests for README examples"""
4+
5+
from unittest import TestCase
6+
from lambda_calculus import Variable, Abstraction, Application
7+
from lambda_calculus.terms import Term
8+
from lambda_calculus.visitors.normalisation import BetaNormalisingVisitor
9+
10+
11+
class ExampleTest(TestCase):
12+
"""Test for README example"""
13+
14+
term: Application[str]
15+
16+
def setUp(self) -> None:
17+
"""create a reference term"""
18+
term: Term[str] = Application(Variable("+"), Variable("x"))
19+
term = Application(term, Variable("y"))
20+
term = Abstraction("y", term)
21+
term = Abstraction("x", term)
22+
term = Application(term, Variable("y"))
23+
term = Application(term, Variable("3"))
24+
term = Abstraction("y", term)
25+
self.term = Application(term, Variable("4"))
26+
27+
def test_nesting(self) -> None:
28+
"""test nesting example"""
29+
term: Term[str] = Application(Variable("+"), Variable("x"))
30+
term = Application(term, Variable("y"))
31+
term = Abstraction("y", term)
32+
term = Abstraction("x", term)
33+
term = Application(term, Variable("y"))
34+
term = Application(term, Variable("3"))
35+
term = Abstraction("y", term)
36+
term = Application(term, Variable("4"))
37+
self.assertEqual(term, self.term)
38+
39+
def test_utility_methods(self) -> None:
40+
"""test utility method example"""
41+
x = Variable.with_valid_name("x")
42+
y = Variable.with_valid_name("y")
43+
term: Term[str] = Application.with_arguments(Variable.with_valid_name("+"), (x, y))
44+
term = Abstraction.curried(("x", "y"), term)
45+
term = Application.with_arguments(term, (y, Variable.with_valid_name("3")))
46+
term = Abstraction("y", term)
47+
term = Application(term, Variable.with_valid_name("4"))
48+
self.assertEqual(term, self.term)
49+
50+
def test_method_chaining(self) -> None:
51+
"""test method chaining example"""
52+
x = Variable.with_valid_name("x")
53+
y = Variable.with_valid_name("y")
54+
term = Variable("+") \
55+
.apply_to(x, y) \
56+
.abstract("x", "y") \
57+
.apply_to(y, Variable("3")) \
58+
.abstract("y") \
59+
.apply_to(Variable("4"))
60+
self.assertEqual(term, self.term)
61+
62+
def test_evaluation(self) -> None:
63+
"""test evaluation example"""
64+
self.assertEqual(
65+
BetaNormalisingVisitor().skip_intermediate(self.term),
66+
Application.with_arguments(
67+
Variable("+"),
68+
(Variable("4"), Variable("3"))
69+
)
70+
)

0 commit comments

Comments
 (0)