|
| 1 | +import threading |
1 | 2 | from typing import Any, Callable, Generic, Optional, TypeVar
|
2 | 3 |
|
3 |
| -import basilisp.lang.atom as atom |
4 | 4 | import basilisp.lang.map as lmap
|
5 | 5 | import basilisp.lang.symbol as sym
|
| 6 | +from basilisp.lang.interfaces import IPersistentMap |
6 | 7 | from basilisp.util import Maybe
|
7 | 8 |
|
8 | 9 | T = TypeVar("T")
|
|
11 | 12 |
|
12 | 13 |
|
13 | 14 | class MultiFunction(Generic[T]):
|
14 |
| - __slots__ = ("_name", "_default", "_dispatch", "_methods") |
| 15 | + __slots__ = ("_name", "_default", "_dispatch", "_lock", "_methods") |
15 | 16 |
|
| 17 | + # pylint:disable=assigning-non-slot |
16 | 18 | def __init__(
|
17 | 19 | self, name: sym.Symbol, dispatch: DispatchFunction, default: T
|
18 | 20 | ) -> None:
|
19 |
| - self._name = name # pylint:disable=assigning-non-slot |
20 |
| - self._default = default # pylint:disable=assigning-non-slot |
21 |
| - self._dispatch = dispatch # pylint:disable=assigning-non-slot |
22 |
| - self._methods: atom.Atom = atom.Atom( # pylint:disable=assigning-non-slot |
23 |
| - lmap.Map.empty() |
24 |
| - ) |
| 21 | + self._name = name |
| 22 | + self._default = default |
| 23 | + self._dispatch = dispatch |
| 24 | + self._lock = threading.Lock() |
| 25 | + self._methods: IPersistentMap[T, Method] = lmap.Map.empty() |
25 | 26 |
|
26 | 27 | def __call__(self, *args, **kwargs):
|
27 | 28 | key = self._dispatch(*args, **kwargs)
|
28 |
| - method_cache = self.methods |
29 |
| - method: Optional[Method] = Maybe(method_cache.val_at(key, None)).or_else( |
30 |
| - lambda: method_cache.val_at(self._default, None) # type: ignore |
31 |
| - ) |
32 |
| - if method: |
| 29 | + method = self.get_method(key) |
| 30 | + if method is not None: |
33 | 31 | return method(*args, **kwargs)
|
34 | 32 | raise NotImplementedError
|
35 | 33 |
|
36 |
| - @staticmethod |
37 |
| - def __add_method(m: lmap.Map, key: T, method: Method) -> lmap.Map: |
38 |
| - """Swap the methods atom to include method with key.""" |
39 |
| - return m.assoc(key, method) |
40 |
| - |
41 | 34 | def add_method(self, key: T, method: Method) -> None:
|
42 | 35 | """Add a new method to this function which will respond for
|
43 | 36 | key returned from the dispatch function."""
|
44 |
| - self._methods.swap(MultiFunction.__add_method, key, method) |
| 37 | + with self._lock: |
| 38 | + self._methods = self._methods.assoc(key, method) |
45 | 39 |
|
46 | 40 | def get_method(self, key: T) -> Optional[Method]:
|
47 | 41 | """Return the method which would handle this dispatch key or
|
48 | 42 | None if no method defined for this key and no default."""
|
49 |
| - method_cache = self.methods |
| 43 | + method_cache = self._methods |
50 | 44 | # The 'type: ignore' comment below silences a spurious MyPy error
|
51 | 45 | # about having a return statement in a method which does not return.
|
52 | 46 | return Maybe(method_cache.val_at(key, None)).or_else(
|
53 | 47 | lambda: method_cache.val_at(self._default, None) # type: ignore
|
54 | 48 | )
|
55 | 49 |
|
56 |
| - @staticmethod |
57 |
| - def __remove_method(m: lmap.Map, key: T) -> lmap.Map: |
58 |
| - """Swap the methods atom to remove method with key.""" |
59 |
| - return m.dissoc(key) |
60 |
| - |
61 | 50 | def remove_method(self, key: T) -> Optional[Method]:
|
62 | 51 | """Remove the method defined for this key and return it."""
|
63 |
| - method = self.methods.val_at(key, None) |
64 |
| - if method: |
65 |
| - self._methods.swap(MultiFunction.__remove_method, key) |
66 |
| - return method |
| 52 | + with self._lock: |
| 53 | + method = self._methods.val_at(key, None) |
| 54 | + if method: |
| 55 | + self._methods = self._methods.dissoc(key) |
| 56 | + return method |
67 | 57 |
|
68 | 58 | def remove_all_methods(self) -> None:
|
69 | 59 | """Remove all methods defined for this multi-function."""
|
70 |
| - self._methods.reset(lmap.Map.empty()) |
| 60 | + with self._lock: |
| 61 | + self._methods = lmap.Map.empty() |
71 | 62 |
|
72 | 63 | @property
|
73 | 64 | def default(self) -> T:
|
74 | 65 | return self._default
|
75 | 66 |
|
76 | 67 | @property
|
77 |
| - def methods(self) -> lmap.Map: |
78 |
| - return self._methods.deref() |
| 68 | + def methods(self) -> IPersistentMap[T, Method]: |
| 69 | + return self._methods |
79 | 70 |
|
80 | 71 |
|
81 | 72 | def multifn(dispatch: DispatchFunction, default=None) -> MultiFunction[T]:
|
|
0 commit comments