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

Optics Rendering #1103

Merged
merged 19 commits into from
Jul 27, 2023
Merged

Optics Rendering #1103

merged 19 commits into from
Jul 27, 2023

Conversation

lewisjkl
Copy link
Contributor

Adds the ability to render optics (Prisms and Lenses) in the generated code. The idea is that there will be very basic optic capabilities built into the smithy4s.core and then if users want something more advanced they can use implicit conversions to use something like monocle (see docs in the PR for more info).

Currently Lens instances are generated for structures and Prisms are generated for Unions and Enums.

For newtypes, I added a .value function on prisms/lenses that uses the bijection to automatically create a new lens/prism that targets the inner type of the newtype. I opted for this because then users can choose to create lenses/prisms that target the newtype or the inner type depending on their preferences. Lmk if you think there is a cleaner approach though.

@Baccata
Copy link
Contributor

Baccata commented Jul 20, 2023

@lewisjkl, in the view of the size of the diff, I think it'd be worth having two different ways of enabling optics :

  1. one trait-driven
  2. one metadata-driven

The inlined samples would only use the trait-driven one on a select few shapes (a struct, a union, an enum, etc) to showcase the feature

* Allows abstracting over an optional target by pointing to
* the inside of the optional value (the value inside of the [[Some]]).
*/
final override def some[A0](implicit
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

* Helper function for targeting the value inside of a [[smithy4s.Newtype]]
* or other type with an implicit [[Bijection]] available.
*/
final def value[A0](implicit bijection: Bijection[A0, A]): Lens[S, A0] =
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

* Lens implementation which can be used to abstract over accessing/updating
* a member of a product type
*/
trait Lens[S, A] extends Optional[S, A] { self =>
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@kubukoz kubukoz mentioned this pull request Jul 20, 2023
3 tasks
@@ -15,8 +15,8 @@ object AddMenuItemRequest extends ShapeTag.Companion[AddMenuItemRequest] {
val hints: Hints = Hints.empty

object Optics {
val restaurant = Lens[AddMenuItemRequest, String](_.restaurant)(n => a => a.copy(restaurant = n))
val menuItem = Lens[AddMenuItemRequest, MenuItem](_.menuItem)(n => a => a.copy(menuItem = n))
val restaurantLens = Lens[AddMenuItemRequest, String](_.restaurant)(n => a => a.copy(restaurant = n))
Copy link
Contributor

Choose a reason for hiding this comment

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

is it necessary to have the Lens suffix ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Scala 2.12 was complaining that it couldn't tell if the name inside of the .copy(myName = ???) was the name of the parameter of the case class or the name of the val.. not sure if there is another way around this or not, but that's why I added the suffix to disambiguate

Copy link
Member

Choose a reason for hiding this comment

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

strange, this seems to compile for me:

//> using scala "2.12.18"
trait Lens[A, B]

object Lens {
  def apply[A, B](f: A => B)(g: (A, B) => A): Lens[A, B] = ???
}

case class Hello(name: String)

object Hello {

  val name: Lens[Hello, String] = Lens[Hello, String](_.name)((c, n) => c.copy(name = n))
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahh the issue is that 2.12 needs the type annotation. The following fails:

//> using scala 2.12

trait Lens[A, B]

object Lens {
  def apply[A, B](f: A => B)(g: B => A => A): Lens[A, B] = ???
}

case class Hello(name: String)

object Hello {
  val name  = Lens[Hello, String](_.name)(n => c => c.copy(name = n))
}

@Baccata
Copy link
Contributor

Baccata commented Jul 26, 2023

@kubukoz, Jeff and I tried to unify the rendering of Lenses/Prisms with Fields/Alts. Our conclusion is that trying for this unification requires way to many changes for little benefit, so we're gonna roll with this current PR, after Jeff syncs it with the latest 0.18

If you're curious about our attempt to unify, here's a diff (it introduces a bug that makes 4 aws-related tests fail, not gonna bother attempting to fix) :series/0.18...optics-in-fields-and-alts

@kubukoz
Copy link
Member

kubukoz commented Jul 26, 2023

I appreciate the info!

@Baccata Baccata merged commit 919b7ad into disneystreaming:series/0.18 Jul 27, 2023
10 checks passed
@lewisjkl lewisjkl deleted the optics branch July 27, 2023 13:16
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