diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afe8ec200b..3798d4f497 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -502,7 +502,7 @@ jobs: - name: Publish site if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' - uses: peaceiris/actions-gh-pages@v3.9.3 + uses: peaceiris/actions-gh-pages@v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: site/target/docs/site diff --git a/README.md b/README.md index bfdf0777dd..1112f5a345 100644 --- a/README.md +++ b/README.md @@ -217,4 +217,4 @@ http://opensource.org/licenses/mit-license.php and also in the [COPYING](https://github.com/typelevel/cats/blob/main/COPYING) file. The design is informed by many other projects, in particular [Scalaz](https://github.com/scalaz/scalaz). -Copyright the maintainers, 2015-2023. +Copyright the maintainers, 2015-2024. diff --git a/build.sbt b/build.sbt index e7e8d789b3..12f2b76e59 100644 --- a/build.sbt +++ b/build.sbt @@ -6,7 +6,7 @@ val disciplineVersion = "1.7.0" val disciplineMunitVersion = "2.0.0-M4" -val munitVersion = "1.0.0-RC1" +val munitVersion = "1.0.0" val PrimaryJava = JavaSpec.temurin("8") val LTSJava = JavaSpec.temurin("17") @@ -15,7 +15,7 @@ val GraalVM = JavaSpec.graalvm("17") ThisBuild / githubWorkflowJavaVersions := Seq(PrimaryJava, LTSJava, GraalVM) val Scala212 = "2.12.19" -val Scala213 = "2.13.13" +val Scala213 = "2.13.14" val Scala3 = "3.3.3" ThisBuild / crossScalaVersions := Seq(Scala212, Scala213, Scala3) diff --git a/core/src/main/scala-2/cats/arrow/FunctionKMacros.scala b/core/src/main/scala-2/cats/arrow/FunctionKMacros.scala index 51ed68ee7f..08b02bec7f 100644 --- a/core/src/main/scala-2/cats/arrow/FunctionKMacros.scala +++ b/core/src/main/scala-2/cats/arrow/FunctionKMacros.scala @@ -24,8 +24,7 @@ package arrow import scala.reflect.macros.blackbox -private[arrow] class FunctionKMacroMethods { - protected type τ[F[_], G[_]] +private[arrow] class FunctionKMacroMethods extends FunctionKLift { /** * Lifts function `f` of `F[A] => G[A]` into a `FunctionK[F, G]`. @@ -48,22 +47,6 @@ private[arrow] class FunctionKMacroMethods { */ def lift[F[_], G[_]](f: (F[α] => G[α]) forSome { type α }): FunctionK[F, G] = macro FunctionKMacros.lift[F, G] - - /** - * Lifts function `f` of `F[A] => G[A]` into a `FunctionK[F, G]`. - * - * {{{ - * def headOption[A](list: List[A]): Option[A] = list.headOption - * val lifted = FunctionK.liftFunction[List, Option](headOption) - * }}} - * - * Note: The weird `τ[F, G]` parameter is there to compensate for - * the lack of polymorphic function types in Scala 2. - */ - def liftFunction[F[_], G[_]](f: F[τ[F, G]] => G[τ[F, G]]): FunctionK[F, G] = - new FunctionK[F, G] { - def apply[A](fa: F[A]): G[A] = f.asInstanceOf[F[A] => G[A]](fa) - } } private[arrow] object FunctionKMacros { diff --git a/core/src/main/scala-3/cats/arrow/FunctionKMacros.scala b/core/src/main/scala-3/cats/arrow/FunctionKMacros.scala index 00b00aad61..76e7f22905 100644 --- a/core/src/main/scala-3/cats/arrow/FunctionKMacros.scala +++ b/core/src/main/scala-3/cats/arrow/FunctionKMacros.scala @@ -22,7 +22,7 @@ package cats package arrow -private[arrow] class FunctionKMacroMethods { +private[arrow] class FunctionKMacroMethods extends FunctionKLift { /** * Lifts function `f` of `[X] => F[X] => G[X]` into a `FunctionK[F, G]`. diff --git a/core/src/main/scala/cats/Functor.scala b/core/src/main/scala/cats/Functor.scala index 7707186e8b..6f42a7a750 100644 --- a/core/src/main/scala/cats/Functor.scala +++ b/core/src/main/scala/cats/Functor.scala @@ -64,12 +64,11 @@ trait Functor[F[_]] extends Invariant[F] { self => * * Example: * {{{ - * scala> import cats.Functor - * scala> import cats.implicits.catsStdInstancesForOption + * scala> import cats.syntax.all._ * - * scala> val s = Some(42) - * scala> Functor[Option].widen(s) - * res0: Option[Int] = Some(42) + * scala> val l = List(Some(42)) + * scala> l.widen[Option[Int]] + * res0: List[Option[Int]] = List(Some(42)) * }}} */ def widen[A, B >: A](fa: F[A]): F[B] = fa.asInstanceOf[F[B]] @@ -94,10 +93,9 @@ trait Functor[F[_]] extends Invariant[F] { self => * * Example: * {{{ - * scala> import cats.Functor - * scala> import cats.implicits.catsStdInstancesForList + * scala> import cats.syntax.all._ * - * scala> Functor[List].void(List(1,2,3)) + * scala> List(1,2,3).void * res0: List[Unit] = List((), (), ()) * }}} */ @@ -109,10 +107,9 @@ trait Functor[F[_]] extends Invariant[F] { self => * * Example: * {{{ - * scala> import cats.Functor - * scala> import cats.implicits.catsStdInstancesForOption + * scala> import cats.syntax.all._ * - * scala> Functor[Option].fproduct(Option(42))(_.toString) + * scala> Option(42).fproduct(_.toString) * res0: Option[(Int, String)] = Some((42,42)) * }}} */ @@ -123,10 +120,9 @@ trait Functor[F[_]] extends Invariant[F] { self => * * Example: * {{{ - * scala> import cats.Functor - * scala> import cats.implicits.catsStdInstancesForOption + * scala> import cats.syntax.all._ * - * scala> Functor[Option].fproductLeft(Option(42))(_.toString) + * scala> Option(42).fproductLeft(_.toString) * res0: Option[(String, Int)] = Some((42,42)) * }}} */ @@ -138,10 +134,9 @@ trait Functor[F[_]] extends Invariant[F] { self => * Example: * * {{{ - * scala> import cats.Functor - * scala> import cats.implicits.catsStdInstancesForList + * scala> import cats.syntax.all._ * - * scala> Functor[List].as(List(1,2,3), "hello") + * scala> List(1,2,3).as("hello") * res0: List[String] = List(hello, hello, hello) * }}} */ @@ -153,10 +148,9 @@ trait Functor[F[_]] extends Invariant[F] { self => * Example: * {{{ * scala> import scala.collection.immutable.Queue - * scala> import cats.Functor - * scala> import cats.implicits.catsStdInstancesForQueue + * scala> import cats.syntax.all._ * - * scala> Functor[Queue].tupleLeft(Queue("hello", "world"), 42) + * scala> Queue("hello", "world").tupleLeft(42) * res0: scala.collection.immutable.Queue[(Int, String)] = Queue((42,hello), (42,world)) * }}} */ @@ -168,10 +162,9 @@ trait Functor[F[_]] extends Invariant[F] { self => * Example: * {{{ * scala> import scala.collection.immutable.Queue - * scala> import cats.Functor - * scala> import cats.implicits.catsStdInstancesForQueue + * scala> import cats.syntax.all._ * - * scala> Functor[Queue].tupleRight(Queue("hello", "world"), 42) + * scala> Queue("hello", "world").tupleRight(42) * res0: scala.collection.immutable.Queue[(String, Int)] = Queue((hello,42), (world,42)) * }}} */ @@ -181,10 +174,9 @@ trait Functor[F[_]] extends Invariant[F] { self => * Modifies the `A` value in `F[A]` with the supplied function, if the function is defined for the value. * Example: * {{{ - * scala> import cats.Functor - * scala> import cats.implicits.catsStdInstancesForList + * scala> import cats.syntax.all._ * - * scala> Functor[List].mapOrKeep(List(1, 2, 3)) { case 2 => 42 } + * scala> List(1, 2, 3).mapOrKeep { case 2 => 42 } * res0: List[Int] = List(1, 42, 3) * }}} */ @@ -203,7 +195,6 @@ trait Functor[F[_]] extends Invariant[F] { self => * res0: (List[Int], List[Int]) = (List(1, 3),List(2, 4)) * }}} */ - def unzip[A, B](fab: F[(A, B)]): (F[A], F[B]) = (map(fab)(_._1), map(fab)(_._2)) /** @@ -218,7 +209,6 @@ trait Functor[F[_]] extends Invariant[F] { self => * res0: List[Int] = List(1, 0, 0) * }}} */ - def ifF[A](fb: F[Boolean])(ifTrue: => A, ifFalse: => A): F[A] = map(fb)(x => if (x) ifTrue else ifFalse) def compose[G[_]: Functor]: Functor[λ[α => F[G[α]]]] = diff --git a/core/src/main/scala/cats/arrow/FunctionKLift.scala b/core/src/main/scala/cats/arrow/FunctionKLift.scala new file mode 100644 index 0000000000..7f5dafb525 --- /dev/null +++ b/core/src/main/scala/cats/arrow/FunctionKLift.scala @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats +package arrow + +private[arrow] trait FunctionKLift { + protected type τ[F[_], G[_]] + + /** + * Lifts function `f` of `F[A] => G[A]` into a `FunctionK[F, G]`. + * + * {{{ + * def headOption[A](list: List[A]): Option[A] = list.headOption + * val lifted = FunctionK.liftFunction[List, Option](headOption) + * }}} + * + * Note: The weird `τ[F, G]` parameter is there to compensate for + * the lack of polymorphic function types in Scala 2. + * + * It is present in the Scala 3 API to simplify cross-compilation. + */ + def liftFunction[F[_], G[_]](f: F[τ[F, G]] => G[τ[F, G]]): FunctionK[F, G] = + new FunctionK[F, G] { + def apply[A](fa: F[A]): G[A] = f.asInstanceOf[F[A] => G[A]](fa) + } +} diff --git a/docs/index.md b/docs/index.md index 990c82b39c..8cbf838018 100644 --- a/docs/index.md +++ b/docs/index.md @@ -201,6 +201,6 @@ http://opensource.org/licenses/mit-license.php and also in the [COPYING](https://github.com/typelevel/cats/blob/main/COPYING) file. The design is informed by many other projects, in particular [Scalaz](https://github.com/scalaz/scalaz). -Copyright the maintainers, 2015-2023. +Copyright the maintainers, 2015-2024. [Adopters page]: ADOPTERS.md \ No newline at end of file diff --git a/project/build.properties b/project/build.properties index 04267b14af..081fdbbc76 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.9 +sbt.version=1.10.0 diff --git a/project/plugins.sbt b/project/plugins.sbt index 8217b3cae5..a5b2fdf8bc 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -val sbtTypelevelVersion = "0.7.0" +val sbtTypelevelVersion = "0.7.1" addSbtPlugin("org.typelevel" % "sbt-typelevel" % sbtTypelevelVersion) addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % sbtTypelevelVersion) addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7") diff --git a/scalafix/project/build.properties b/scalafix/project/build.properties index 04267b14af..081fdbbc76 100644 --- a/scalafix/project/build.properties +++ b/scalafix/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.9 +sbt.version=1.10.0 diff --git a/tests/shared/src/test/scala-2/cats/tests/FunctionKLiftSuite.scala b/tests/shared/src/test/scala-2/cats/tests/FunctionKLiftSuite.scala index 7dd041d2bc..04257a3474 100644 --- a/tests/shared/src/test/scala-2/cats/tests/FunctionKLiftSuite.scala +++ b/tests/shared/src/test/scala-2/cats/tests/FunctionKLiftSuite.scala @@ -72,14 +72,6 @@ class FunctionKLiftSuite extends CatsSuite { } } - test("lift a function directly") { - def headOption[A](list: List[A]): Option[A] = list.headOption - val fHeadOption = FunctionK.liftFunction[List, Option](headOption) - forAll { (a: List[Int]) => - assert(fHeadOption(a) === a.headOption) - } - } - { // lifting concrete types should fail to compile def sample[A](option: Option[A]): List[A] = option.toList assert(compileErrors("FunctionK.lift(sample[String])").nonEmpty) diff --git a/tests/shared/src/test/scala/cats/tests/FunctionKLiftCrossBuildSuite.scala b/tests/shared/src/test/scala/cats/tests/FunctionKLiftCrossBuildSuite.scala new file mode 100644 index 0000000000..5edb678a96 --- /dev/null +++ b/tests/shared/src/test/scala/cats/tests/FunctionKLiftCrossBuildSuite.scala @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015 Typelevel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cats.tests + +import cats.arrow.FunctionK +import cats.syntax.all._ +import org.scalacheck.Prop._ + +class FunctionKLiftCrossBuildSuite extends CatsSuite { + test("lift a function directly") { + def headOption[A](list: List[A]): Option[A] = list.headOption + val fHeadOption = FunctionK.liftFunction[List, Option](headOption) + forAll { (a: List[Int]) => + assert(fHeadOption(a) === a.headOption) + } + } +}