Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setter -> Traversal #32

Open
Gurkenglas opened this issue Aug 26, 2021 · 3 comments
Open

Setter -> Traversal #32

Gurkenglas opened this issue Aug 26, 2021 · 3 comments

Comments

@Gurkenglas
Copy link
Contributor

Gurkenglas commented Aug 26, 2021

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.

@ingolemo
Copy link
Owner

ingolemo commented Aug 26, 2021

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.

@Gurkenglas
Copy link
Contributor Author

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 ^^.

@ingolemo
Copy link
Owner

ingolemo commented Sep 8, 2021

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants