-
Notifications
You must be signed in to change notification settings - Fork 14
Description
We're running into occasional annoying problems because the ubiquitous Python iterators are mutable. Clojure doesn't have this problem because it uses the immutable seq abstraction instead. A seq can still have the lazyness of some iterables, but getting the next item won't change what the first seq means.
I've been playing around with a Python implementation of seq. Here's a rough attempt. This could easily be translated to Hy.
from collections.abc import Iterable
class LazyCons:
__slots__ = ['car', 'cdr']
def __init__(self, car, cdr):
self.car = lambda: car
def lazy_cdr():
res = cdr()
self.cdr = lambda: res
return res
self.cdr = lazy_cdr
class Seq(LazyCons, Iterable):
__slots__ = ()
def __init__(self, iterable):
it = iter(iterable)
car = next(it)
super().__init__(car, lambda: Seq(it))
def __iter__(self):
def iterator():
seq = self
while 1:
yield seq.car()
seq = seq.cdr()
return iterator()And a function to demonstrate the laziness.
def verboseRange(*stop_startstopstep):
for i in range(*stop_startstopstep):
print('yielding', i)
yield iAnd the demonstration:
>>> spam = Seq(iter(verboseRange(5))) # note iter() call
yielding 0
>>> spam.car()
0
>>> spam.car()
0
>>> spam.cdr()
yielding 1
<__main__.Seq object at 0x0000019C4D773D68>
>>> spam.cdr().car()
1
>>> list(spam)
yielding 2
yielding 3
yielding 4
[0, 1, 2, 3, 4]
>>> spam.car()
0That iter() call isn't required, but it demonstrates converting a mutable iterator into an immutable seq (provided nothing else has a reference to that iterator).
Some more ideas:
Clojure often converts collections to seq's automatically. Rather than add that to a lot of Hy functions, we might instead have a short reader macro to do it. Although, (seq xs) is not that long to begin with.
We could also change the __repr__ of a Seq to print off up to the first 20 items or something. This would make seq's a lot more friendly in the repl, and lazy Python iterables too especially, when combined with the reader macro.