Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Added a `:slots` meta flag for `deftype` to disable creation of `__slots__` on created types (#1241)
* Added support for f-strings (#922)
* Added the `aslice` macro to facilitate the use of Python style `array[start:stop:step]` slicing in Basilisp (#1248)
* Added the `IPending` interface which is implemented by delays, futures, and promises (#1260)

### Changed
* Removed implicit support for single-use iterables in sequences, and introduced `iterator-seq` to expliciltly handle them (#1192)
Expand Down
5 changes: 5 additions & 0 deletions src/basilisp/core.lpy
Original file line number Diff line number Diff line change
Expand Up @@ -1404,6 +1404,11 @@
[x]
(and (integer? x) (neg? x)))

(defn ^:inline promise?
"Return ``true`` if ``x`` is a promise."
[x]
(instance? basilisp.lang.promise/Promise x))

(defn ^:inline some?
"Return ``true`` if ``x`` is not ``nil``\\, otherwise ``false`` s."
[x]
Expand Down
12 changes: 5 additions & 7 deletions src/basilisp/lang/compiler/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1031,12 +1031,10 @@ def _def_ast( # pylint: disable=too-many-locals,too-many-statements
# where we directly set the Var meta for the running Basilisp instance
# this causes problems since we'll end up getting something like
# `(quote ([] [v]))` rather than simply `([] [v])`.
arglists_meta = def_meta.val_at(ARGLISTS_KW) # type: ignore
arglists_meta = def_meta.val_at(ARGLISTS_KW)
if isinstance(arglists_meta, llist.PersistentList):
assert arglists_meta.first == SpecialForm.QUOTE
var_meta = def_meta.update( # type: ignore
{ARGLISTS_KW: runtime.nth(arglists_meta, 1)}
)
var_meta = def_meta.update({ARGLISTS_KW: runtime.nth(arglists_meta, 1)})
else:
var_meta = def_meta

Expand All @@ -1055,7 +1053,7 @@ def _def_ast( # pylint: disable=too-many-locals,too-many-statements
var = Var.intern_unbound(
ns_sym,
bare_name,
dynamic=def_meta.val_at(SYM_DYNAMIC_META_KEY, False), # type: ignore
dynamic=def_meta.val_at(SYM_DYNAMIC_META_KEY, False),
meta=var_meta,
)

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

if tag_ast is not None and any(
arity.tag is not None for arity in init.arities
Expand Down Expand Up @@ -1113,7 +1111,7 @@ def _def_ast( # pylint: disable=too-many-locals,too-many-statements
# some-name
# "some value")
meta_ast = _analyze_form(
def_meta.update( # type: ignore
def_meta.update(
{
NAME_KW: llist.l(SpecialForm.QUOTE, bare_name),
NS_KW: llist.l(
Expand Down
4 changes: 2 additions & 2 deletions src/basilisp/lang/delay.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import attr

from basilisp.lang import atom as atom
from basilisp.lang.interfaces import IDeref
from basilisp.lang.interfaces import IDeref, IPending

T = TypeVar("T")

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


class Delay(IDeref[T]):
class Delay(IDeref[T], IPending):
__slots__ = ("_state",)

def __init__(self, f: Callable[[], T]) -> None:
Expand Down
4 changes: 2 additions & 2 deletions src/basilisp/lang/futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
import attr
from typing_extensions import ParamSpec

from basilisp.lang.interfaces import IBlockingDeref
from basilisp.lang.interfaces import IBlockingDeref, IPending

T = TypeVar("T")
P = ParamSpec("P")


@attr.frozen(eq=True, repr=False)
class Future(IBlockingDeref[T]):
class Future(IBlockingDeref[T], IPending):
_future: "_Future[T]"

def __repr__(self): # pragma: no cover
Expand Down
16 changes: 16 additions & 0 deletions src/basilisp/lang/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,22 @@ def val_at(self, k: K, default: Optional[V] = None) -> Optional[V]:
raise NotImplementedError()


class IPending(ABC):
"""``IPending`` types are types which may represent deferred computations such
as a future or promise.

.. seealso::

:lpy:fn:`realized?`, :lpy:fn:`delay`, :lpy:fn:`future`, :lpy:fn:`promise`"""

__slots__ = ()

@property
@abstractmethod
def is_realized(self) -> bool:
raise NotImplementedError()


class IPersistentCollection(ISeqable[T]):
"""``IPersistentCollection`` types support both fetching empty variants of an
existing persistent collection and creating a new collection with additional
Expand Down
4 changes: 2 additions & 2 deletions src/basilisp/lang/promise.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import threading
from typing import Optional, TypeVar

from basilisp.lang.interfaces import IBlockingDeref
from basilisp.lang.interfaces import IBlockingDeref, IPending

T = TypeVar("T")


class Promise(IBlockingDeref[T]):
class Promise(IBlockingDeref[T], IPending):
__slots__ = ("_condition", "_is_delivered", "_value")

def __init__(self) -> None:
Expand Down
7 changes: 2 additions & 5 deletions src/basilisp/lang/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ def _read_map(
k = process_key(k)
try:
if k in d:
raise ctx.syntax_error(f"Duplicate key '{k}' in map literal") # type: ignore[str-bytes-safe]
raise ctx.syntax_error(f"Duplicate key '{k}' in map literal")
except TypeError as e:
raise ctx.syntax_error("Map keys must be hashable") from e
else:
Expand Down Expand Up @@ -1622,10 +1622,7 @@ def _read_reader_conditional(ctx: ReaderContext) -> LispReaderForm:
reader_cond = _read_reader_conditional_preserving(ctx, is_splicing)
if ctx.should_process_reader_cond and not reader_cond.is_splicing:
form = _select_reader_conditional_branch(ctx, reader_cond)
return cast(
LispReaderForm,
COMMENT if form is ReaderConditional.FEATURE_NOT_PRESENT else form,
)
return COMMENT if form is ReaderConditional.FEATURE_NOT_PRESENT else form
else:
return reader_cond

Expand Down