From 80527cfb7a05c0eba32f59c315bbdde006b44d7c Mon Sep 17 00:00:00 2001 From: aluscent Date: Tue, 29 Oct 2024 14:35:21 +0330 Subject: [PATCH 1/6] Fixed issue #4642 --- .../src/main/scala/alleycats/syntax/all.scala | 2 +- .../src/main/scala/alleycats/syntax/extract.scala | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 alleycats-core/src/main/scala/alleycats/syntax/extract.scala diff --git a/alleycats-core/src/main/scala/alleycats/syntax/all.scala b/alleycats-core/src/main/scala/alleycats/syntax/all.scala index 79ef6c6a48..be4a67772e 100644 --- a/alleycats-core/src/main/scala/alleycats/syntax/all.scala +++ b/alleycats-core/src/main/scala/alleycats/syntax/all.scala @@ -21,4 +21,4 @@ package alleycats.syntax -object all extends EmptySyntax with FoldableSyntax +object all extends EmptySyntax with FoldableSyntax with ExtractSyntax diff --git a/alleycats-core/src/main/scala/alleycats/syntax/extract.scala b/alleycats-core/src/main/scala/alleycats/syntax/extract.scala new file mode 100644 index 0000000000..6d1a013a34 --- /dev/null +++ b/alleycats-core/src/main/scala/alleycats/syntax/extract.scala @@ -0,0 +1,12 @@ +package alleycats +package syntax + +import alleycats.Extract + +object extract extends ExtractSyntax + +trait ExtractSyntax { + implicit class ExtractOps[F[_], A](fa: F[A])(implicit ev: Extract[F]) { + def extract(): A = ev.extract(fa) + } +} \ No newline at end of file From 992f72d751a092df9742ca838a9330ea65382220 Mon Sep 17 00:00:00 2001 From: aluscent Date: Tue, 29 Oct 2024 15:09:05 +0330 Subject: [PATCH 2/6] Added header to file --- .../main/scala/alleycats/syntax/extract.scala | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/alleycats-core/src/main/scala/alleycats/syntax/extract.scala b/alleycats-core/src/main/scala/alleycats/syntax/extract.scala index 6d1a013a34..bd5d1823f7 100644 --- a/alleycats-core/src/main/scala/alleycats/syntax/extract.scala +++ b/alleycats-core/src/main/scala/alleycats/syntax/extract.scala @@ -1,3 +1,24 @@ +/* + * 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 alleycats package syntax From 2c1d7971aa5aa9fbfcffcc46c2015235df1bceb9 Mon Sep 17 00:00:00 2001 From: aluscent Date: Tue, 29 Oct 2024 15:16:12 +0330 Subject: [PATCH 3/6] Formatted using scalafmt --- alleycats-core/src/main/scala/alleycats/syntax/extract.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alleycats-core/src/main/scala/alleycats/syntax/extract.scala b/alleycats-core/src/main/scala/alleycats/syntax/extract.scala index bd5d1823f7..7a4fb8afa0 100644 --- a/alleycats-core/src/main/scala/alleycats/syntax/extract.scala +++ b/alleycats-core/src/main/scala/alleycats/syntax/extract.scala @@ -30,4 +30,4 @@ trait ExtractSyntax { implicit class ExtractOps[F[_], A](fa: F[A])(implicit ev: Extract[F]) { def extract(): A = ev.extract(fa) } -} \ No newline at end of file +} From b9b7e201ac10d3b7a164c57ee0bb511ea45b3ec9 Mon Sep 17 00:00:00 2001 From: Ali Tariverdy <35956849+aluscent@users.noreply.github.com> Date: Sat, 2 Nov 2024 12:52:13 +0330 Subject: [PATCH 4/6] Changing ExtractOps class to value class The reason why I think it could make sense is that: 1. This is a pretty valid use case for value classes in general and allows to save on memory allocations a little bit. 2. Newer syntaxes in Cats-core use this approach a lot, see Applicative syntax for example: Co-authored-by: Sergey Torgashov --- alleycats-core/src/main/scala/alleycats/syntax/extract.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alleycats-core/src/main/scala/alleycats/syntax/extract.scala b/alleycats-core/src/main/scala/alleycats/syntax/extract.scala index 7a4fb8afa0..3ba0697975 100644 --- a/alleycats-core/src/main/scala/alleycats/syntax/extract.scala +++ b/alleycats-core/src/main/scala/alleycats/syntax/extract.scala @@ -27,7 +27,7 @@ import alleycats.Extract object extract extends ExtractSyntax trait ExtractSyntax { - implicit class ExtractOps[F[_], A](fa: F[A])(implicit ev: Extract[F]) { - def extract(): A = ev.extract(fa) + implicit final class ExtractOps[F[_], A](private val fa: F[A]) extends AnyVal { + def extract(implicit ev: Extract[F]): A = ev.extract(fa) } } From 4bb630a4fa413dd3f3ef44189fda3c3b73d8f689 Mon Sep 17 00:00:00 2001 From: aluscent Date: Tue, 3 Dec 2024 17:17:07 +0330 Subject: [PATCH 5/6] Made ExtractOps class a value class Wrote tests for alleycats module --- .../main/scala/alleycats/syntax/extract.scala | 8 ++- .../test/scala/alleycats/SyntaxSuite.scala | 67 +++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 alleycats-core/src/test/scala/alleycats/SyntaxSuite.scala diff --git a/alleycats-core/src/main/scala/alleycats/syntax/extract.scala b/alleycats-core/src/main/scala/alleycats/syntax/extract.scala index 3ba0697975..78d5272599 100644 --- a/alleycats-core/src/main/scala/alleycats/syntax/extract.scala +++ b/alleycats-core/src/main/scala/alleycats/syntax/extract.scala @@ -27,7 +27,9 @@ import alleycats.Extract object extract extends ExtractSyntax trait ExtractSyntax { - implicit final class ExtractOps[F[_], A](private val fa: F[A]) extends AnyVal { - def extract(implicit ev: Extract[F]): A = ev.extract(fa) - } + implicit final def catsSyntaxExtract[F[_], A](fa: F[A]): ExtractOps[F, A] = new ExtractOps[F, A](fa) +} + +final private[alleycats] class ExtractOps[F[_], A](private val fa: F[A]) extends AnyVal { + def extract(implicit F: Extract[F]): A = F.extract(fa) } diff --git a/alleycats-core/src/test/scala/alleycats/SyntaxSuite.scala b/alleycats-core/src/test/scala/alleycats/SyntaxSuite.scala new file mode 100644 index 0000000000..40e1ba713d --- /dev/null +++ b/alleycats-core/src/test/scala/alleycats/SyntaxSuite.scala @@ -0,0 +1,67 @@ +/* + * 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 alleycats + +import cats.{Eq, Foldable} +import alleycats.syntax.all.{EmptyOps, ExtraFoldableOps, catsSyntaxExtract} + +/** + * Test that our syntax implicits are working. + * + * Each method should correspond to one type class worth of syntax. + * Ideally, we should be testing every operator or method that we + * expect to add to generic parameters. This file is a safeguard + * against accidentally breaking (or removing) syntax which was + * otherwise untested. + * + * The strategy here is to create "mock" values of particular types, + * and then ensure that the syntax we want is available. We never plan + * to run any of these methods, so we don't need real values. All + * values in the methods should be generic -- we rely on parametricity + * to guarantee that the syntax will be available for any type with + * the proper type class instance(s). + * + * None of these tests should ever run, or do any runtime checks. + */ +object SyntaxSuite { + + // pretend we have a value of type A + def mock[A]: A = ??? + + def testEmpty[A: Empty]: Unit = { + val x = mock[A] + implicit val y: Eq[A] = mock[Eq[A]] + val a0: Boolean = x.isEmpty + val a1: Boolean = x.nonEmpty + } + + def testFoldable[F[_]: Foldable, A]: Unit = { + val x = mock[F[A]] + val y = mock[A => Unit] + x.foreach(y) + } + + def testExtract[F[_]: Extract, A]: Unit = { + val x = mock[F[A]] + val y = x.extract + } +} From 843182c127bff0b892c5d74a7a95df9a121f71b2 Mon Sep 17 00:00:00 2001 From: aluscent Date: Tue, 3 Dec 2024 17:31:05 +0330 Subject: [PATCH 6/6] Applied scalafmt on file --- alleycats-core/src/test/scala/alleycats/SyntaxSuite.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alleycats-core/src/test/scala/alleycats/SyntaxSuite.scala b/alleycats-core/src/test/scala/alleycats/SyntaxSuite.scala index 40e1ba713d..71a8867f14 100644 --- a/alleycats-core/src/test/scala/alleycats/SyntaxSuite.scala +++ b/alleycats-core/src/test/scala/alleycats/SyntaxSuite.scala @@ -22,7 +22,7 @@ package alleycats import cats.{Eq, Foldable} -import alleycats.syntax.all.{EmptyOps, ExtraFoldableOps, catsSyntaxExtract} +import alleycats.syntax.all.{catsSyntaxExtract, EmptyOps, ExtraFoldableOps} /** * Test that our syntax implicits are working.