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

Add all the typeclasses instance for the zio.prelude.data.Optional type (copied from the Option instances) #1415

Merged
merged 19 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package zio.prelude

import zio._
import zio.prelude.coherent.CovariantIdentityBoth
import zio.prelude.data.Optional
import zio.prelude.newtypes.{AndF, Failure, OrF}
import zio.stm.ZSTM
import zio.stream.{ZSink, ZStream}
Expand Down Expand Up @@ -1185,9 +1186,31 @@ object AssociativeBoth extends AssociativeBothLowPriority {
Some(())

def both[A, B](fa: => Option[A], fb: => Option[B]): Option[(A, B)] =
(fa, fb) match {
case (Some(a), Some(b)) => Some((a, b))
case _ => None
fa match {
case Some(a) =>
fb match {
case Some(b) => Some((a, b))
case None => None
}
case None => None
}
}
Comment on lines +1189 to +1197
Copy link
Member Author

Choose a reason for hiding this comment

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

Just optimizing the code here


/**
* The [[IdentityBoth]] (with [[AssociativeBoth]]) instance for [[Optional]].
*/
implicit val OptionalIdentityBoth: IdentityBoth[Optional] =
new IdentityBoth[Optional] {
val any: Optional[Any] = Optional.Present(())

def both[A, B](fa: => Optional[A], fb: => Optional[B]): Optional[(A, B)] =
fa match {
case Optional.Present(a) =>
fb match {
case Optional.Present(b) => Optional.Present((a, b))
case Optional.Absent => Optional.Absent
}
case Optional.Absent => Optional.Absent
}
}

Expand Down
12 changes: 12 additions & 0 deletions core/shared/src/main/scala/zio/prelude/AssociativeEither.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package zio.prelude

import zio._
import zio.prelude.data.Optional
import zio.prelude.newtypes.Failure
import zio.stream.ZStream

Expand Down Expand Up @@ -157,6 +158,17 @@ object AssociativeEither {
None
}

/**
* The [[IdentityEither]] (and [[AssociativeEither]]) instance for [[Optional]].
*/
implicit val OptionalIdentityEither: IdentityEither[Optional] =
new IdentityEither[Optional] {
def either[A, B](fa: => Optional[A], fb: => Optional[B]): Optional[Either[A, B]] =
fa.map(Left(_)) orElse fb.map(Right(_))

val none: Optional[Nothing] = Optional.Absent
}

/**
* The `AssociativeEither` instance for `Schedule`.
*/
Expand Down
11 changes: 11 additions & 0 deletions core/shared/src/main/scala/zio/prelude/AssociativeFlatten.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package zio.prelude

import zio._
import zio.prelude.data.Optional
import zio.stm.ZSTM
import zio.stream.ZStream

Expand Down Expand Up @@ -162,6 +163,16 @@ object AssociativeFlatten {
def flatten[A](ffa: Option[Option[A]]): Option[A] = ffa.flatten
}

/**
* The [[AssociativeFlatten]] and [[IdentityFlatten]] instance for [[Optional]].
*/
implicit val OptionalIdentityFlatten: IdentityFlatten[Optional] =
new IdentityFlatten[Optional] {
def any: Optional[Any] = Optional.Present(())

def flatten[A](ffa: Optional[Optional[A]]): Optional[A] = ffa.flatten
}

/**
* The `AssociativeFlatten` and `IdentityFlatten` instance for `Try`.
*/
Expand Down
27 changes: 24 additions & 3 deletions core/shared/src/main/scala/zio/prelude/CommutativeBoth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package zio.prelude

import zio._
import zio.prelude.data.Optional
import zio.prelude.newtypes.{AndF, Failure, OrF}
import zio.stream.{ZSink, ZStream}

Expand Down Expand Up @@ -84,9 +85,29 @@ object CommutativeBoth {
implicit val OptionCommutativeBoth: CommutativeBoth[Option] =
new CommutativeBoth[Option] {
def both[A, B](fa: => Option[A], fb: => Option[B]): Option[(A, B)] =
(fa, fb) match {
case (Some(a), Some(b)) => Some((a, b))
case _ => None
fa match {
case Some(a) =>
fb match {
case Some(b) => Some((a, b))
case None => None
}
case None => None
}
}

/**
* The [[CommutativeBoth]] instance for [[Optional]].
*/
implicit val OptionalCommutativeBoth: CommutativeBoth[Optional] =
new CommutativeBoth[Optional] {
def both[A, B](fa: => Optional[A], fb: => Optional[B]): Optional[(A, B)] =
fa match {
case Optional.Present(a) =>
fb match {
case Optional.Present(b) => Optional.Present((a, b))
case Optional.Absent => Optional.Absent
}
case Optional.Absent => Optional.Absent
}
}

Expand Down
10 changes: 10 additions & 0 deletions core/shared/src/main/scala/zio/prelude/Derive.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package zio.prelude

import zio.prelude.data.Optional
import zio.{Cause, Chunk, Exit, NonEmptyChunk}

import scala.util.Try
Expand Down Expand Up @@ -100,6 +101,15 @@ object Derive {
Equal.OptionEqual
}

/**
* The [[DeriveEqual]] instance for [[Optional]].
*/
implicit val OptionalDeriveEqual: DeriveEqual[Optional] =
Copy link
Member Author

Choose a reason for hiding this comment

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

@sideeffffect I didn't find the tests for this typeclass 🤔

new DeriveEqual[Optional] {
def derive[A: Equal]: Equal[Optional[A]] =
Equal.OptionalEqual
}

/**
* The `DeriveEqual` instance for `ParSeq`.
*/
Expand Down
13 changes: 12 additions & 1 deletion core/shared/src/main/scala/zio/prelude/Equal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ package zio.prelude

import zio.Exit.{Failure, Success}
import zio.prelude.coherent.{HashOrd, HashPartialOrd}
import zio.{Cause, Chunk, Duration => ZIODuration, Exit, FiberId, NonEmptyChunk, StackTrace}
import zio.prelude.data.Optional
import zio.{Cause, Chunk, Exit, FiberId, NonEmptyChunk, StackTrace, Duration => ZIODuration}

import scala.annotation.{implicitNotFound, nowarn}
import scala.concurrent.duration.{Duration => ScalaDuration}
Expand Down Expand Up @@ -353,6 +354,16 @@ object Equal extends EqualVersionSpecific {
case _ => false
}

/**
* Derives an `Equal[Optional[A]]` given an `Equal[A]`.
*/
implicit def OptionalEqual[A: Equal]: Equal[Optional[A]] =
Copy link
Member Author

Choose a reason for hiding this comment

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

@sideeffffect I didn't find the tests for this typeclass 🤔

make {
case (Optional.Absent, Optional.Absent) => true
case (Optional.Present(a1), Optional.Present(a2)) => a1 === a2
case _ => false
}

/**
* `PartialOrd` and `Hash` (and thus also `Equal`) instance for `Set[A]` values.
* Due to the limitations of Scala's `Set`,
Expand Down
10 changes: 10 additions & 0 deletions core/shared/src/main/scala/zio/prelude/Invariant.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package zio.prelude

import zio._
import zio.prelude.coherent.CovariantIdentityBoth
import zio.prelude.data.Optional
import zio.prelude.newtypes.Failure
import zio.stm.ZSTM
import zio.stream.{ZSink, ZStream}
Expand Down Expand Up @@ -751,6 +752,15 @@ object Invariant extends LowPriorityInvariantImplicits with InvariantVersionSpec
option.fold[G[Option[B]]](Option.empty.succeed)(a => f(a).map(Some(_)))
}

/**
* The [[ForEach]] (and thus [[Covariant]] and [[Invariant]]) instance for [[Optional]].
*/
implicit val OptionalForEach: ForEach[Optional] =
new ForEach[Optional] {
def forEach[G[+_]: IdentityBoth: Covariant, A, B](option: Optional[A])(f: A => G[B]): G[Optional[B]] =
option.fold[G[Optional[B]]](Optional.Absent.succeed)(a => f(a).map(Optional.Present(_)))
}

/**
* The `Covariant` (and thus `Invariant`) instance for `Schedule`
*/
Expand Down
9 changes: 3 additions & 6 deletions core/shared/src/main/scala/zio/prelude/data/Optional.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ sealed trait Optional[+A] { self =>
case Optional.Absent => ifEmpty
}

final def flatten[B](implicit ev: A <:< Option[B]): Option[B] =
final def flatten[B](implicit ev: A <:< Optional[B]): Optional[B] =
Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't understand why the evidence what proving that A was an Option[B] nor why this method was returning an Option[B] and not an Optional[B].
Probably a copy/paste mistake

self match {
case Optional.Present(get) => ev(get)
case Optional.Absent => None
Expand Down Expand Up @@ -119,11 +119,8 @@ sealed trait Optional[+A] { self =>
case Optional.Absent => Optional.Absent
}

final def orElse[B >: A](other: Optional[B]): Optional[B] =
self match {
case Optional.Present(_) => self
case Optional.Absent => other
}
final def orElse[B >: A](alternative: => Optional[B]): Optional[B] =
Copy link
Member Author

Choose a reason for hiding this comment

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

The Option#orElse method is lazy

if (isEmpty) alternative else this
Copy link
Member Author

Choose a reason for hiding this comment

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

Code copied from Option#orElse. Avoids the call to the unapply function in Optional.Present(_)


final def iterator: Iterator[A] =
self match {
Expand Down
Loading