hissp.macros module#

[source]

Hissp’s bundled macros.

To help keep macro definitions and expansions manageable in complexity, these macros lack some of the extra features their equivalents have in Python or in other Lisps. These are not intended to be a standard library for general use, but do bring Hissp up to a basic standard of utility without adding dependencies, which may suffice in some cases.

Because of certain deliberate restrictions in design, there are no dependencies in their expansions either, meaning compiled Hissp code utilizing the bundled macros need not have Hissp installed to work, only the Python standard library. All helper code must therefore be inlined, resulting in larger expansions than might otherwise be necessary.

They also have no prerequisite initialization, beyond what is available in a standard Python module. For example, a _macro_ namespace need not be available for defmacro. It’s smart enough to check for the presence of _macro_ (at compile time) in its expansion context, and inline the initialization code when required.

As a convenience, the bundled macros are automatically made available unqualified in the Lissp REPL, but this does not apply to modules. A Hissp module with better alternatives need not use the bundled macros at all.

They also eschew any expansions of subexpressions to non-atomic Python code strings, relying only on the built-in special forms quote and lambda, which makes their expansions compatible with advanced rewriting macros that process the Hissp expansions of other macros. ([# is something of an exception, as one subexpression is written in Python to begin with.) Note that this need not apply to inlined helper functions that contain no user code. if-else is one such example, expanding to a lambda containing a Python conditional expression, immediately called with the subexpressions as arguments.

_macro_#

hissp.macros._macro_.OQzHASH_(e)#

O# ‘thunk’ Make e an anonymous function with no parameters.

See also: X#.

hissp.macros._macro_.QzAT_(*xs)#

@ ‘list of’ Mnemonic: @rray list.

Creates the list from each expression’s result. A :* unpacks the next argument.

#> (@ :* "AB" (math..sqrt 9) :* "XY" 2 1)
>>> # QzAT_
... (lambda *xs:[*xs])(
...   *('AB'),
...   __import__('math').sqrt(
...     (9)),
...   *('XY'),
...   (2),
...   (1))
['A', 'B', 3.0, 'X', 'Y', 2, 1]

See also: #, %.

hissp.macros._macro_.QzAT_QzHASH_(decoration, definition)#

@# ‘decorator’ applies decoration to a global and reassigns.

definition form must assign a global identified by its first arg. Expands to a define, meaning decorators can stack.

Decorator syntax is for global definitions, like define and deftype, and would work on any global definition macro that has the (unqualified) defined name as its first argument.

#> @##str.swapcase
#..@##str.title
#..(define spam 'spam) ; Unlike Python def, not always a function.
>>> # hissp.macros.._macro_.define
... __import__('builtins').globals().update(
...   spam=# hissp.macros.._macro_.progn
...        (lambda :(
...          # hissp.macros.._macro_.define
...          __import__('builtins').globals().update(
...            spam=# hissp.macros.._macro_.progn
...                 (lambda :(
...                   # define
...                   __import__('builtins').globals().update(
...                     spam='spam'),
...                   str.title(
...                     spam))[-1])()),
...          str.swapcase(
...            spam))[-1])())

#> spam
>>> spam
'sPAM'
hissp.macros._macro_.QzHASH_(*xs)#

# ‘set of’ Mnemonic: Hash (#) set.

Creates the set from each expression’s result. A :* unpacks the next argument.

#> (# 1 :* (@ 1 2 3) 4)                ;Set, with unpacking.
>>> # QzHASH_
... (lambda *xs:{*xs})(
...   (1),
...   *# QzAT_
...    (lambda *xs:[*xs])(
...      (1),
...      (2),
...      (3)),
...   (4))
{1, 2, 3, 4}

See also: @, %.

hissp.macros._macro_.QzLSQB_QzHASH_(e)#

[# ‘subscript’ Injection. Python’s subscription operator.

Creates a function from the Python expression e prepended with the argument and a [.

#> ([#1][::2] '(foo bar))
>>> (lambda _Qz5GEAOGSQz_G:(_Qz5GEAOGSQz_G[1][::2]))(
...   ('foo',
...    'bar',))
'br'

See also: get#, ->, slice, Subscriptions, Slicings.

hissp.macros._macro_.QzLT_QzLT_QzHASH_(comment)#

<<# ‘comment string’ reader macro.

Converts a block of line comments to a raw string. Roughly equivalent to 'hissp.reader..Comment.contents#.

#> <<#
#..;; You won't have to
#..;; escape the "quotes".
#..
>>> 'You won\'t have to\nescape the "quotes".'
'You won\'t have to\nescape the "quotes".'

See also: triple-quoted string.

hissp.macros._macro_.QzPCENT_(*kvs)#

% ‘dict of’. Mnemonic: dict of pairs (%).

Key-value pairs are implied by position. A :** mapping-unpacks the next argument.

#> (% 1 2  :** (dict : x 3  y 4)  5 6) ;Dict, with mapping unpacking.
>>> # QzPCENT_
... (lambda x0,x1,x3,x4,x5:{x0:x1,**x3,x4:x5})(
...   (1),
...   (2),
...   dict(
...     x=(3),
...     y=(4)),
...   (5),
...   (6))
{1: 2, 'x': 3, 'y': 4, 5: 6}

#> (%)
>>> # QzPCENT_
... {}
{}

See also: @, #.

hissp.macros._macro_.Qz_QzGT_(expr, *forms)#

-> ‘Thread-first’.

Converts a pipeline to function calls by recursively threading expressions as the first argument of the next form. Non-tuple forms (typically function identifiers) will be wrapped in a tuple. Can make chained method calls easier to read.

#> (.-> _macro_ : :* '(x (A b) (C d e)))
>>> _macro_.Qz_QzGT_(
...   *('x',
...     ('A',
...      'b',),
...     ('C',
...      'd',
...      'e',),))
('C', ('A', 'x', 'b'), 'd', 'e')

#> (-> 'a set (en#list 'bc) (en#tuple 'de))
>>> # Qz_QzGT_
... (lambda *_Qz6RFWTTVXz_xs:
...   tuple(
...     _Qz6RFWTTVXz_xs))(
...   (lambda *_Qz6RFWTTVXz_xs:
...     list(
...       _Qz6RFWTTVXz_xs))(
...     set(
...       'a'),
...     'bc'),
...   'de')
([{'a'}, 'bc'], 'de')

See also: -<>>, X#, get#.

hissp.macros._macro_.Qz_QzLT_QzGT_QzGT_(expr, *forms)#

-<>> ‘Thread-through’.

Converts a pipeline to function calls by recursively threading expressions into the next form at the first point indicated with :<>, or at the last if no :<> is found. Non-tuple forms (typically function identifiers) will be wrapped in a tuple first. Can replace partial application in some cases.

#> (.-<>> _macro_ : :* '(x Y (:<> A b) (C d e)))
>>> _macro_.Qz_QzLT_QzGT_QzGT_(
...   *('x',
...     'Y',
...     (':<>',
...      'A',
...      'b',),
...     ('C',
...      'd',
...      'e',),))
('C', 'd', 'e', (('Y', 'x'), 'A', 'b'))

#> (-<>> 'a set (en#list 'bc) (en#tuple 'de :<> 'fg :<>))
>>> # Qz_QzLT_QzGT_QzGT_
... (lambda *_Qz6RFWTTVXz_xs:
...   tuple(
...     _Qz6RFWTTVXz_xs))(
...   'de',
...   (lambda *_Qz6RFWTTVXz_xs:
...     list(
...       _Qz6RFWTTVXz_xs))(
...     'bc',
...     set(
...       'a')),
...   'fg',
...   ':<>')
('de', ['bc', {'a'}], 'fg', ':<>')

See also: ->.

hissp.macros._macro_.XQzHASH_(e)#

X# Anaphoric. Make e an anonymous function with parameter X.

Examples:

Convert macro to function.

#> (list (map X#(@ X) "abc")) ; en#list would also work here.
>>> list(
...   map(
...     (lambda X:
...       # QzAT_
...       (lambda *xs:[*xs])(
...         X)),
...     ('abc')))
[['a'], ['b'], ['c']]

Compact function definition using Python operators.

#> (define teen? X#|13<=X<20|)
>>> # define
... __import__('builtins').globals().update(
...   teenQzQUERY_=(lambda X:13<=X<20))

#> (teen? 12.5)
>>> teenQzQUERY_(
...   (12.5))
False

#> (teen? 19.5)
>>> teenQzQUERY_(
...   (19.5))
True

Get an attribute without calling it.

#> (X#X.upper "shout")
>>> (lambda X:X.upper)(
...   ('shout'))
<built-in method upper of str object at ...>

#> (_)
>>> _()
'SHOUT'

#> (define class-name X#X.__class__.__name__) ; Attributes chain.
>>> # define
... __import__('builtins').globals().update(
...   classQz_name=(lambda X:X.__class__.__name__))

#> (class-name object)
>>> classQz_name(
...   object)
'type'

#> (class-name "foo")
>>> classQz_name(
...   ('foo'))
'str'

See also: en#, O#, XY#, getattr, operator.attrgetter, lambda.

hissp.macros._macro_.XYQzHASH_(e)#

XY# Anaphoric. Make e an anonymous function with parameters X Y.

#> (functools..reduce XY#(op#concat Y X) 'abcd)
>>> __import__('functools').reduce(
...   (lambda X,Y:
...     __import__('operator').concat(
...       Y,
...       X)),
...   'abcd')
'dcba'

See also: X#, XYZ#.

hissp.macros._macro_.XYZQzHASH_(e)#

XYZ# Anaphoric. Make e an anonymous function with parameters X Y Z.

#> (XYZ#|X*Y == Z| : X math..pi  Y 2  Z math..tau)
>>> (lambda X,Y,Z:X*Y == Z)(
...   X=__import__('math').pi,
...   Y=(2),
...   Z=__import__('math').tau)
True

See also: XY#, XYZW#.

hissp.macros._macro_.XYZWQzHASH_(e)#

XYZW# Anaphoric. Make e an anonymous function with parameters X Y Z W.

#> (XYZW#|X[Y:Z:W]| "QuaoblcldefHg" -2 1 -2)
>>> (lambda X,Y,Z,W:X[Y:Z:W])(
...   ('QuaoblcldefHg'),
...   (-2),
...   (1),
...   (-2))
'Hello'

See also: XYZ#, en#, X#.

hissp.macros._macro_.alias(abbreviation, qualifier)#

Defines a reader macro abbreviation of a qualifier.

#> (hissp.._macro_.alias H: hissp.._macro_)
>>> # hissp.._macro_.alias
... # hissp.macros.._macro_.defmacro
... # hissp.macros.._macro_.let
... (lambda _Qz2D5FNHXZz_fn=(lambda _QzE4JATHEUz_attr,*_QzE4JATHEUz_args,**_QzE4JATHEUz_kwargs:(
...   'Aliases ``hissp.._macro_`` as ``HQzCOLON_#``.',
...   # hissp.macros.._macro_.ifQz_else
...   (lambda b,c,a:c()if b else a())(
...     _QzE4JATHEUz_args,
...     (lambda :
...       __import__('builtins').getattr(
...         __import__('hissp')._macro_,
...         ('{}{}').format(
...           _QzE4JATHEUz_attr,
...           'QzHASH_'))(
...         *_QzE4JATHEUz_args,
...         **_QzE4JATHEUz_kwargs)),
...     (lambda :
...       ('{}.{}').format(
...         'hissp.._macro_',
...         _QzE4JATHEUz_attr))))[-1]):(
...   __import__('builtins').setattr(
...     _Qz2D5FNHXZz_fn,
...     '__doc__',
...     'Aliases ``hissp.._macro_`` as ``HQzCOLON_#``.'),
...   __import__('builtins').setattr(
...     _Qz2D5FNHXZz_fn,
...     '__qualname__',
...     ('.').join(
...       ('_macro_',
...        'HQzCOLON_QzHASH_',))),
...   __import__('builtins').setattr(
...     __import__('operator').getitem(
...       __import__('builtins').globals(),
...       '_macro_'),
...     'HQzCOLON_QzHASH_',
...     _Qz2D5FNHXZz_fn))[-1])()

#> 'H:#alias
>>> 'hissp.._macro_.alias'
'hissp.._macro_.alias'

#> H:#b\#                              ;b# macro callable
>>> __import__('hissp')._macro_.bQzHASH_
<function _macro_.bQzHASH_ at ...>

#> (H:#b\# |b# macro at compile time|)
>>> # hissp.._macro_.bQzHASH_
... b'b# macro at compile time'
b'b# macro at compile time'

#> hissp.._macro_.b#|Fully-qualified b# macro at read time.|
>>> b'Fully-qualified b# macro at read time.'
b'Fully-qualified b# macro at read time.'

#> H:##b|Read-time b# via alias.|
>>> b'Read-time b# via alias.'
b'Read-time b# via alias.'

The bundled op# and i# reader macros are aliases for operator and itertools, respectively.

See also: prelude, attach.

hissp.macros._macro_.ands(*exprs)#

Variadic shortcutting logical AND.

Returns the first false value, otherwise the last value. There is an implicit initial value of True.

#> (ands True True False) ; and finds the False
>>> # ands
... (lambda x0,x1,x2:x0 and x1()and x2())(
...   True,
...   (lambda :True),
...   (lambda :False))
False

#> (ands False (print 'oops)) ; Shortcutting.
>>> # ands
... (lambda x0,x1:x0 and x1())(
...   False,
...   (lambda :
...     print(
...       'oops')))
False

#> (ands True 42)
>>> # ands
... (lambda x0,x1:x0 and x1())(
...   True,
...   (lambda :(42)))
42

#> (ands)
>>> # ands
... True
True

#> (ands 42)
>>> # ands
... (42)
42

See also: ors, Boolean operations, all, when.

hissp.macros._macro_.anyQzSTAR_map(variables, xss, *body)#

any*map ‘any star map’ Bind each x to a variable and evaluate the body for each xs from xss until any result is true (and return True), or until xss is exhausted (and return False).

#> (any*map (i c) (enumerate 'abc 1)  ;As any-map, but with starmap.
#..  (print (op#mul i c)))
>>> # anyQzSTAR_map
... __import__('builtins').any(
...   __import__('itertools').starmap(
...     (lambda i,c:
...       print(
...         __import__('operator').mul(
...           i,
...           c))),
...     enumerate(
...       'abc',
...       (1))))
a
bb
ccc
False

See also: itertools.starmap, any-map, loop-from.

hissp.macros._macro_.anyQz_map(variable, xs, *body)#

any-map Bind the variable and evaluate the body for each x from xs until any result is true (and return True), or until xs is exhausted (and return False).

#> (any-map index (range 1 11)         ;Imperative loop with break.
#..  (print index : end :)
#..  (not (op#mod index 7)))
>>> # anyQz_map
... __import__('builtins').any(
...   __import__('builtins').map(
...     (lambda index:(
...       print(
...         index,
...         end=':'),
...       not(
...         __import__('operator').mod(
...           index,
...           (7))))[-1]),
...     range(
...       (1),
...       (11))))
1:2:3:4:5:6:7:True

See also: any, map, any*map, The for statement, The break statement, functools.reduce.

hissp.macros._macro_.assure(e, predicate, *args)#

Anaphoric. Raises AssertionError unless (-> e predicate).

As avow, but expansion is simply e when __debug__ is off:

$ python -Om hissp -c "(print (assure 0 bool))"
0

$ lissp -c "(print (assure 0 bool))"
Hissp abort!
Traceback (most recent call last):
  ...
AssertionError

Note that for pre-compiled code, it’s the __debug__ state at compile time, not at run time, that determines if assure assertions are turned on.

For internal integrity checks, prefer avow to assure, unless profiling indicates the check is unacceptably expensive in production, and the risk of not checking is acceptable; assume __debug__ will later be turned off.

Also useful at the top level for quick unit tests in smaller projects, because they can be turned off. Larger projects may be better off with unittest and separated test modules, which need not be distributed and likely produce better error messages.

#> (assure 7 (X#|X%2 == 0|)
#..  it "That's odd.")
>>> # assure
... # hissp.macros.._macro_.avow
... # hissp.macros.._macro_.let
... (lambda it=(7):(
...   # hissp.macros.._macro_.unless
...   (lambda b,a:()if b else a())(
...     # hissp.macros.._macro_.Qz_QzGT_
...     (lambda X:X%2 == 0)(
...       it),
...     (lambda :
...       # hissp.macros.._macro_.throw
...       # hissp.macros.._macro_.throwQzSTAR_
...       (lambda g:g.close()or g.throw)(c for c in'')(
...         __import__('builtins').AssertionError(
...           it,
...           ("That's odd."))))),
...   it)[-1])()
Traceback (most recent call last):
  ...
AssertionError: (7, "That's odd.")

See also: The assert statement.

hissp.macros._macro_.attach(target, *args)#

Attaches the named variables to the target as attributes.

Positional arguments must be identifiers. The identifier name becomes the attribute name. Names after the : are identifier-value pairs. Returns the target.

#> (attach (types..SimpleNamespace) _macro_.attach : a 1  b 'Hi)
>>> # attach
... # hissp.macros.._macro_.let
... (lambda _QzWG5WN73Wz_target=__import__('types').SimpleNamespace():(
...   __import__('builtins').setattr(
...     _QzWG5WN73Wz_target,
...     'attach',
...     _macro_.attach),
...   __import__('builtins').setattr(
...     _QzWG5WN73Wz_target,
...     'a',
...     (1)),
...   __import__('builtins').setattr(
...     _QzWG5WN73Wz_target,
...     'b',
...     'Hi'),
...   _QzWG5WN73Wz_target)[-1])()
namespace(a=1, attach=<function _macro_.attach at 0x...>, b='Hi')

See also: setattr, set@, vars.

hissp.macros._macro_.avow(e, predicate, *args)#

Anaphoric. Raises AssertionError unless (-> e predicate).

Additional arguments are evaluated in a context where it refers to the result of e. These (if any) are passed to the AssertionError. Evaluates to the result of e.

Assertions document assumptions that should never be false; only raise AssertionErrors to fail fast when there is a bug in your code violating one, which can never happen if the code was written correctly. Though implemented as exceptions in Python, they should almost never be caught, except (perhaps) by a supervising system (such as a REPL) capable of dealing with broken subsystems. They are not to be used like normal exceptions to handle expected cases.

#> (avow 7 (X#|X%2 == 0|)
#..  it "That's odd.")
>>> # avow
... # hissp.macros.._macro_.let
... (lambda it=(7):(
...   # hissp.macros.._macro_.unless
...   (lambda b,a:()if b else a())(
...     # hissp.macros.._macro_.Qz_QzGT_
...     (lambda X:X%2 == 0)(
...       it),
...     (lambda :
...       # hissp.macros.._macro_.throw
...       # hissp.macros.._macro_.throwQzSTAR_
...       (lambda g:g.close()or g.throw)(c for c in'')(
...         __import__('builtins').AssertionError(
...           it,
...           ("That's odd."))))),
...   it)[-1])()
Traceback (most recent call last):
  ...
AssertionError: (7, "That's odd.")

See also: The assert statement, assure, throw.

hissp.macros._macro_.bQzHASH_(raw)#

b# bytes literal reader macro

#> b#|\xff'\n'||foo|
>>> b"\xff'\n'|foo"
b"\xff'\n'|foo"

#> b#.#"bytes
#..with\nnewlines"
>>> b'bytes\nwith\nnewlines'
b'bytes\nwith\nnewlines'
hissp.macros._macro_.case(key, default, *pairs)#

Switch case macro.

Precomputes a lookup table (dict), so must switch on a hashable key. Target keys are not evaluated, so don’t quote them; they must be known at compile time.

The default case is first and required. The remainder are implicitly paired by position.

#> (any-map x '(1 2 spam |42| :eggs)
#..  (case x (print "default")
#..    (0 2 |42|) (print "even")
#..    (1 3 spam) (print "odd")))
>>> # anyQz_map
... __import__('builtins').any(
...   __import__('builtins').map(
...     (lambda x:
...       # case
...       __import__('operator').getitem(
...         # hissp.macros.._macro_.QzAT_
...         (lambda *xs:[*xs])(
...           (lambda :
...             print(
...               ('odd'))),
...           (lambda :
...             print(
...               ('even'))),
...           (lambda :
...             print(
...               ('default')))),
...         {1: 0, 3: 0, 'spam': 0, 0: 1, 2: 1, '42': 1}.get(
...           x,
...           (-1)))()),
...     ((1),
...      (2),
...      'spam',
...      '42',
...      ':eggs',)))
odd
even
odd
even
default
False

See also: cond.

hissp.macros._macro_.chainQzHASH_(itr)#

chain# Abbreviation for itertools.chain.from_iterable

hissp.macros._macro_.cond(*pairs)#

Multiple condition branching.

Pairs are implied by position. Default is (), use something always truthy to change it, like :else or True. For example,

#> (any-map x (@ -0.6 -0.0 42.0 math..nan)
#..  (cond (op#lt x 0) (print :Negative) ;if-else cascade
#..        (op#eq x 0) (print :Zero)
#..        (op#gt x 0) (print :Positive)
#..        :else (print :Not-a-Number)))
>>> # anyQz_map
... __import__('builtins').any(
...   __import__('builtins').map(
...     (lambda x:
...       # cond
...       (lambda x0,x1,x2,x3,x4,x5,x6,x7:x1()if x0 else x3()if x2()else x5()if x4()else x7()if x6()else())(
...         __import__('operator').lt(
...           x,
...           (0)),
...         (lambda :
...           print(
...             ':Negative')),
...         (lambda :
...           __import__('operator').eq(
...             x,
...             (0))),
...         (lambda :
...           print(
...             ':Zero')),
...         (lambda :
...           __import__('operator').gt(
...             x,
...             (0))),
...         (lambda :
...           print(
...             ':Positive')),
...         (lambda :':else'),
...         (lambda :
...           print(
...             ':Not-a-Number')))),
...     # QzAT_
...     (lambda *xs:[*xs])(
...       (-0.6),
...       (-0.0),
...       (42.0),
...       __import__('math').nan)))
:Negative
:Zero
:Positive
:Not-a-Number
False

See also: if-else, case, any-map, The if statement.

hissp.macros._macro_.define(name, value)#

Assigns a global the value in the current module.

#> (define SPAM 'tomato)
>>> # define
... __import__('builtins').globals().update(
...   SPAM='tomato')

#> SPAM
>>> SPAM
'tomato'

See also: globals, dict.update, defonce, Function definitions, Assignment statements, The global statement.

hissp.macros._macro_.defmacro(name, parameters, docstring=(), *body)#

Creates a new macro for the current module.

If there’s no local _macro_ namespace (at compile time), creates one using types.ModuleType (at runtime). If there’s a docstring, stores it as the new lambda’s __doc__. Adds the _macro_ prefix to the lambda’s __qualname__. Saves the lambda in _macro_ using the given attribute name.

#> (defmacro p123 (sep)
#..  <<#;Prints 1 2 3 with the given separator
#..  `(print 1 2 3 : sep ,sep))
>>> # defmacro
... # hissp.macros.._macro_.let
... (lambda _QzAW22OE5Kz_fn=(lambda sep:(
...   'Prints 1 2 3 with the given separator',
...   (lambda * _: _)(
...     'builtins..print',
...     (1),
...     (2),
...     (3),
...     ':',
...     '__main__..sep',
...     sep))[-1]):(
...   __import__('builtins').setattr(
...     _QzAW22OE5Kz_fn,
...     '__doc__',
...     'Prints 1 2 3 with the given separator'),
...   __import__('builtins').setattr(
...     _QzAW22OE5Kz_fn,
...     '__qualname__',
...     ('.').join(
...       ('_macro_',
...        'p123',))),
...   __import__('builtins').setattr(
...     __import__('operator').getitem(
...       __import__('builtins').globals(),
...       '_macro_'),
...     'p123',
...     _QzAW22OE5Kz_fn))[-1])()

#> (p123 ::)
>>> # p123
... __import__('builtins').print(
...   (1),
...   (2),
...   (3),
...   sep='::')
1::2::3

See also: <<#, attach, lambda.

hissp.macros._macro_.defonce(name, value)#

Assigns a global the value in the current module, unless it exists.

Like define, but won’t overwrite an existing global. Useful when sending the whole file to the REPL repeatedly or when using importlib.reload and you want to cache an expensive object instead of re-initializing it every time.

#> (defonce CACHE (types..SimpleNamespace : x 1))
>>> # defonce
... # hissp.macros.._macro_.unless
... (lambda b,a:()if b else a())(
...   __import__('operator').contains(
...     __import__('builtins').globals(),
...     'CACHE'),
...   (lambda :
...     # hissp.macros.._macro_.define
...     __import__('builtins').globals().update(
...       CACHE=__import__('types').SimpleNamespace(
...               x=(1)))))

#> (setattr CACHE 'x 42)
>>> setattr(
...   CACHE,
...   'x',
...   (42))

#> (defonce CACHE (progn (print 'not 'evaluated)
#..                      (types..SimpleNamespace : x 1)))
>>> # defonce
... # hissp.macros.._macro_.unless
... (lambda b,a:()if b else a())(
...   __import__('operator').contains(
...     __import__('builtins').globals(),
...     'CACHE'),
...   (lambda :
...     # hissp.macros.._macro_.define
...     __import__('builtins').globals().update(
...       CACHE=# progn
...             (lambda :(
...               print(
...                 'not',
...                 'evaluated'),
...               __import__('types').SimpleNamespace(
...                 x=(1)))[-1])())))
()

#> CACHE ; The second defonce had no effect.
>>> CACHE
namespace(x=42)
hissp.macros._macro_.deftype(name, bases, *body)#

Defines a type (class) in the current module.

Key-value pairs are implied by position in the body.

#> (deftype Point2D (tuple)
#..  __doc__ "Simple ordered pair."
#..  __new__ (lambda (cls x y)
#..            (.__new__ tuple cls `(,x ,y)))
#..  __repr__ (lambda (self)
#..             (.format "Point2D({!r}, {!r})" : :* self)))
>>> # deftype
... # hissp.macros.._macro_.define
... __import__('builtins').globals().update(
...   Point2D=__import__('builtins').type(
...             'Point2D',
...             (lambda * _: _)(
...               tuple),
...             __import__('builtins').dict(
...               __doc__=('Simple ordered pair.'),
...               __new__=(lambda cls,x,y:
...                         tuple.__new__(
...                           cls,
...                           (lambda * _: _)(
...                             x,
...                             y))),
...               __repr__=(lambda self:
...                          ('Point2D({!r}, {!r})').format(
...                            *self)))))

#> (Point2D 1 2)
>>> Point2D(
...   (1),
...   (2))
Point2D(1, 2)

Also supports kwds in the bases tuple for object.__init_subclass__. Separate with a :.

#> (deftype Foo ()
#..  __init_subclass__ (lambda (cls :/ : :** kwargs)
#..                      (print kwargs)))
>>> # deftype
... # hissp.macros.._macro_.define
... __import__('builtins').globals().update(
...   Foo=__import__('builtins').type(
...         'Foo',
...         (lambda * _: _)(),
...         __import__('builtins').dict(
...           __init_subclass__=(lambda cls,/,**kwargs:
...                               print(
...                                 kwargs)))))

#> (deftype Bar (Foo : a 1  b 2))
>>> # deftype
... # hissp.macros.._macro_.define
... __import__('builtins').globals().update(
...   Bar=__import__('builtins').type(
...         'Bar',
...         (lambda * _: _)(
...           Foo),
...         __import__('builtins').dict(),
...         a=(1),
...         b=(2)))
{'a': 1, 'b': 2}

See also: attach, type, @#, class, types.new_class

hissp.macros._macro_.doto(self, *invocations)#

Configure an object.

Calls multiple ‘methods’ on one ‘self’.

Evaluates the given self, then injects it as the first argument to a sequence of invocations. Returns self.

#> (doto (list)
#..  (.extend 'bar)
#..  .sort
#..  (.append 'foo))
>>> # doto
... (lambda _QzKIUMBHNZz_self=list():(
...   _QzKIUMBHNZz_self.extend(
...     'bar'),
...   _QzKIUMBHNZz_self.sort(),
...   _QzKIUMBHNZz_self.append(
...     'foo'),
...   _QzKIUMBHNZz_self)[-1])()
['a', 'b', 'r', 'foo']

See also: attach, progn, ->.

hissp.macros._macro_.enQzHASH_(f)#

en# reader macro. Wrap a function applicable to a tuple as a function of its elements.

#> (en#list 1 2 3)
>>> (lambda *_Qz6RFWTTVXz_xs:
...   list(
...     _Qz6RFWTTVXz_xs))(
...   (1),
...   (2),
...   (3))
[1, 2, 3]

#> (en#.extend _ 4 5 6) ; Methods too.
#..
>>> (lambda _Qz4LWLAFU3z_self,*_Qz4LWLAFU3z_xs:
...   _Qz4LWLAFU3z_self.extend(
...     _Qz4LWLAFU3z_xs))(
...   _,
...   (4),
...   (5),
...   (6))

#> _
>>> _
[1, 2, 3, 4, 5, 6]

#> (define enjoin en#X#(.join "" (map str X)))
>>> # define
... __import__('builtins').globals().update(
...   enjoin=(lambda *_Qz6RFWTTVXz_xs:
...            (lambda X:
...              ('').join(
...                map(
...                  str,
...                  X)))(
...              _Qz6RFWTTVXz_xs)))

#> (enjoin "Sum: "(op#add 2 3)". Product: "(op#mul 2 3)".")
>>> enjoin(
...   ('Sum: '),
...   __import__('operator').add(
...     (2),
...     (3)),
...   ('. Product: '),
...   __import__('operator').mul(
...     (2),
...     (3)),
...   ('.'))
'Sum: 5. Product: 6.'

There are no bundled reader macros for a quinary, senary, etc. but the en#X# variadic or a normal lambda form can be used instead.

See also: X#.

hissp.macros._macro_.getQzHASH_(e)#

get# ‘itemgetter-’ Makes an operator.itemgetter function from e.

#> (define first get#0)                ;Gets an item by key.
>>> # define
... __import__('builtins').globals().update(
...   first=__import__('operator').itemgetter(
...           (0)))

#> (first "abc")
>>> first(
...   ('abc'))
'a'

#> (get#(slice None None -1) "abc")    ;Slicing without injection.
>>> __import__('operator').itemgetter(
...   slice(
...     None,
...     None,
...     (-1)))(
...   ('abc'))
'cba'

#> (get#'+ (dict : foo 2  + 1))        ;These also work on dicts.
>>> __import__('operator').itemgetter(
...   'QzPLUS_')(
...   dict(
...     foo=(2),
...     QzPLUS_=(1)))
1

See also: operator.getitem, [#, set!.

hissp.macros._macro_.iQzHASH_(_QzE4JATHEUz_attr, *_QzE4JATHEUz_args, **_QzE4JATHEUz_kwargs)#

Aliases itertools. as i#.

hissp.macros._macro_.ifQz_else(test, consequent, alternate)#

if-else Basic ternary branching construct.

Like Python’s conditional expressions, the ‘else’ clause is required.

#> (any-map c 'ab
#..  (if-else (op#eq c 'b)            ;ternary conditional
#..    (print 'Yes)
#..    (print 'No)))
>>> # anyQz_map
... __import__('builtins').any(
...   __import__('builtins').map(
...     (lambda c:
...       # ifQz_else
...       (lambda b,c,a:c()if b else a())(
...         __import__('operator').eq(
...           c,
...           'b'),
...         (lambda :
...           print(
...             'Yes')),
...         (lambda :
...           print(
...             'No')))),
...     'ab'))
No
Yes
False

See also: when, cond, any-map, Conditional expressions.

hissp.macros._macro_.let(pairs, *body)#

Creates local variables. Pairs are implied by position.

Locals are not in scope until the body.

#> (let (x 'a                          ;Create locals.
#..      y 'b )                        ;Any number of pairs.
#..  (print x y)
#..  (let (x 'x
#..        y (op#concat x x))          ;Not in scope until body.
#..    (print x y))                    ;Outer variables shadowed.
#..  (print x y))                      ;Inner went out of scope.
>>> # let
... (lambda x='a',y='b':(
...   print(
...     x,
...     y),
...   # let
...   (lambda x='x',y=__import__('operator').concat(
...     x,
...     x):
...     print(
...       x,
...       y))(),
...   print(
...     x,
...     y))[-1])()
a b
x aa
a b

See also: let-from, my#, locals.

hissp.macros._macro_.letQzSTAR_from(pairs, *body)#

let*from ‘let star from’ Nested let-from.

Can unpack nested iterables by using multiple stages.

#> (dict : A 'B  C 'D)
>>> dict(
...   A='B',
...   C='D')
{'A': 'B', 'C': 'D'}

#> (let*from ((ab cd) (.items _)    ;Nested let-froms.
#..           (a b) ab              ;Unpacks first item ('A', 'B')
#..           (c d) cd)             ;Unpacks second item ('C', 'D')
#..  (print a b c d))
>>> # letQzSTAR_from
... # hissp.macros.._macro_.letQz_from
... (lambda ab,cd:
...   # hissp.macros..QzMaybe_.letQzSTAR_from
...   # hissp.macros.._macro_.letQz_from
...   (lambda a,b:
...     # hissp.macros..QzMaybe_.letQzSTAR_from
...     # hissp.macros.._macro_.letQz_from
...     (lambda c,d:
...       # hissp.macros..QzMaybe_.letQzSTAR_from
...       # hissp.macros.._macro_.progn
...       (lambda :
...         print(
...           a,
...           b,
...           c,
...           d))())(
...       *cd))(
...     *ab))(
...   *_.items())
A B C D


#> (let*from ((ab cd) (.items _)    ;Fewer stack frames.
#..           (a b c d) `(,@ab ,@cd)) ;Leveraging ,@ splicing.
#..  (print a b c d))
>>> # letQzSTAR_from
... # hissp.macros.._macro_.letQz_from
... (lambda ab,cd:
...   # hissp.macros..QzMaybe_.letQzSTAR_from
...   # hissp.macros.._macro_.letQz_from
...   (lambda a,b,c,d:
...     # hissp.macros..QzMaybe_.letQzSTAR_from
...     # hissp.macros.._macro_.progn
...     (lambda :
...       print(
...         a,
...         b,
...         c,
...         d))())(
...     *(lambda * _: _)(
...        *ab,
...        *cd)))(
...   *_.items())
A B C D


#> (let-from (a c b d) ; Didn't really need let*from this time.
#..          `(,@(.keys _) ,@(.values _)) ; Not always this easy.
#..  (print a b c d))
>>> # letQz_from
... (lambda a,c,b,d:
...   print(
...     a,
...     b,
...     c,
...     d))(
...   *(lambda * _: _)(
...      *_.keys(),
...      *_.values()))
A B C D
hissp.macros._macro_.letQz_from(syms, itr, *body)#

let-from Create listed locals from iterable.

#> (let-from (a b : :* cs) 'ABCDEFG
#..  (print cs b a))
>>> # letQz_from
... (lambda a,b,*cs:
...   print(
...     cs,
...     b,
...     a))(
...   *'ABCDEFG')
('C', 'D', 'E', 'F', 'G') B A

See also: let, let*from, Assignment statements.

hissp.macros._macro_.loopQz_from(syms, inits, *body)#

loop-from Anaphoric. Loop/recur with trampoline.

Set local values for the first loop with an iterable as let-from.

Creates a stack to schedule future loops. Call the recur-from anaphor with an iterable of values for the locals to push another loop to the schedule. Call with None to abort any remaining schedule.

Returns the value of the final loop.

#> (loop-from x '(3)                   ;Unpacks as let-from.
#..  (when x
#..    (print x)
#..    (recur-from (@ (op#sub x 1)))))
>>> # loopQz_from
... # hissp.macros.._macro_.let
... (lambda _QzDKFIH6Z2z_stack=# hissp.macros..QzMaybe_.QzAT_
... (lambda *xs:[*xs])(
...   (),
...   None,
...   ((3),)):
...   # hissp.macros.._macro_.let
...   (lambda recurQz_from=_QzDKFIH6Z2z_stack.append:(
...     # hissp.macros.._macro_.anyQzSTAR_map
...     __import__('builtins').any(
...       __import__('itertools').starmap(
...         (lambda x:(
...           __import__('operator').setitem(
...             _QzDKFIH6Z2z_stack,
...             (0),
...             # hissp.macros.._macro_.progn
...             (lambda :
...               # when
...               (lambda b,c:c()if b else())(
...                 x,
...                 (lambda :(
...                   print(
...                     x),
...                   recurQz_from(
...                     # QzAT_
...                     (lambda *xs:[*xs])(
...                       __import__('operator').sub(
...                         x,
...                         (1)))))[-1])))()),
...           None)[-1]),
...         __import__('builtins').iter(
...           _QzDKFIH6Z2z_stack.pop,
...           None))),
...     __import__('operator').itemgetter(
...       (0))(
...       _QzDKFIH6Z2z_stack))[-1])())()
3
2
1
()

See also: any*map, Ensue, The while statement.

hissp.macros._macro_.myQzHASH_(e)#

my# Anaphoric. let my be a fresh types.SimpleNamespace in a lexical scope surrounding e.

Creates a local namespace for imperative-style reassignments, typically combined with set@ for an assignment.

#> my#(print (set@ my.x (op#add 1 1))
#..           my.x)
>>> # hissp.macros.._macro_.let
... (lambda my=__import__('types').SimpleNamespace():
...   print(
...     # setQzAT_
...     # hissp.macros.._macro_.let
...     (lambda _QzRMG5GSSIz_val=__import__('operator').add(
...       (1),
...       (1)):(
...       __import__('builtins').setattr(
...         my,
...         'x',
...         _QzRMG5GSSIz_val),
...       _QzRMG5GSSIz_val)[-1])(),
...     my.x))()
2 2

my#my is a shorthand for a new empty namespace.

Often combined with branching macros to reuse the results of an expression, with uses similar to Python’s ‘walrus’ operator :=.

See also, attach, python-grammar:assignment_expression.

hissp.macros._macro_.nilQzHASH_(x)#

nil# evaluates as x or (). Adapter for ‘nil punning’.

hissp.macros._macro_.opQzHASH_(_QzE4JATHEUz_attr, *_QzE4JATHEUz_args, **_QzE4JATHEUz_kwargs)#

Aliases operator. as op#.

hissp.macros._macro_.ors(*exprs)#

Variadic shortcutting logical OR.

Returns the first true value, otherwise the last value. There is an implicit initial value of ().

#> (ors True (print 'oops)) ; Shortcutting.
>>> # ors
... (lambda x0,x1:x0 or x1())(
...   True,
...   (lambda :
...     print(
...       'oops')))
True

#> (ors 42 False)
>>> # ors
... (lambda x0,x1:x0 or x1())(
...   (42),
...   (lambda :False))
42

#> (ors () False 0 1)  ; or seeks the truth
>>> # ors
... (lambda x0,x1,x2,x3:x0 or x1()or x2()or x3())(
...   (),
...   (lambda :False),
...   (lambda :(0)),
...   (lambda :(1)))
1

#> (ors False)
>>> # ors
... False
False

#> (ors)
>>> # ors
... ()
()

See also: ands, bool, Boolean operations, any.

hissp.macros._macro_.prelude(ns=('builtins..globals',))#

Hissp’s bundled micro prelude.

Brings Hissp up to a minimal standard of usability without adding any dependencies in the compiled output.

Mainly intended for single-file scripts that can’t have dependencies, or similarly constrained environments (e.g. embedded, readerless). There, the first form should be (hissp.._macro_.prelude), which is also implied in $ lissp -c commands.

Larger projects with access to functional and macro libraries need not use this prelude at all.

The prelude has several effects:

  • Defines engarde, which calls a function with exception handler:

    def engarde(xs,h,f,/,*a,**kw):
     try:return f(*a,**kw)
     except xs as e:return h(e)
    

    engarde with handlers can stack above in a single form.

    See engarde examples below.

  • Defines enter, which calls a function with context manager:

    def enter(c,f,/,*a):
     with c as C:return f(*a,C)
    

    enter with context managers can stack above in a single form.

    See enter examples below.

  • Defines the Ensue class; trampolined continuation generators:

    class Ensue(__import__('collections.abc').abc.Generator):
     send=lambda s,v:s.g.send(v);throw=lambda s,*x:s.g.throw(*x);F=0;X=();Y=[]
     def __init__(s,p):s.p,s.g,s.n=p,s._(s),s.Y
     def _(s,k,v=None):
      while isinstance(s:=k,__class__) and not setattr(s,'sent',v):
       try:k,y=s.p(s),s.Y;v=(yield from y)if s.F or y is s.n else(yield y)
       except s.X as e:v=e
      return k
    

    Ensue takes a step function and returns a generator. The step function receives the previous Ensue step and must return the next one to continue. Returning a different type raises a StopIteration with that object. Set the Y attribute on the current step to [Y]ield a value this step. Set the F attribute to a true value to yield values [F]rom the Y iterable instead. Set the X attribute to an e[X]ception class or tuple to catch any targeted exceptions on the next step. Each step keeps a sent attribute, which is the value sent to the generator this step, or the exception caught this step instead.

    See Ensue examples and enter examples below.

    See also: types.coroutine, collections.abc.Generator, loop-from.

  • Adds the bundled macros, but only if available (macros are typically only used at compile time), so its compiled expansion does not require Hissp to be installed. (This replaces _macro_ if you already had one.):

    _macro_=__import__('types').SimpleNamespace()
    try:exec('from {}._macro_ import *',vars(_macro_))
    except ModuleNotFoundError:pass
    

Prelude Usage#

The REPL has the bundled macros loaded by default, but not the prelude. Invoke (prelude) to get the rest.

#> (prelude)
>>> # prelude
... __import__('builtins').exec(
...   ('from functools import partial,reduce\n'
...    'from itertools import *;from operator import *\n'
...    'def engarde(xs,h,f,/,*a,**kw):\n'
...    ' try:return f(*a,**kw)\n'
...    ' except xs as e:return h(e)\n'
...    'def enter(c,f,/,*a):\n'
...    ' with c as C:return f(*a,C)\n'
...    "class Ensue(__import__('collections.abc').abc.Generator):\n"
...    ' send=lambda s,v:s.g.send(v);throw=lambda s,*x:s.g.throw(*x);F=0;X=();Y=[]\n'
...    ' def __init__(s,p):s.p,s.g,s.n=p,s._(s),s.Y\n'
...    ' def _(s,k,v=None):\n'
...    "  while isinstance(s:=k,__class__) and not setattr(s,'sent',v):\n"
...    '   try:k,y=s.p(s),s.Y;v=(yield from y)if s.F or y is s.n else(yield y)\n'
...    '   except s.X as e:v=e\n'
...    '  return k\n'
...    "_macro_=__import__('types').SimpleNamespace()\n"
...    "try:exec('from hissp.macros._macro_ import *',vars(_macro_))\n"
...    'except ModuleNotFoundError:pass'),
...   __import__('builtins').globals())

See also, alias.

engarde examples#

#> (engarde `(,FloatingPointError ,ZeroDivisionError) ;two targets
#..         (lambda e (print "Oops!") e)    ;handler (returns exception)
#..         truediv 6 0)                    ;calls it on your behalf
>>> engarde(
...   (lambda * _: _)(
...     FloatingPointError,
...     ZeroDivisionError),
...   (lambda e:(
...     print(
...       ('Oops!')),
...     e)[-1]),
...   truediv,
...   (6),
...   (0))
Oops!
ZeroDivisionError('division by zero')

#> (engarde ArithmeticError repr truediv 6 0) ;superclass target
>>> engarde(
...   ArithmeticError,
...   repr,
...   truediv,
...   (6),
...   (0))
"ZeroDivisionError('division by zero')"

#> (engarde ArithmeticError repr truediv 6 2) ;returned answer
>>> engarde(
...   ArithmeticError,
...   repr,
...   truediv,
...   (6),
...   (2))
3.0

;; You can stack them.
#> (engarde Exception                       ;The outer engarde
#.. print
#.. engarde ZeroDivisionError               ; calls the inner.
#.. (lambda e (print "It means what you want it to mean."))
#.. truediv "6" 0)                          ;Try variations.
>>> engarde(
...   Exception,
...   print,
...   engarde,
...   ZeroDivisionError,
...   (lambda e:
...     print(
...       ('It means what you want it to mean.'))),
...   truediv,
...   ('6'),
...   (0))
unsupported operand type(s) for /: 'str' and 'int'

#> (engarde Exception
#..         (lambda x x.__cause__)
#..         (lambda : (throw-from Exception (Exception "msg"))))
>>> engarde(
...   Exception,
...   (lambda x:x.__cause__),
...   (lambda :
...     # throwQz_from
...     # hissp.macros.._macro_.throwQzSTAR_
...     (lambda g:g.close()or g.throw)(c for c in'')(
...       # hissp.macros.._macro_.let
...       (lambda _Qz2IKKUCBWz_G=(lambda _Qz2IKKUCBWz_x:
...         # hissp.macros.._macro_.ifQz_else
...         (lambda b,c,a:c()if b else a())(
...           # hissp.macros.._macro_.ands
...           (lambda x0,x1:x0 and x1())(
...             __import__('builtins').isinstance(
...               _Qz2IKKUCBWz_x,
...               __import__('builtins').type),
...             (lambda :
...               __import__('builtins').issubclass(
...                 _Qz2IKKUCBWz_x,
...                 __import__('builtins').BaseException))),
...           (lambda :_Qz2IKKUCBWz_x()),
...           (lambda :_Qz2IKKUCBWz_x))):
...         # hissp.macros.._macro_.attach
...         # hissp.macros.._macro_.let
...         (lambda _QzWG5WN73Wz_target=_Qz2IKKUCBWz_G(
...           Exception):(
...           __import__('builtins').setattr(
...             _QzWG5WN73Wz_target,
...             '__cause__',
...             _Qz2IKKUCBWz_G(
...               Exception(
...                 ('msg')))),
...           _QzWG5WN73Wz_target)[-1])())())))
Exception('msg')

Ensue examples#

#> (define fibonacci
#..  (lambda (: a 1  b 1)
#..    (Ensue (lambda (step)
#..             (set@ step.Y a)            ;Y for yield.
#..             (fibonacci b (add a b))))))
>>> # define
... __import__('builtins').globals().update(
...   fibonacci=(lambda a=(1),b=(1):
...               Ensue(
...                 (lambda step:(
...                   # setQzAT_
...                   # hissp.macros.._macro_.let
...                   (lambda _QzRMG5GSSIz_val=a:(
...                     __import__('builtins').setattr(
...                       step,
...                       'Y',
...                       _QzRMG5GSSIz_val),
...                     _QzRMG5GSSIz_val)[-1])(),
...                   fibonacci(
...                     b,
...                     add(
...                       a,
...                       b)))[-1]))))

#> (list (islice (fibonacci) 7))
>>> list(
...   islice(
...     fibonacci(),
...     (7)))
[1, 1, 2, 3, 5, 8, 13]

#> (define my-range ; Terminate by not returning an Ensue.
#..  (lambda in
#..    (Ensue (lambda (step)
#..             (when (lt i n)             ;Acts like a while loop.
#..               (set@ step.Y i)
#..               (my-range (add i 1) n)))))) ;Conditional recursion.
#..
>>> # define
... __import__('builtins').globals().update(
...   myQz_range=(lambda i,n:
...                Ensue(
...                  (lambda step:
...                    # when
...                    (lambda b,c:c()if b else())(
...                      lt(
...                        i,
...                        n),
...                      (lambda :(
...                        # setQzAT_
...                        # hissp.macros.._macro_.let
...                        (lambda _QzRMG5GSSIz_val=i:(
...                          __import__('builtins').setattr(
...                            step,
...                            'Y',
...                            _QzRMG5GSSIz_val),
...                          _QzRMG5GSSIz_val)[-1])(),
...                        myQz_range(
...                          add(
...                            i,
...                            (1)),
...                          n))[-1]))))))

#> (list (my-range 1 6))
>>> list(
...   myQz_range(
...     (1),
...     (6)))
[1, 2, 3, 4, 5]

#> (Ensue (lambda (step)
#..         (attach step :
#..           F True ; Set F for yield-From mode.
#..           Y '(1 2 3 4 5))
#..         None))
>>> Ensue(
...   (lambda step:(
...     # attach
...     # hissp.macros.._macro_.let
...     (lambda _QzWG5WN73Wz_target=step:(
...       __import__('builtins').setattr(
...         _QzWG5WN73Wz_target,
...         'F',
...         True),
...       __import__('builtins').setattr(
...         _QzWG5WN73Wz_target,
...         'Y',
...         ((1),
...          (2),
...          (3),
...          (4),
...          (5),)),
...       _QzWG5WN73Wz_target)[-1])(),
...     None)[-1]))
<...Ensue object at ...>

#> (list _)
>>> list(
...   _)
[1, 2, 3, 4, 5]

#> (define recycle
#..  (lambda (itr)
#..    (Ensue (lambda (step)
#..             ;; Implicit continuation; step is an Ensue.
#..             (attach step : Y itr  F 1)))))
>>> # define
... __import__('builtins').globals().update(
...   recycle=(lambda itr:
...             Ensue(
...               (lambda step:
...                 # attach
...                 # hissp.macros.._macro_.let
...                 (lambda _QzWG5WN73Wz_target=step:(
...                   __import__('builtins').setattr(
...                     _QzWG5WN73Wz_target,
...                     'Y',
...                     itr),
...                   __import__('builtins').setattr(
...                     _QzWG5WN73Wz_target,
...                     'F',
...                     (1)),
...                   _QzWG5WN73Wz_target)[-1])()))))

#> (-> '(1 2 3) recycle (islice 7) list)
>>> # Qz_QzGT_
... list(
...   islice(
...     recycle(
...       ((1),
...        (2),
...        (3),)),
...     (7)))
[1, 2, 3, 1, 2, 3, 1]

#> (define echo
#..  (Ensue (lambda (step)
#..           (set@ step.Y step.sent)
#..           step)))
>>> # define
... __import__('builtins').globals().update(
...   echo=Ensue(
...          (lambda step:(
...            # setQzAT_
...            # hissp.macros.._macro_.let
...            (lambda _QzRMG5GSSIz_val=step.sent:(
...              __import__('builtins').setattr(
...                step,
...                'Y',
...                _QzRMG5GSSIz_val),
...              _QzRMG5GSSIz_val)[-1])(),
...            step)[-1])))

#> (.send echo None) ; Always send a None first. Same as Python.
>>> echo.send(
...   None)

#> (.send echo "Yodel!") ; Generators are two-way.
>>> echo.send(
...   ('Yodel!'))
'Yodel!'

#> (.send echo 42)
>>> echo.send(
...   (42))
42

enter examples#

#> (define wrap
#..  (contextlib..contextmanager
#..   (lambda (msg)
#..     (print "enter" msg)
#..     (Ensue (lambda (step)
#..              (set@ step.Y msg)
#..              (Ensue (lambda (step)
#..                       (print "exit" msg))))))))
>>> # define
... __import__('builtins').globals().update(
...   wrap=__import__('contextlib').contextmanager(
...          (lambda msg:(
...            print(
...              ('enter'),
...              msg),
...            Ensue(
...              (lambda step:(
...                # setQzAT_
...                # hissp.macros.._macro_.let
...                (lambda _QzRMG5GSSIz_val=msg:(
...                  __import__('builtins').setattr(
...                    step,
...                    'Y',
...                    _QzRMG5GSSIz_val),
...                  _QzRMG5GSSIz_val)[-1])(),
...                Ensue(
...                  (lambda step:
...                    print(
...                      ('exit'),
...                      msg))))[-1])))[-1])))

#> (enter (wrap 'A)
#..       (lambda a (print a)))
>>> enter(
...   wrap(
...     'A'),
...   (lambda a:
...     print(
...       a)))
enter A
A
exit A

#> (enter (wrap 'A)
#.. enter (wrap 'B)
#.. enter (wrap 'C) ; You can stack them.
#.. (lambda abc (print a b c)))
>>> enter(
...   wrap(
...     'A'),
...   enter,
...   wrap(
...     'B'),
...   enter,
...   wrap(
...     'C'),
...   (lambda a,b,c:
...     print(
...       a,
...       b,
...       c)))
enter A
enter B
enter C
A B C
exit C
exit B
exit A

#> (define suppress-zde
#..  (contextlib..contextmanager
#..   (lambda :
#..     (Ensue (lambda (step)
#..              (attach step :
#..                Y None
#..                X ZeroDivisionError) ;X for eXcept (can be a tuple).
#..              (Ensue (lambda (step)
#..                       (print "Caught a" step.sent))))))))
>>> # define
... __import__('builtins').globals().update(
...   suppressQz_zde=__import__('contextlib').contextmanager(
...                    (lambda :
...                      Ensue(
...                        (lambda step:(
...                          # attach
...                          # hissp.macros.._macro_.let
...                          (lambda _QzWG5WN73Wz_target=step:(
...                            __import__('builtins').setattr(
...                              _QzWG5WN73Wz_target,
...                              'Y',
...                              None),
...                            __import__('builtins').setattr(
...                              _QzWG5WN73Wz_target,
...                              'X',
...                              ZeroDivisionError),
...                            _QzWG5WN73Wz_target)[-1])(),
...                          Ensue(
...                            (lambda step:
...                              print(
...                                ('Caught a'),
...                                step.sent))))[-1])))))

#> (enter (suppress-zde)
#..  (lambda _ (truediv 1 0)))
>>> enter(
...   suppressQz_zde(),
...   (lambda _:
...     truediv(
...       (1),
...       (0))))
Caught a division by zero

;; No exception, so step.sent was .send() value.
#> (enter (suppress-zde)
#..  (lambda _ (truediv 4 2)))
>>> enter(
...   suppressQz_zde(),
...   (lambda _:
...     truediv(
...       (4),
...       (2))))
Caught a None
2.0

#> (enter (suppress-zde)
#..  (lambda _ (throw Exception)))
>>> enter(
...   suppressQz_zde(),
...   (lambda _:
...     # throw
...     # hissp.macros.._macro_.throwQzSTAR_
...     (lambda g:g.close()or g.throw)(c for c in'')(
...       Exception)))
Traceback (most recent call last):
  ...
Exception
hissp.macros._macro_.prog1(expr1, *body)#

Evaluates each expression in sequence (for side effects), resulting in the value of the first.

#> (print (prog1 0                     ;Sequence for side effects, eval to first.
#..         (print 1)
#..         (print 2)))
>>> print(
...   # prog1
...   # hissp.macros.._macro_.let
...   (lambda _Qz46BJ7IW6z_value1=(0):(
...     print(
...       (1)),
...     print(
...       (2)),
...     _Qz46BJ7IW6z_value1)[-1])())
1
2
0

Combine with progn for a value in the middle of the sequence.

#> (prog1                              ;Sequence for side effects, eval to first.
#..  (progn (print 1)                  ;Sequence for side effects, eval to last.
#..         3)
#..  (print 2))
>>> # prog1
... # hissp.macros.._macro_.let
... (lambda _Qz46BJ7IW6z_value1=# progn
... (lambda :(
...   print(
...     (1)),
...   (3))[-1])():(
...   print(
...     (2)),
...   _Qz46BJ7IW6z_value1)[-1])()
1
2
3

See also: doto.

hissp.macros._macro_.progn(*body)#

Evaluates each body expression in sequence (for side effects), resulting in the value of the last (or () if empty).

#> (print (progn (print 1)             ;Sequence for side effects, eval to last.
#..              (print 2)
#..              3))
>>> print(
...   # progn
...   (lambda :(
...     print(
...       (1)),
...     print(
...       (2)),
...     (3))[-1])())
1
2
3

See also: prog1, Expression statements.

hissp.macros._macro_.setQzAT_(name, val)#

set@ ‘setat’ Assigns an attribute, returns the value. Mnemonic: set @tribute.

#> (define spam (types..SimpleNamespace))
>>> # define
... __import__('builtins').globals().update(
...   spam=__import__('types').SimpleNamespace())

#> (set@ spam.foo 10)
>>> # setQzAT_
... # hissp.macros.._macro_.let
... (lambda _QzRMG5GSSIz_val=(10):(
...   __import__('builtins').setattr(
...     spam,
...     'foo',
...     _QzRMG5GSSIz_val),
...   _QzRMG5GSSIz_val)[-1])()
10

#> spam
>>> spam
namespace(foo=10)

See also: attach, delattr, zap@, setattr.

hissp.macros._macro_.setQzBANG_(coll, key, val)#

set! ‘setbang’ Assigns an item, returns the value. Mnemonic: set !tem.

#> (define spam (dict))
>>> # define
... __import__('builtins').globals().update(
...   spam=dict())

#> (set! spam 2 10) ; Like operator.setitem, but returns value given.
#..
>>> # setQzBANG_
... # hissp.macros.._macro_.let
... (lambda _QzE3BPTV2Tz_val=(10):(
...   __import__('operator').setitem(
...     spam,
...     (2),
...     _QzE3BPTV2Tz_val),
...   _QzE3BPTV2Tz_val)[-1])()
10

#> spam
>>> spam
{2: 10}

See also: operator.setitem, operator.delitem, zap!.

hissp.macros._macro_.spyQzHASH_(expr, file='sys..stderr')#

spy# Print e => its value to the file. Return the value.

Typically used to debug a Lissp expression.

#> (op#add 5 spy##file=#sys..stdout(op#mul 7 3))
>>> __import__('operator').add(
...   (5),
...   # hissp.._macro_._spy
...   # hissp.macros.._macro_.let
...   (lambda _Qz764KZBP5z_e=__import__('operator').mul(
...     (7),
...     (3)):(
...     __import__('builtins').print(
...       __import__('pprint').pformat(
...         ('operator..mul',
...          (7),
...          (3),),
...         sort_dicts=(0)),
...       ('=>'),
...       __import__('builtins').repr(
...         _Qz764KZBP5z_e),
...       file=__import__('sys').stdout),
...     _Qz764KZBP5z_e)[-1])())
('operator..mul', 7, 3) => 21
26

See also: print, doto, progn.

hissp.macros._macro_.throw(exception)#

Raise an exception.

#> (throw Exception)                   ;Raise exception objects or classes.
>>> # throw
... # hissp.macros.._macro_.throwQzSTAR_
... (lambda g:g.close()or g.throw)(c for c in'')(
...   Exception)
Traceback (most recent call last):
  ...
Exception

#> (throw (TypeError 'message))
>>> # throw
... # hissp.macros.._macro_.throwQzSTAR_
... (lambda g:g.close()or g.throw)(c for c in'')(
...   TypeError(
...     'message'))
Traceback (most recent call last):
  ...
TypeError: message

See also: throw-from, engarde, The raise statement.

hissp.macros._macro_.throwQzSTAR_(*exception)#

throw* ‘throw star’ Creates a closed generator and calls .throw.

Despite PEP 3109, .throw still seems to accept multiple arguments. Avoid using this form except when implementing throw method overrides. Prefer throw instead.

hissp.macros._macro_.throwQz_from(exception, cause)#

throw-from Raise an exception with a cause, which can be None.

#> (throw-from Exception (Exception 'message)) ;Explicit chaining.
#..
>>> # throwQz_from
... # hissp.macros.._macro_.throwQzSTAR_
... (lambda g:g.close()or g.throw)(c for c in'')(
...   # hissp.macros.._macro_.let
...   (lambda _Qz2IKKUCBWz_G=(lambda _Qz2IKKUCBWz_x:
...     # hissp.macros.._macro_.ifQz_else
...     (lambda b,c,a:c()if b else a())(
...       # hissp.macros.._macro_.ands
...       (lambda x0,x1:x0 and x1())(
...         __import__('builtins').isinstance(
...           _Qz2IKKUCBWz_x,
...           __import__('builtins').type),
...         (lambda :
...           __import__('builtins').issubclass(
...             _Qz2IKKUCBWz_x,
...             __import__('builtins').BaseException))),
...       (lambda :_Qz2IKKUCBWz_x()),
...       (lambda :_Qz2IKKUCBWz_x))):
...     # hissp.macros.._macro_.attach
...     # hissp.macros.._macro_.let
...     (lambda _QzWG5WN73Wz_target=_Qz2IKKUCBWz_G(
...       Exception):(
...       __import__('builtins').setattr(
...         _QzWG5WN73Wz_target,
...         '__cause__',
...         _Qz2IKKUCBWz_G(
...           Exception(
...             'message'))),
...       _QzWG5WN73Wz_target)[-1])())())
Traceback (most recent call last):
  ...
Exception

See also: throw, throw*.

hissp.macros._macro_.timeQzHASH_(expr, file='sys..stderr')#

time# Print ms elapsed running e to the file. Return its value.

Typically used when optimizing a Lissp expression.

#> time##file=#sys..stdout(time..sleep .05)
>>> # hissp.macros.._macro_.let
... (lambda _QzPMWTVFTZz_time=__import__('time').time_ns:
...   # hissp.macros.._macro_.letQz_from
...   (lambda _QzPMWTVFTZz_start,_QzPMWTVFTZz_val,_QzPMWTVFTZz_end:(
...     __import__('builtins').print(
...       ('time# ran'),
...       __import__('pprint').pformat(
...         ('time..sleep',
...          (0.05),),
...         sort_dicts=(0)),
...       ('in'),
...       __import__('operator').truediv(
...         __import__('operator').sub(
...           _QzPMWTVFTZz_end,
...           _QzPMWTVFTZz_start),
...         __import__('decimal').Decimal(
...           (1000000.0))),
...       ('ms'),
...       file=__import__('sys').stdout),
...     _QzPMWTVFTZz_val)[-1])(
...     *# hissp.macros.._macro_.QzAT_
...      (lambda *xs:[*xs])(
...        _QzPMWTVFTZz_time(),
...        __import__('time').sleep(
...          (0.05)),
...        _QzPMWTVFTZz_time())))()
time# ran ('time..sleep', 0.05) in ... ms

See also: timeit.

hissp.macros._macro_.unless(condition, *body)#

Unless the condition is true, evaluates each expression in sequence for side effects, resulting in the value of the last. Otherwise, skips them and returns ().

#> (any-map c 'abcd
#..  (unless (op#eq c 'b)
#..    (print c)))
>>> # anyQz_map
... __import__('builtins').any(
...   __import__('builtins').map(
...     (lambda c:
...       # unless
...       (lambda b,a:()if b else a())(
...         __import__('operator').eq(
...           c,
...           'b'),
...         (lambda :
...           print(
...             c)))),
...     'abcd'))
a
c
d
False

See also: when.

hissp.macros._macro_.when(condition, *body)#

When the condition is true, evaluates each expression in sequence for side effects, resulting in the value of the last. Otherwise, skips them and returns ().

#> (any-map c 'abcd
#..  (print c)
#..  (when (op#eq c 'b)
#..    (print 'found)
#..    :break))
>>> # anyQz_map
... __import__('builtins').any(
...   __import__('builtins').map(
...     (lambda c:(
...       print(
...         c),
...       # when
...       (lambda b,c:c()if b else())(
...         __import__('operator').eq(
...           c,
...           'b'),
...         (lambda :(
...           print(
...             'found'),
...           ':break')[-1])))[-1]),
...     'abcd'))
a
b
found
True

See also: if-else, unless, The if statement.

hissp.macros._macro_.zapQzAT_(op, name, *args)#

zap@ ‘zapat’ Augmented attribute assignment operator.

The current attribute value becomes the first argument. Returns the value. Mnemonic: zap @tribute.

#> (define spam (types..SimpleNamespace : foo 10))
>>> # define
... __import__('builtins').globals().update(
...   spam=__import__('types').SimpleNamespace(
...          foo=(10)))

#> (zap@ operator..iadd spam.foo 1)
>>> # zapQzAT_
... # hissp.macros.._macro_.setQzAT_
... # hissp.macros.._macro_.let
... (lambda _QzRMG5GSSIz_val=__import__('operator').iadd(
...   spam.foo,
...   (1)):(
...   __import__('builtins').setattr(
...     spam,
...     'foo',
...     _QzRMG5GSSIz_val),
...   _QzRMG5GSSIz_val)[-1])()
11

#> spam
>>> spam
namespace(foo=11)

See also: set@, zap!, operator.iadd, Augmented assignment statements.

hissp.macros._macro_.zapQzBANG_(op, coll, key, *args)#

zap! ‘zapbang’ Augmented item assignment operator.

The current item value becomes the first argument. Returns the value. Mnemonic: zap !tem.

#> (define spam (dict : b 10))
>>> # define
... __import__('builtins').globals().update(
...   spam=dict(
...          b=(10)))

#> (zap! operator..iadd spam 'b 1) ; Augmented item assignment, like +=.
#..
>>> # zapQzBANG_
... # hissp.macros.._macro_.let
... (lambda _QzRDZYRDXSz_coll=spam,_QzRDZYRDXSz_key='b':
...   # hissp.macros.._macro_.setQzBANG_
...   # hissp.macros.._macro_.let
...   (lambda _QzE3BPTV2Tz_val=__import__('operator').iadd(
...     __import__('operator').getitem(
...       _QzRDZYRDXSz_coll,
...       _QzRDZYRDXSz_key),
...     (1)):(
...     __import__('operator').setitem(
...       _QzRDZYRDXSz_coll,
...       _QzRDZYRDXSz_key,
...       _QzE3BPTV2Tz_val),
...     _QzE3BPTV2Tz_val)[-1])())()
11

#> spam
>>> spam
{'b': 11}

See also: set!, zap@, Augmented assignment statements.