map and filter
Native lazy iteration builtins. map applies a function to each
element of a source; filter keeps the elements a predicate
approves. Both accept any iterable – a sequence, a koskari generator,
a python generator, or a python async iterator – and both can be
consumed synchronously or asynchronously.
Unlike their python counterparts, these run inside the koskari evaluator: a koskari function passed as the mapper or predicate is driven directly, with no wrapping, and may await.
map
Syntax: (map FUNC ITERABLE ...)
Return a lazy iterator producing (FUNC element) for each element of
ITERABLE. Nothing is consumed until the map itself is iterated.
(list (map (lambda [x] (* x 10)) #[1 2 3]))
; #[10 20 30]
With several iterables, FUNC takes one element from each per
step, in the order the iterables are written, and the map ends with
the shortest of them. The iterables are stepped left to right, so once
one is spent the ones after it are not asked for that step.
(list (map + #[1 2 3] #[10 20 30]))
; #[11 22 33]
(list (map (lambda [a b] (#tuple a b)) #[1 2 3] "ab"))
; #[#(1 "a") #(2 "b")]
FUNC may be any callable: a koskari function, a python function,
or another callable object. A koskari function that suspends works
transparently when the map is consumed in an asynchronous context:
import-from asyncio sleep
def slow-double [x]:
(sleep 0)
(* x 2)
for-each [v (map slow-double #[1 2 3])]:
(print v)
A koskari function awaits a suspending call such as sleep on its own;
no await is written.
Matching python’s map, a mapped function that raises
StopIteration truncates the iteration.
filter
Syntax: (filter PRED ITERABLE)
Return a lazy iterator producing the elements of ITERABLE for
which (PRED element) is true. PRED may be None, keeping
the truthy elements – the same convention as python’s filter.
(list (filter (lambda [x] (> x 1)) #[1 2 3]))
; #[2 3]
(list (filter None #[0 1 "" "x"]))
; #[1 "x"]
Like map, the predicate may be a koskari function and may await.
Consuming a map or filter
Both are lazy single-pass iterators, usable from either surface:
synchronously:
(list ...),(next ...),for-each, or pythonlist(),forandnext(iter(m)). A map or filter is an iterable, not an iterator, so pythonnext()needs theiter()step. A source or function that genuinely suspends on an awaitable cannot be driven this way and raisesKoskariSyncViolation.asynchronously:
for-eachin an asynchronous context, or pythonasync for. Awaits in the function, the predicate, or the source pass through transparently.
Exhaustion is permanent: a drained map or filter yields nothing on
later iteration. Closing one (python aclose, or generator close
semantics) closes its source.
Type predicates
map? and filter? test for the native types:
(map? (map str #[1])) ; True
(filter? (py-filter None #[1])) ; False
py-map and py-filter
The python builtins remain available as py-map and
py-filter, with py-map? and py-filter? as their type
predicates. See the python documentation for
map and
filter.
(list (py-map str #[1 2])) ; #["1" "2"]
(py-map? (py-map str #[1])) ; True
The python builtins call their function through the python calling
convention. A koskari function used there behaves as it would in any
python higher-order function: wrap it with demand so each call
returns a completed value. The native map and filter need
no wrapping.