|
| 1 | +from characteristic import attributes |
| 2 | + |
| 3 | +from ._base import Effect |
| 4 | +from ._dispatcher import TypeDispatcher |
| 5 | +from ._sync import sync_performer |
| 6 | + |
| 7 | + |
| 8 | +class Reference(object): |
| 9 | + """ |
| 10 | + An effectful mutable variable, suitable for sharing between multiple |
| 11 | + logical threads of execution, that can be read and modified in a purely |
| 12 | + functional way. |
| 13 | +
|
| 14 | + Compare to Haskell's ``IORef`` or Clojure's ``atom``. |
| 15 | + """ |
| 16 | + |
| 17 | + # TODO: Add modify_atomic that either uses a lock or a low-level |
| 18 | + # compare-and-set operation. |
| 19 | + |
| 20 | + def __init__(self, initial): |
| 21 | + self._value = initial |
| 22 | + |
| 23 | + def read(self): |
| 24 | + """Return an Effect that results in the current value.""" |
| 25 | + return Effect(ReadReference(ref=self)) |
| 26 | + |
| 27 | + def modify(self, transformer): |
| 28 | + """ |
| 29 | + Return an Effect that updates the value with ``fn(old_value)``. |
| 30 | +
|
| 31 | + This is not guaranteed to be linearizable if multiple threads are |
| 32 | + modifying the reference at the same time. |
| 33 | + """ |
| 34 | + return Effect(ModifyReference(ref=self, transformer=transformer)) |
| 35 | + |
| 36 | + |
| 37 | +@attributes(['ref']) |
| 38 | +class ReadReference(object): |
| 39 | + """Intent that gets a Reference's current value.""" |
| 40 | + |
| 41 | + |
| 42 | +@attributes(['ref', 'transformer']) |
| 43 | +class ModifyReference(object): |
| 44 | + """ |
| 45 | + Intent that modifies a Reference value in-place with a transformer func. |
| 46 | +
|
| 47 | + This intent is not necessarily linearizable if multiple threads are |
| 48 | + modifying the same reference at the same time. |
| 49 | + """ |
| 50 | + |
| 51 | + |
| 52 | +@sync_performer |
| 53 | +def perform_read_reference(dispatcher, intent): |
| 54 | + """Performer for :obj:`ReadReference`.""" |
| 55 | + return intent.ref._value |
| 56 | + |
| 57 | + |
| 58 | +@sync_performer |
| 59 | +def perform_modify_reference(dispatcher, intent): |
| 60 | + """ |
| 61 | + Performer for :obj:`ModifyReference`. |
| 62 | +
|
| 63 | + This performer is not linearizable if multiple physical threads are |
| 64 | + modifying the same reference at the same time. |
| 65 | + """ |
| 66 | + new_value = intent.transformer(intent.ref._value) |
| 67 | + intent.ref._value = new_value |
| 68 | + return new_value |
| 69 | + |
| 70 | + |
| 71 | +reference_dispatcher = TypeDispatcher({ |
| 72 | + ReadReference: perform_read_reference, |
| 73 | + ModifyReference: perform_modify_reference}) |
0 commit comments