From baec5e07079049cdc83a64ce1fa640b92752d11c Mon Sep 17 00:00:00 2001 From: Grigory Pomadchin Date: Sat, 11 May 2024 18:43:57 -0400 Subject: [PATCH] Expose FunctionK.liftFunction as a part of the Scala 3 API --- .../scala-2/cats/arrow/FunctionKMacros.scala | 19 +------- .../scala-3/cats/arrow/FunctionKMacros.scala | 2 +- .../main/scala/cats/arrow/FunctionKLift.scala | 45 +++++++++++++++++++ .../cats/tests/FunctionKLiftSuite.scala | 8 ++++ 4 files changed, 55 insertions(+), 19 deletions(-) create mode 100644 core/src/main/scala/cats/arrow/FunctionKLift.scala 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/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/tests/shared/src/test/scala-3/cats/tests/FunctionKLiftSuite.scala b/tests/shared/src/test/scala-3/cats/tests/FunctionKLiftSuite.scala index da70f8fc87..9345706234 100644 --- a/tests/shared/src/test/scala-3/cats/tests/FunctionKLiftSuite.scala +++ b/tests/shared/src/test/scala-3/cats/tests/FunctionKLiftSuite.scala @@ -34,4 +34,12 @@ class FunctionKLiftSuite extends CatsSuite { assert(fHeadOption(a) === a.headOption) } } + + test("lift a function directly using Scala 2 compatible syntax") { + 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) + } + } }