Skip to content

Commit 9681c15

Browse files
authored
Merge pull request #33 from alexanderthiem/release
Fixed severe error in monoid.py -> mconcat leading to wrong results
2 parents e785cab + 508fe72 commit 9681c15

File tree

1 file changed

+68
-20
lines changed

1 file changed

+68
-20
lines changed

pymonad/monoid.py

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,20 @@
2626
2727
"""
2828

29-
from typing import Any, Generic, List, TypeVar, Union # pylint: disable=unused-import
29+
from typing import (
30+
Any,
31+
Generic,
32+
List,
33+
TypeVar,
34+
Union,
35+
Iterable,
36+
Self,
37+
) # pylint: disable=unused-import
3038

31-
T = TypeVar('T') # pylint: disable=invalid-name
32-
MonoidT = Union['Monoid[T]', '_MonoidIdentity']
39+
T = TypeVar("T") # pylint: disable=invalid-name
3340

34-
class Monoid(Generic[T]):
41+
42+
class Monoid[T]:
3543
"""Base class for Monoid instances.
3644
3745
To implement a monoid instance, create a sub-class of Monoid and
@@ -40,19 +48,31 @@ class Monoid(Generic[T]):
4048
4149
"""
4250

51+
@classmethod
52+
def wrap(cls, value: T) -> Self:
53+
if value == None:
54+
return cls.identity_element()
55+
return cls(value)
56+
4357
def __init__(self, value: T) -> None:
58+
if value == None:
59+
raise ValueError("None Objects not allowed in Monoids")
4460
self.value = value
4561

46-
def __add__(self: MonoidT, other: MonoidT) -> MonoidT:
62+
def __add__(self, other: Self | T) -> Self:
63+
if not isinstance(other, self.__class__):
64+
if isinstance(other, Monoid):
65+
raise ValueError("Incompatible Monoid")
66+
return self.addition_operation(self.__class__(other))
4767
return self.addition_operation(other)
4868

4969
def __eq__(
50-
self: Union['_MonoidIdentity', 'Monoid[T]'],
51-
other: Union['_MonoidIdentity', 'Monoid[T]']
70+
self: Union["_MonoidIdentity", "Monoid[T]"],
71+
other: Union["_MonoidIdentity", "Monoid[T]"],
5272
) -> bool:
5373
return self.value == other.value
5474

55-
def addition_operation(self: MonoidT, other: MonoidT) -> MonoidT:
75+
def addition_operation(self: Self, other: Self) -> Self:
5676
"""Defines how monoid values are added together.
5777
5878
addition_operation() method is automatically called by
@@ -70,36 +90,64 @@ def addition_operation(self: MonoidT, other: MonoidT) -> MonoidT:
7090
"""
7191
raise NotImplementedError
7292

73-
@staticmethod
74-
def identity_element() -> 'Monoid[Any]':
93+
@classmethod
94+
def identity_element[a: "Monoid"](cls: type[a]) -> a:
7595
"""Returns the identity value for the monoid type.
7696
77-
This method must be overridden in subclasses of Monoid.
97+
This method must be overridden in subclasses of Monoid
7898
7999
"""
80100
raise NotImplementedError
81101

82-
class _MonoidIdentity:
102+
103+
# class _MonoidIdentity[a : Monoid](a): #once Python Types has those features, this is what we want
104+
class _MonoidIdentity[T](Monoid[T]):
105+
superclass = Monoid
106+
83107
def __init__(self):
108+
found = False
109+
for i in type(self).__mro__:
110+
if (
111+
i != _MonoidIdentity
112+
and i != self.__class__
113+
and i != Monoid
114+
and i != Generic
115+
and i != object
116+
):
117+
self.superclass = i
118+
found = True
119+
break
120+
if not found and self.__class__ != _MonoidIdentity:
121+
raise Exception("no superclass found")
84122
self.value = None
85123

86-
def __add__(self, other):
124+
def __add__(self: Self, other: Monoid[T] | T):
125+
if not isinstance(other, Monoid):
126+
return self.superclass(other)
87127
return other
88128

89-
def __radd__(self, other):
129+
def __radd__(self, other: Self):
130+
if not isinstance(other, Monoid):
131+
return self.superclass(other)
90132
return other
91133

92134
def __repr__(self):
93-
return 'IDENTITY'
135+
return "IDENTITY"
136+
94137

95138
IDENTITY = _MonoidIdentity()
96139

97-
def mconcat(monoid_list: List[MonoidT]) -> MonoidT:
140+
141+
# mconcat([Monoida, Monoidb]) not throws an error :nice
142+
def mconcat[a: Monoid](monoid_list: Iterable[a]) -> a:
98143
"""Takes a list of monoid values and reduces them to a single value
99144
by applying the '+' operation to all elements of the list.
100-
145+
Needs a non empty list, because Python doesn't allow calling on types
101146
"""
102-
result = monoid_list[0]
103-
for value in monoid_list:
104-
result += value
147+
it = monoid_list.__iter__()
148+
149+
# a.identity()
150+
result = it.__next__()
151+
for value in it:
152+
result = result + value
105153
return result

0 commit comments

Comments
 (0)