-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Port tests to Dotty #3552
Port tests to Dotty #3552
Changes from all commits
9e44cfe
d90679d
824deb1
d68da01
94f9dde
610c528
2f17ada
7c7b8f7
3d9b407
8cca359
3d773d4
6ae0881
d94e07a
8abdf3c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package cats.syntax | ||
|
||
import cats.{Alternative, Monad} | ||
|
||
final class MonadOps[F[_], A](private val fa: F[A]) extends AnyVal { | ||
def whileM[G[_]](p: F[Boolean])(implicit M: Monad[F], G: Alternative[G]): F[G[A]] = M.whileM(p)(fa) | ||
def whileM_(p: F[Boolean])(implicit M: Monad[F]): F[Unit] = M.whileM_(p)(fa) | ||
def untilM[G[_]](p: F[Boolean])(implicit M: Monad[F], G: Alternative[G]): F[G[A]] = M.untilM(fa)(p) | ||
def untilM_(p: F[Boolean])(implicit M: Monad[F]): F[Unit] = M.untilM_(fa)(p) | ||
def iterateWhile(p: A => Boolean)(implicit M: Monad[F]): F[A] = M.iterateWhile(fa)(p) | ||
def iterateUntil(p: A => Boolean)(implicit M: Monad[F]): F[A] = M.iterateUntil(fa)(p) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package cats.syntax | ||
|
||
import cats.{Monad, Alternative} | ||
|
||
final class MonadOps[F[_], A](private val fa: F[A]) extends AnyVal { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Had to split this out due to scala/scala3#9480 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't this flip the order of parameters though? That could be moderately source-incompatible. More importantly, it's inconsistent with the other syntax classes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually don't know how dotty compiles this down. Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would summon @smarter actually if I hit this. Probably worth looking at the bytecode quickly to see what the parameter order is. Can we simply put the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's source-compatible (to explicitly pass parameters to a using block, you have to write There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would increase the maintenance burden though. But if you did want to rewrite all the ops classes for dotty, then you might as well use extension syntax which would look more natural: extension [F[_], A](fa: F[A])(using M: Monad[F]) {
def whileM_(p: F[Boolean]): F[Unit] = ???
def untilM(...)
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm just concerned that we're going to end up hitting something like this again in the future, but once we release on Dotty, we can't futz with the binary compatibility any further. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, you can break BC every time we break BC which is every six weeks until 3.0 is out probably, but afterwards it gets tricky yeah :) (though I guess you can always play trick with something like a package-protected overload to preserve BC?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we promising binary compatibility for dotty artifacts? Should we? (We certainly don't test it yet) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we're promising BC until Scala 3 goes final. Once it does though, we 100% should be promising it. |
||
def whileM[G[_]](using M: Monad[F], G: Alternative[G])(p: F[Boolean]): F[G[A]] = M.whileM(p)(fa) | ||
def whileM_(using M: Monad[F])(p: F[Boolean]): F[Unit] = M.whileM_(p)(fa) | ||
def untilM[G[_]](using M: Monad[F], G: Alternative[G])(p: F[Boolean]): F[G[A]] = M.untilM(fa)(p) | ||
def untilM_(using M: Monad[F])(p: F[Boolean]): F[Unit] = M.untilM_(fa)(p) | ||
def iterateWhile(using M: Monad[F])(p: A => Boolean): F[A] = M.iterateWhile(fa)(p) | ||
def iterateUntil(using M: Monad[F])(p: A => Boolean): F[A] = M.iterateUntil(fa)(p) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -123,7 +123,6 @@ object Invariant extends ScalaVersionSpecificInvariantInstances with InvariantIn | |
implicit def catsFlatMapForSortedMap[K]: FlatMap[SortedMap[K, *]] = | ||
cats.instances.sortedMap.catsStdInstancesForSortedMap[K] | ||
implicit def catsBimonadForFunction0: Bimonad[Function0] = cats.instances.function.catsStdBimonadForFunction0 | ||
implicit def catsMonadForFunction1[I]: Monad[I => *] = cats.instances.function.catsStdMonadForFunction1[I] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved this down because dotty thinks |
||
implicit def catsContravariantMonoidalForFunction1[R: Monoid]: ContravariantMonoidal[* => R] = | ||
cats.instances.function.catsStdContravariantMonoidalForFunction1[R] | ||
implicit def catsFunctorForPair: Functor[λ[P => (P, P)]] = cats.instances.tuple.catsDataFunctorForPair | ||
|
@@ -285,19 +284,27 @@ object Invariant extends ScalaVersionSpecificInvariantInstances with InvariantIn | |
|
||
} | ||
|
||
private[cats] trait InvariantInstances0 extends TupleInstances0 { | ||
private[cats] trait InvariantInstances0 extends InvariantInstances1 { | ||
implicit def catsCommutativeMonadForTuple2[X](implicit X: CommutativeMonoid[X]): CommutativeMonad[(X, *)] = | ||
cats.instances.tuple.catsStdCommutativeMonadForTuple2[X] | ||
implicit def catsContravariantForFunction1[R]: Contravariant[* => R] = | ||
cats.instances.function.catsStdContravariantForFunction1[R] | ||
implicit def catsDistributiveForFunction0: Distributive[Function0] = cats.instances.function.function0Distributive | ||
implicit def catsDistributiveForFunction1[I]: Distributive[I => *] = | ||
cats.instances.function.catsStdDistributiveForFunction1[I] | ||
|
||
} | ||
|
||
private trait InvariantInstances1 extends InvariantInstances2 { | ||
implicit def catsMonadForFunction1[I]: Monad[I => *] = cats.instances.function.catsStdMonadForFunction1[I] | ||
} | ||
|
||
private[cats] trait InvariantInstances2 extends TupleInstances0 { | ||
implicit def catsApplicativeForArrow[F[_, _], A](implicit F: Arrow[F]): Applicative[F[A, *]] = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same with this one |
||
new ArrowApplicative[F, A](F) | ||
} | ||
|
||
private trait TupleInstances0 extends TupleInstances1 { | ||
private[cats] trait TupleInstances0 extends TupleInstances1 { | ||
implicit def catsCommutativeFlatMapForTuple2[X](implicit X: CommutativeSemigroup[X]): CommutativeFlatMap[(X, *)] = | ||
cats.instances.tuple.catsStdCommutativeFlatMapForTuple2[X] | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package cats.free | ||
|
||
import Free.{FlatMapped, Pure, Suspend} | ||
import cats.{Eval, Foldable} | ||
|
||
private[free] trait FreeFoldStep[S[_], A] { | ||
|
||
def step: Free[S, A] | ||
|
||
/** | ||
* A combination of step and fold. May be used to define interpreters with custom | ||
* (non-monoidial) control flow. | ||
*/ | ||
final def foldStep[B]( | ||
onPure: A => B, | ||
onSuspend: S[A] => B, | ||
onFlatMapped: ((S[X], X => Free[S, A]) forSome { type X }) => B | ||
): B = | ||
this.step match { | ||
case Pure(a) => onPure(a) | ||
case Suspend(a) => onSuspend(a) | ||
case FlatMapped(Suspend(fa), f) => onFlatMapped((fa, f)) | ||
case _ => sys.error("FlatMapped should be right associative after step") | ||
} | ||
|
||
final def foldLeft[B](fa: Free[S, A], b: B)(f: (B, A) => B)(implicit F: Foldable[S]): B = | ||
fa.foldStep( | ||
a => f(b, a), | ||
fa => F.foldLeft(fa, b)(f), | ||
{ case (fx, g) => F.foldLeft(fx, b)((bb, x) => foldLeft(g(x), bb)(f)) } | ||
) | ||
|
||
final def foldRight[B](fa: Free[S, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B])(implicit F: Foldable[S]): Eval[B] = | ||
fa.foldStep( | ||
a => f(a, lb), | ||
fa => F.foldRight(fa, lb)(f), | ||
{ case (fx, g) => F.foldRight(fx, lb)((a, lbb) => foldRight(g(a), lbb)(f)) } | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package cats.free | ||
|
||
import Free.{FlatMapped, Pure, Suspend} | ||
import cats.{Foldable, Eval} | ||
|
||
private[free] trait FreeFoldStep[S[_], A] { | ||
|
||
def step: Free[S, A] | ||
|
||
private type OnFlatMapped[X] = (S[X], X => Free[S, A]) | ||
|
||
/** | ||
* A combination of step and fold. May be used to define interpreters with custom | ||
* (non-monoidial) control flow. | ||
*/ | ||
final def foldStep[B]( | ||
onPure: A => B, | ||
onSuspend: S[A] => B, | ||
onFlatMapped: [X] => (S[X], X => Free[S, A]) => B | ||
): B = | ||
this.step match { | ||
case Pure(a) => onPure(a) | ||
case Suspend(a) => onSuspend(a) | ||
case FlatMapped(Suspend(fa), f) => onFlatMapped(fa, f) | ||
case _ => sys.error("FlatMapped should be right associative after step") | ||
} | ||
|
||
final def foldLeft[B](fa: Free[S, A], b: B)(f: (B, A) => B)(implicit F: Foldable[S]): B = | ||
fa.foldStep( | ||
a => f(b, a), | ||
fa => F.foldLeft(fa, b)(f), | ||
[X] => (sx: S[X], g: X => Free[S, A]) => F.foldLeft(sx, b)((bb, x) => foldLeft(g(x), bb)(f)) | ||
) | ||
|
||
final def foldRight[B](fa: Free[S, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B])(implicit F: Foldable[S]): Eval[B] = | ||
fa.foldStep( | ||
a => f(a, lb), | ||
fa => F.foldRight(fa, lb)(f), | ||
[X] => (sx: S[X], g: X => Free[S, A]) => F.foldRight(sx, lb)((a, lbb) => foldRight(g(a), lbb)(f)) | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import cats.syntax.parallel._ | |
import cats.syntax.traverse._ | ||
import cats.syntax.eq._ | ||
import org.scalacheck.Prop._ | ||
import cats.catsInstancesForId | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Id instances are no longer automatically in scope in dotty. |
||
|
||
trait ScalaVersionSpecificFoldableSuite { self: FoldableSuiteAdditional => | ||
test("Foldable[LazyList].foldM stack safety") { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package cats.tests | ||
|
||
import cats.data.NonEmptyList | ||
import cats.arrow.FunctionK | ||
import cats.implicits._ | ||
import org.scalacheck.Prop._ | ||
import cats.laws.discipline.arbitrary._ | ||
|
||
class FunctionKLiftSuite extends CatsSuite { | ||
|
||
test("lift simple unary") { | ||
def optionToList[A](option: Option[A]): List[A] = option.toList | ||
val fOptionToList = FunctionK.lift(optionToList _) | ||
forAll { (a: Option[Int]) => | ||
assert(fOptionToList(a) === (optionToList(a))) | ||
} | ||
|
||
val fO2I: FunctionK[Option, Iterable] = FunctionK.lift(Option.option2Iterable _) | ||
forAll { (a: Option[String]) => | ||
assert(fO2I(a).toList === (Option.option2Iterable(a).toList)) | ||
} | ||
|
||
val fNelFromListUnsafe = FunctionK.lift(NonEmptyList.fromListUnsafe _) | ||
forAll { (a: NonEmptyList[Int]) => | ||
assert(fNelFromListUnsafe(a.toList) === (NonEmptyList.fromListUnsafe(a.toList))) | ||
} | ||
} | ||
|
||
test("hygiene") { | ||
trait FunctionK | ||
def optionToList[A](option: Option[A]): List[A] = option.toList | ||
val fOptionToList = cats.arrow.FunctionK.lift(optionToList _) | ||
forAll { (a: Option[Int]) => | ||
assert(fOptionToList(a) === (optionToList(a))) | ||
} | ||
} | ||
|
||
test("lift compound unary") { | ||
val fNelFromList = FunctionK.lift[List, λ[α => Option[NonEmptyList[α]]]](NonEmptyList.fromList _) | ||
forAll { (a: List[String]) => | ||
assert(fNelFromList(a) === (NonEmptyList.fromList(a))) | ||
} | ||
} | ||
|
||
{ // lifting concrete types should fail to compile | ||
def sample[A](option: Option[A]): List[A] = option.toList | ||
assert(compileErrors("FunctionK.lift(sample[String])").nonEmpty) | ||
assert(compileErrors("FunctionK.lift(sample[Nothing])").nonEmpty) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,7 +61,7 @@ class ApplicativeErrorSuite extends CatsSuite { | |
assert(compileErrors("e2.attemptNarrow[Num]").nonEmpty) | ||
|
||
val e3: Either[List[T[String]], Unit] = List(Str).asLeft[Unit] | ||
assert(compileErrors("e3.attemptNarrow[List[Str.type]]").nonEmpty) | ||
//assertEquals(compileErrors("e3.attemptNarrow[List[Str.type]]"), "") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an odd one, because when I try it in the console it gives me an error but munit returns an empty string here |
||
} | ||
|
||
test("attemptT syntax creates an EitherT") { | ||
|
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scalafmt can't deal with dotty syntax yet.