Skip to content

Commit 921846a

Browse files
authored
Add IPending interface for delayed computations (#1261)
Fixes #1260
1 parent 48bb54f commit 921846a

File tree

8 files changed

+35
-18
lines changed

8 files changed

+35
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
* Added a `:slots` meta flag for `deftype` to disable creation of `__slots__` on created types (#1241)
1313
* Added support for f-strings (#922)
1414
* Added the `aslice` macro to facilitate the use of Python style `array[start:stop:step]` slicing in Basilisp (#1248)
15+
* Added the `IPending` interface which is implemented by delays, futures, and promises (#1260)
1516

1617
### Changed
1718
* Removed implicit support for single-use iterables in sequences, and introduced `iterator-seq` to expliciltly handle them (#1192)

src/basilisp/core.lpy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,6 +1404,11 @@
14041404
[x]
14051405
(and (integer? x) (neg? x)))
14061406

1407+
(defn ^:inline promise?
1408+
"Return ``true`` if ``x`` is a promise."
1409+
[x]
1410+
(instance? basilisp.lang.promise/Promise x))
1411+
14071412
(defn ^:inline some?
14081413
"Return ``true`` if ``x`` is not ``nil``\\, otherwise ``false`` s."
14091414
[x]

src/basilisp/lang/compiler/analyzer.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,12 +1031,10 @@ def _def_ast( # pylint: disable=too-many-locals,too-many-statements
10311031
# where we directly set the Var meta for the running Basilisp instance
10321032
# this causes problems since we'll end up getting something like
10331033
# `(quote ([] [v]))` rather than simply `([] [v])`.
1034-
arglists_meta = def_meta.val_at(ARGLISTS_KW) # type: ignore
1034+
arglists_meta = def_meta.val_at(ARGLISTS_KW)
10351035
if isinstance(arglists_meta, llist.PersistentList):
10361036
assert arglists_meta.first == SpecialForm.QUOTE
1037-
var_meta = def_meta.update( # type: ignore
1038-
{ARGLISTS_KW: runtime.nth(arglists_meta, 1)}
1039-
)
1037+
var_meta = def_meta.update({ARGLISTS_KW: runtime.nth(arglists_meta, 1)})
10401038
else:
10411039
var_meta = def_meta
10421040

@@ -1055,7 +1053,7 @@ def _def_ast( # pylint: disable=too-many-locals,too-many-statements
10551053
var = Var.intern_unbound(
10561054
ns_sym,
10571055
bare_name,
1058-
dynamic=def_meta.val_at(SYM_DYNAMIC_META_KEY, False), # type: ignore
1056+
dynamic=def_meta.val_at(SYM_DYNAMIC_META_KEY, False),
10591057
meta=var_meta,
10601058
)
10611059

@@ -1076,7 +1074,7 @@ def _def_ast( # pylint: disable=too-many-locals,too-many-statements
10761074
"generated inline function"
10771075
)
10781076
var.alter_meta(lambda m: m.assoc(SYM_INLINE_META_KW, init.inline_fn)) # type: ignore[misc]
1079-
def_meta = def_meta.assoc(SYM_INLINE_META_KW, init.inline_fn.form) # type: ignore[union-attr]
1077+
def_meta = def_meta.assoc(SYM_INLINE_META_KW, init.inline_fn.form)
10801078

10811079
if tag_ast is not None and any(
10821080
arity.tag is not None for arity in init.arities
@@ -1113,7 +1111,7 @@ def _def_ast( # pylint: disable=too-many-locals,too-many-statements
11131111
# some-name
11141112
# "some value")
11151113
meta_ast = _analyze_form(
1116-
def_meta.update( # type: ignore
1114+
def_meta.update(
11171115
{
11181116
NAME_KW: llist.l(SpecialForm.QUOTE, bare_name),
11191117
NS_KW: llist.l(

src/basilisp/lang/delay.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import attr
44

55
from basilisp.lang import atom as atom
6-
from basilisp.lang.interfaces import IDeref
6+
from basilisp.lang.interfaces import IDeref, IPending
77

88
T = TypeVar("T")
99

@@ -15,7 +15,7 @@ class _DelayState(Generic[T]):
1515
computed: bool = False
1616

1717

18-
class Delay(IDeref[T]):
18+
class Delay(IDeref[T], IPending):
1919
__slots__ = ("_state",)
2020

2121
def __init__(self, f: Callable[[], T]) -> None:

src/basilisp/lang/futures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
import attr
88
from typing_extensions import ParamSpec
99

10-
from basilisp.lang.interfaces import IBlockingDeref
10+
from basilisp.lang.interfaces import IBlockingDeref, IPending
1111

1212
T = TypeVar("T")
1313
P = ParamSpec("P")
1414

1515

1616
@attr.frozen(eq=True, repr=False)
17-
class Future(IBlockingDeref[T]):
17+
class Future(IBlockingDeref[T], IPending):
1818
_future: "_Future[T]"
1919

2020
def __repr__(self): # pragma: no cover

src/basilisp/lang/interfaces.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,22 @@ def val_at(self, k: K, default: Optional[V] = None) -> Optional[V]:
308308
raise NotImplementedError()
309309

310310

311+
class IPending(ABC):
312+
"""``IPending`` types are types which may represent deferred computations such
313+
as a future or promise.
314+
315+
.. seealso::
316+
317+
:lpy:fn:`realized?`, :lpy:fn:`delay`, :lpy:fn:`future`, :lpy:fn:`promise`"""
318+
319+
__slots__ = ()
320+
321+
@property
322+
@abstractmethod
323+
def is_realized(self) -> bool:
324+
raise NotImplementedError()
325+
326+
311327
class IPersistentCollection(ISeqable[T]):
312328
"""``IPersistentCollection`` types support both fetching empty variants of an
313329
existing persistent collection and creating a new collection with additional

src/basilisp/lang/promise.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import threading
22
from typing import Optional, TypeVar
33

4-
from basilisp.lang.interfaces import IBlockingDeref
4+
from basilisp.lang.interfaces import IBlockingDeref, IPending
55

66
T = TypeVar("T")
77

88

9-
class Promise(IBlockingDeref[T]):
9+
class Promise(IBlockingDeref[T], IPending):
1010
__slots__ = ("_condition", "_is_delivered", "_value")
1111

1212
def __init__(self) -> None:

src/basilisp/lang/reader.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ def _read_map(
812812
k = process_key(k)
813813
try:
814814
if k in d:
815-
raise ctx.syntax_error(f"Duplicate key '{k}' in map literal") # type: ignore[str-bytes-safe]
815+
raise ctx.syntax_error(f"Duplicate key '{k}' in map literal")
816816
except TypeError as e:
817817
raise ctx.syntax_error("Map keys must be hashable") from e
818818
else:
@@ -1622,10 +1622,7 @@ def _read_reader_conditional(ctx: ReaderContext) -> LispReaderForm:
16221622
reader_cond = _read_reader_conditional_preserving(ctx, is_splicing)
16231623
if ctx.should_process_reader_cond and not reader_cond.is_splicing:
16241624
form = _select_reader_conditional_branch(ctx, reader_cond)
1625-
return cast(
1626-
LispReaderForm,
1627-
COMMENT if form is ReaderConditional.FEATURE_NOT_PRESENT else form,
1628-
)
1625+
return COMMENT if form is ReaderConditional.FEATURE_NOT_PRESENT else form
16291626
else:
16301627
return reader_cond
16311628

0 commit comments

Comments
 (0)