Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@guersam
Copy link
Contributor

@guersam guersam commented Sep 8, 2023

/claim #8106

This PR adds fully working implementation of ZLayer.derive, including:

  • Both Scala 2 and 3
  • Derivation for regular concrete class (not only case classes), including ones with curried constructor
  • Parameterized polymorphism
  • Default instances via ZLayer.Default[A]
    • Automatic config load using ZIO.config when an implicit Config[A] exists
  • Lifecycle Hooks via ZLayer.LifecycleHooks[R, E]
  • Documentation

Copy link
Contributor

@adamgfraser adamgfraser left a comment

Choose a reason for hiding this comment

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

Couple of specific comments but overall looks great!

@guersam guersam requested a review from adamgfraser September 9, 2023 05:18
@guersam
Copy link
Contributor Author

guersam commented Sep 10, 2023

Added support for parameterized polymorphism in 5d55e65. The Scala 3 part was much harder than I thought...

@guersam
Copy link
Contributor Author

guersam commented Sep 10, 2023

Addressed all feedbacks.


def acquire: ZIO[R, E, Any]

def release: ZIO[R, Nothing, Any]
Copy link
Contributor

Choose a reason for hiding this comment

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

Should release have access to the acquired resource?

trait AcquireRelease[-R, +E] extends Scoped[R, E] {
  type A
  final def scoped(implicit trace: Trace): ZIO[R & Scope, E, Any] =
    ZIO.acquireRelease(acquire)(release)
  def acquire: ZIO[R, E, A]
  def release(a: A): ZIO[R, Nothing, Any]
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How about just AcquireRelease[-R, +E, A]? A not being covariant is a bit inconsistent with the other datatypes but it's simpler.

Copy link
Contributor Author

@guersam guersam Sep 10, 2023

Choose a reason for hiding this comment

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

Here's the revised example with the new design:

import zio._
import java.io.File

def acquireLockFile(path: String): ZIO[Any, Throwable, File] = ???
def deleteFile(file: File): ZIO[Any, Throwable, Unit] = ???

class ASingletonService(lockFilePath: String) extends ZLayer.Derive.AcquireRelease[Any, Throwable, File] {

  override def acquire: ZIO[Any, Throwable, File] =
     acquireLockFile(lockFilePath)

  override def release(lockFile: File): ZIO[Any, Nothing, Any] =
     deleteFile(lockFile).ignore
}

@guersam guersam requested a review from adamgfraser September 10, 2023 14:21
@guersam
Copy link
Contributor Author

guersam commented Sep 13, 2023

@adamgfraser Could you take another look? AcquireRelease is slightly different than what you suggested, but I've applied all the feedback.

@adamgfraser
Copy link
Contributor

@guersam I like it!

* `ZLayer.Default[A]` to ensure correct type inference and dependency
* resolution during `ZLayer` derivation.
*/
trait Default[+A] {
Copy link
Contributor

Choose a reason for hiding this comment

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

This could probably be moved into the Derive companion object since like Scoped and AcquireRelease it really only has meaning within the context of derive.

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 thought about it too, but unlike Scoped sometimes it might be used like Default[A].layer without ZLayer.derive. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Besides the possible independent use case, putting Default inside of Derive can imply that the layer derivation process is aware of Default.

Copy link
Contributor

Choose a reason for hiding this comment

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

I tend to think there is not a use of Default independent of derive. Theoretically the concept of a default value could make sense in other contexts but practically there is nothing else that uses it and the fact that a default value could itself by constructed from a layer makes it very specific to the derivation.

I don't understand your comment about the derivation process being aware of default. The derivation process is aware of default in that if you construct something that has like a Ref where are going to look for a default value for the value inside the Ref. So the default values are intimately related to derivation and in fact derivation is the reason for their existence.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. I mean when we put Default in Derive it will reveal that relationship more explicitly. I'll move it into Derive.


object Derive {

/**
Copy link
Contributor

Choose a reason for hiding this comment

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

Documentation could be updated here to reflect the changes we have made.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated Scaladoc.

If you're uncertain about the exact type signature, a practical approach is to omit the type annotation initially. Then,
use your IDE's autocomplete feature to insert the inferred type.

## Lifecycle Hooks
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

Copy link
Contributor

@adamgfraser adamgfraser left a comment

Choose a reason for hiding this comment

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

Looks great! Just a few minor comments.

Copy link
Contributor Author

@guersam guersam left a comment

Choose a reason for hiding this comment

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

@adamgfraser Applied the latest feedback in 7e21b1b.


object Derive {

/**
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated Scaladoc.

* `ZLayer.Default[A]` to ensure correct type inference and dependency
* resolution during `ZLayer` derivation.
*/
trait Default[+A] {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved under ZLayer.Derive.

`A` implements the `ZLayer.Derive.Scoped[-R, +E]` trait, `ZLayer.derive[A]` automatically recognizes it. As a result,
the `scoped` effect is executed during the layer's construction and finalization phases.

The 'resource' might be a background task, a lock file, or etc., that can be managed by [`Scope`](../resource/scope.md).
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Improved doc about Scoped with an example.

Copy link
Contributor

@adamgfraser adamgfraser left a comment

Choose a reason for hiding this comment

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

Looks great! Thanks for your work on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants