Skip to content

Implement lens composition #344

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

Merged
merged 1 commit into from
Dec 2, 2019
Merged

Implement lens composition #344

merged 1 commit into from
Dec 2, 2019

Conversation

Ralith
Copy link
Collaborator

@Ralith Ralith commented Nov 23, 2019

No description provided.

@Ralith Ralith force-pushed the lens-ext branch 3 times, most recently from af4b3ce to 417311a Compare November 23, 2019 21:14
@cmyr cmyr requested a review from raphlinus November 25, 2019 17:05
@Ralith
Copy link
Collaborator Author

Ralith commented Nov 26, 2019

Rebased and made independent.

@Ralith
Copy link
Collaborator Author

Ralith commented Nov 26, 2019

Rebased back onto #341 for the sake of tests, renamed Pipe to Then.

Copy link
Contributor

@raphlinus raphlinus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally this looks good and I'm happy to see it land. A few comments and suggestions inline.

}
}

/// `Lens` for an offset value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced of the general usefulness of Mul and Add here. I can see their use for the temperature conversion 7guis example, but can't easily think of other uses. So I think they make sense as an example of what lenses can do, but prefer not to have them in the main docs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are motivated by my own real-world use, where I want a slider to control a value whose range is not 0-1. They're particularly convenient because they bake in the inverse operation, which is an error/correctness risk otherwise. I'll see if I can maybe expand Iso into something more convenient for this case while retaining generality.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh right. I wonder whether longer term we want to do it this way (emphasizing the simplicity of individual components and composing them), or make the slider more configurable. But yes, that is an additional use case, and argues in favor of including them.

};
}

/// `Lens` composed of two lenses joined together
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely one of the most important combinators, thanks! I think I'm good with "Then", but was also thinking about names more reminiscent of functional composition.

Copy link
Collaborator Author

@Ralith Ralith Nov 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to suggestions! This has some precedent, with Option::and_then and moreso FutureExt::then.

}

impl<T, U, B: ?Sized> Then<T, U, B> {
pub fn new<A: ?Sized, C: ?Sized>(left: T, right: U) -> Self
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring should probably point to the then method. Also, I'm not sure "left" and "right" are clear names, though I can't think of more authoritative ones off the top of my head. (I feel like I should read a primer on lenses or compositional mathematic operations)

I know in Haskell it's just function composition, but that doesn't seem available here.

}

/// `Lens` for indexing containers
pub struct Index<I> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm impressed we can use the Rust Index trait - I was kinda thinking we'd need separate ones for Vec, etc. I am wondering, though, what we need to punch through the Arc in the common Arc<Vec> combination.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a deref combinator is possible. Alternatively, for better ergonomics maybe Deref can be baked directly into this? I'll experiment some tonight.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither of those approaches admit a reasonable way to inject the Arc::make_mut, so probably a different tactic is needed for that.

@@ -26,7 +26,8 @@ mod command;
mod data;
mod env;
mod event;
mod lens;
pub mod lens;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't want the modules in the namespace, just the items such as Lens and so on. I can see some possible issues with the intermediate structs, in which case we should probably be using pub(crate) if impl Lens will work as a type, or failing that, #[doc(hidden)].

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

impl Lens is unfortunately impossible in an extension trait for the time being. I'm not sure how I feel about going out of our way to hide these types; what's the upside?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not so much that we want to hide the types, as that we want to avoid modules being exported directly into the namespace. This is recent reorg work by @cmyr and maybe he has stronger feelings about it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I like pub mod foo { struct Bar; } when I would otherwise have to do struct FooBar;, which I think is the case here; the lens names are pretty vague out of context.

Copy link
Member

@cmyr cmyr Nov 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm 100% okay with some public modules in the case where there is a clear argument for them, e.g. they have tons of exports and it is clarifying to keep them separate, and this sounds like a example of that.

I mostly just don't want public modules to be the default choice.

@Ralith Ralith force-pushed the lens-ext branch 3 times, most recently from e162f67 to a7ebc14 Compare December 1, 2019 16:51
@Ralith Ralith changed the title Introduce some simple lens combinators Implement lens composition Dec 1, 2019
@Ralith
Copy link
Collaborator Author

Ralith commented Dec 1, 2019

Stripped this down to the obviously correct portion; will continue to explore more advanced stuff in one or more new PRs.

@Ralith
Copy link
Collaborator Author

Ralith commented Dec 1, 2019

Renamed the set helper to put for greater edit distance from get.

Copy link
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay a few nitty thoughts, but then feel free to merge this.

@Ralith Ralith merged commit f8791a8 into linebender:master Dec 2, 2019
@Ralith Ralith deleted the lens-ext branch December 2, 2019 17:02
@cmyr cmyr mentioned this pull request Dec 31, 2019
7 tasks
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

Successfully merging this pull request may close these issues.

3 participants