python-lenses
python-lenses copied to clipboard
Setter -> Traversal
Python's nonlocal state side effects give every setter traversal powers. You should unify them or have a utility to convert them.
I say this because using re.sub
(which allows a function argument) for a traversal is turning out much harder than the lambda regex: Setter(lambda f s: re.sub(regex, f, s))
it should be.
I'm not sure I understand what you're trying to say. I don't get how side-effects could unify setters and traversals.
If you want a traversal that focuses parts of a string that are matched by a regex, then you'd write it like this:
import re
from lenses import lens
def regex_traversal(pattern, flags=0):
def folder(state):
for match in re.finditer(pattern, state, flags=flags):
yield match.group(0)
def builder(state, values):
iterator = iter(values)
return re.sub(pattern, lambda _: next(iterator), state, flags=flags)
return lens.Traversal(folder, builder)
state = "First thou pullest the Holy Pin"
state &= regex_traversal("\w+") + "!"
print(state) # "First! thou! pullest! the! Holy! Pin!"
If there's a better way to write this I'd like to know.
def setter_traversal(setter):
def folder(state):
acc = []
setter(acc.append, state)
return iter(acc)
def builder(state, values):
iterator = iter(values)
return setter(lambda _: next(iterator), state)
return lens.Traversal(folder, builder)
state = "First thou pullest the Holy Pin"
state &= setter_traversal(lambda f,x: re.sub("\w+", lambda m: f(m.group(0)), x)) + "!"
print(state) # "First! thou! pullest! the! Holy! Pin!"
builder should take a generator of values instead of a list, of course.
For better streaming:
def folder(state):
g = greenlet.greenlet(lambda _: setter(lambda a: ret.switch(a), state))
while not g.dead:
ret = greenlet.getcurrent()
result = g.switch(None)
if not g.dead:
yield result
I spent a few days there trying to stream Traversal.func correctly for an arbitrary Applicative ^^.
How useful would it be to have a general convenience method for this? Haskell-style setter functions (a -> b) -> s -> t
aren't particularly common in python. Does anyone know of any in the standard library other than re.sub
?