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

Restrict over', iover', and set' to traversals #475

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

treeowl
Copy link
Contributor

@treeowl treeowl commented Dec 28, 2022

  • over', iover', set', and associated operators previously
    accepted setters. However, it's impossible to actually modify strictly
    through a setter; a traversal is needed for that. Restrict the types
    to require A_Traversal, and remove the associated (technically
    correct but deceptive) Mapping instances.

  • Document the strictness behavior of set'.

  • The internal Identity' type was non-standard and overly complicated.
    The Solo type, now in base, is operationally identical and simpler to
    deal with. Use that instead.

  • Remove some surprisingly lazy matches on Identity'/Solo.

Fixes #473

* The `Identity'` type was non-standard and overly complicated.  The
  `Solo` type, now in `base`, is operationally identical and simpler to
  deal with. Use that instead.

* Remove some surprisingly lazy matches on `Identity'`.
* `over'`, `iover'`, `set'`, and associated operators previously
  accepted setters. However, it's impossible to actually modify strictly
  through a setter; a traversal is needed for that. Restrict the types
  to require `A_Traversal`, and remove the associated (technically
  correct but deceptive) `Mapping` instances.

* Document the strictness behavior of `set'`.

Fixes well-typed#473
@treeowl
Copy link
Contributor Author

treeowl commented Dec 28, 2022

I think it would be rather nice to add the strictly combinator I've proposed for lens, but it would probably take me a bit to learn enough about how this package works to be able to do that myself.

Copy link
Member

@adamgundry adamgundry left a comment

Choose a reason for hiding this comment

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

Thanks very much for contributing @treeowl! Sorry for the lag in responding. I'm not extremely familiar with the strictness stuff here, but the general idea seems plausible to me.

@arybczak would you be able to take a look, as you are probably more familiar with the relevant code?

Comment on lines 104 to +111
over'
:: Is k A_Setter
:: Is k A_Traversal
=> Optic k is s t a b
-> (a -> b) -> s -> t
-- See [Note: Solo wrapping]
over' o = \f ->
let star = getOptic (castOptic @A_Setter o) $ Star (wrapIdentity' . f)
in unwrapIdentity' . runStar star
let star = getOptic (castOptic @A_Traversal o) $ Star ((Solo $!) . f)
in getSolo . runStar star
Copy link
Member

Choose a reason for hiding this comment

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

Should we move set' and over' to Optics.Traversal instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They would be a better fit, probably; I was just trying to avoid breaking things unnecessarily.

@@ -77,6 +77,7 @@ library
, indexed-profunctors >= 0.1 && <0.2
, transformers >= 0.5 && <0.7
, indexed-traversable >= 0.1 && <0.2
, OneTuple >= 0.3 && <0.4
Copy link
Member

Choose a reason for hiding this comment

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

This looks like it might pull in quite a few extra dependencies to optics-core. Is that right, and can we avoid it?

Copy link
Member

Choose a reason for hiding this comment

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

Actually, perhaps this is true only for older GHC versions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It looks to me like the package is quite careful to avoid excessive dependencies when possible.

Copy link
Collaborator

@arybczak arybczak Jan 29, 2023

Choose a reason for hiding this comment

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

For psychological reasons I'd prefer us to not include this dependency.

The cost of not doing so is irrelevant - Solo can be copy pasted where Identity' was along with its Applicative instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@arybczak Psychological reasons? Why put the compat code here when someone else has already written it there?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Psychological reasons?

Yes, people don't like a lot of deps in "core" packages. Whether it's rational or not is irrelevant.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay ... I'll copy whatever's needed over.

Comment on lines +145 to +147
-- The new value will be forced if and only if the optic traverses at
-- least one target. If forcing the new value is inexpensive, then it
-- is cheaper to do so manually and use 'set'.
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we could easily test this, since we didn't before and apparently didn't get it right...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure what the intention is here. I'm just documenting what it actually does.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Or were you referring to testing strictness generally? containers does some of that; I haven't checked if there's traversal strictness testing, but if so that could be copied maybe.

@@ -102,14 +102,28 @@ over o = \f -> runFunArrow $ getOptic (castOptic @A_Setter o) (FunArrow f)
-- 'over' is used, because the first coordinate of a pair is never forced.
--
over'
Copy link
Member

Choose a reason for hiding this comment

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

The haddocks above here have another "TODO DOC" it would be great to nail along with this.

Copy link
Contributor Author

@treeowl treeowl Jan 28, 2023

Choose a reason for hiding this comment

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

The intuition is that each target result is forced before producing the full result. More precisely, I believe

over' p f xs = forceElements res `seq` res
  where
    forceElements = foldrOf p seq ()
    res = over p f xs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That "law" only works with a non-type-changing traversal, but I think it's a good way to express the concept.

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.

over' and iover' pretend to work with incompatible optics
3 participants