Exit e-book
Show all chapters
13
Bonus section: Additional ways for combining ZLayers
13. 
Bonus section: Additional ways for combining ZLayers

Sign up to our Newsletter

Signing up to our newsletter allows you to read all our ebooks.

I agree to receive marketing communication from Scalac.
You can unsubscribe from these communications at any time. For more information on how to unsubscribe, view our Privacy Policy.

Mastering Modularity in ZIO with Zlayer
13

Bonus section: Additional ways for combining ZLayers

So far we have explored how to combine ZLayers using the ++ operator for horizontal composition and the >>> operator for vertical composition, and most of the time those are the only operators you will need. However, there are other operators that you could use for certain situations.

The <> operator (which is a symbolic alias for the orElse operator), allows to combine two layers such that:

  • If the first layer succeeds when building an output, then the first layer is returned.
  • If the first layer fails when building an output, then the second layer is returned.

For example, in the following code we are combining two layers, and because the first one always fails when building an output, the second layer will be returned:

val layer: ULayer[ConfirmCommandParser] =
  ZLayer.fail("Error") <> ConfirmCommandParser.live

Next, we have the <&> operator (which is a symbolic alias for the zipPar operator), it is also a horizontal composition operator, however there is a slight difference. For example, if we use the ++ operator we get something like this:

val confirmModeDeps: ULayer[ConfirmCommandParser with ConfirmView] =
 ConfirmCommandParser.live ++ ConfirmView.live

And, if we use the <&> operator, the output type of the resulting layer will be a tuple instead:

val confirmModeDeps: ULayer[(ConfirmCommandParser, ConfirmView)] =
 ConfirmCommandParser.live <&> ConfirmView.live

Other operator we can use is +!+, which is for doing unsafe horizontal composition. And why is it unsafe? Well, because when using this operator the right hand side can overwrite the left hand side without us knowing about it. Let’s see an example of when this could happen, imagine we have this:

val confirmView: ULayer[ConfirmView] =
  ConfirmCommandParser.dummy ++ ConfirmView.live

As you can see, here we are ascribing the confirmView variable to a type ULayer[ConfirmView] instead of ULayer[ConfirmCommandParser with ConfirmView], and the compiler will be happy with it, because:

  • ConfirmCommandParser with ConfirmView is a subtype of ConfirmView
  • Since ULayer is covariant in its parameter, that means ULayer[ConfirmCommandParser with ConfirmView] is a subtype of ULayer[ConfirmView].

Having said that, confirmView has just a ConfirmView service inside it, according to the compiler; however we know that in reality it also has a ConfirmCommandParser dummy service.

Now, what happens if we do this:

val confirmModeDeps: ULayer[ConfirmCommandParser with ConfirmView] =
 ConfirmCommandParser.live +!+ confirmView

Well, what will happen is that the ConfirmCommandParser.live implementation will be overwritten by confirmView, because inside it there’s a hidden ConfirmCommandParser.dummy implementation. That’s really bad, because that means we could end running a dummy implementation in a production environment! This scenario won’t happen if we use the ++ operator, because it always prunes the right hand side before doing the composition. And what is pruning about? In this case, the type of confirmView says that it just contains a ConfirmView service inside it, so pruning ensures that’s actually true, removing the hidden ConfirmCommandParser.dummy so that it won’t overwrite the ConfirmCommandParser.live implementation by accident.

After having seen the dangers of using the +!+ operator you must be wondering: why would I ever use it? And the answer is: if you really know what you are doing and if you want to do a more performant composition of layers (because there are no pruning steps to be executed), then use +!+

Finally, we have the >+> operator, here you can see an example of its use:

val confirmModeDeps: ULayer[ConfirmCommandParser with ConfirmView] =
 ConfirmCommandParser.live ++ ConfirmView.live

val confirmModeNoDeps: ULayer[ConfirmCommandParser with ConfirmView with ConfirmMode] =
 confirmModeDeps >+> ConfirmMode.live

The >+> operator is equivalent to a combination of horizontal and vertical composition, like this:

val confirmModeDeps: ULayer[ConfirmCommandParser with ConfirmView] =
 ConfirmCommandParser.live ++ ConfirmView.live

val confirmModeNoDeps: ULayer[ConfirmCommandParser with ConfirmView with ConfirmMode] =
 confirmModeDeps ++ (confirmModeDeps >>> ConfirmMode.live)

And, if we compare this to using the >>> operator:

val confirmModeDeps: ULayer[ConfirmCommandParser with ConfirmView] =
 ConfirmCommandParser.live ++ ConfirmView.live

val confirmModeNoDeps: ULayer[ConfirmMode] = confirmModeDeps >>> ConfirmMode.live

You can realize the difference of using the  >+> operator is that the outputs of confirmModeDeps were included in the outputs of confirmModeNoDeps. So, if that’s something you need, now you know which operator to use.

PREVIOUS
Chapter
12
NEXT
Chapter
14