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

Deprecate auto in favor of semiauto #212

Merged
merged 2 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ cat: Cat[Int] = Cat(1,List(2, 3))
```scala
scala> implicit val fc: Functor[Cat] = {
import auto.functor._
semi.functor }
semiauto.functor }
FC: cats.Functor[Cat] = cats.derived.MkFunctor2$$anon$4@1c60573f

scala> cat.map(_ + 1)
Expand All @@ -82,7 +82,7 @@ scala> implicit val addressShow: Show[Address] = new Show[Address] {

scala> implicit val peopleShow: Show[People] = {
import auto.show._
semi.show
semiauto.show
} //auto derive Show for People

scala> mike.show
Expand Down Expand Up @@ -163,7 +163,7 @@ import cats.derived

implicit val showFoo: Show[Foo] = {
import derived.auto.show._
derived.semi.show
derived.semiauto.show
}
```
This will respect all existing instances even if the field is a type constructor. For example `Show[List[A]]` will use the native `Show` instance for `List` and derived instance for `A`. And it manually caches the result to the `val showFoo`. Downside user will need to write one for every type they directly need a `Show` instance
Expand All @@ -188,7 +188,7 @@ Use this one with caution. It caches the derived instance globally. So it's only

3. manual semi
```scala
implicit val showFoo: Show[Foo] = derived.semi.show
implicit val showFoo: Show[Foo] = derived.semiauto.show
```
It has the same downside as the recommenced semi-auto practice but also suffers from the type constructor field issue. I.e. if a field type is a type constructor whose native instance relies on the instance of the parameter type, this approach will by default derive an instance for the type constructor one. To overcome this user have to first derive the instance for type parameter.
e.g. given
Expand All @@ -198,8 +198,8 @@ case class Bar(a: String)
```
Since the `bars` field of `Foo` is a `List` of `Bar` which breaks the chains of auto derivation, you will need to derive `Bar` first and then `Foo`
```scala
implicit val showBar: Show[Bar] = semi.show
implicit val showFoo: Show[Foo] = semi.show
implicit val showBar: Show[Bar] = semiauto.show
implicit val showFoo: Show[Foo] = semiauto.show
```
This way the native instance for `Show[List]` would be used.

Expand Down
34 changes: 34 additions & 0 deletions core/src/main/scala-2.11/cats/derived/semiauto.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cats.derived

/**
* allows semi automatically derive each instance. The derivation might need help when
* there are fields with type constructor that comes with instances
* e.g.
* {{{
* scala> case class Foo(bars: List[Bar])
* scala> case class Bar(a: String)
*
* scala> cats.derived.semiauto.show[Foo].show(Foo(List(Bar("a"))))
* res1: String = Foo(bars = $colon$colon(head = Bar(a = a), tl$access$1 = Nil.type()))
* }}}
* Note that semi.show didn't respect the native `Show[List]` instance
*
* You could either derive a Bar instance first
* {{{
* scala> implicit val barShow = cats.derived.semi.show[Bar]
*
* scala> cats.derived.semiauto.show[Foo].show(Foo(List(Bar("a"))))
* res2: String = Foo(bars = List(Bar(a = a)))
* }}}
*
* Or you can take advantage of a controlled auto derivation
* {{{
* scala> implicit val fooShow: Show[Foo] = { |
* import cats.derived.auto.show._ |
* cats.derived.semiauto.show |
* }
* scala> Foo(List(Bar("a"))).show
* res3: String = Foo(bars = List(Bar(a = a)))
* }}}
*/
object semiauto extends SemiAutoInstances
34 changes: 34 additions & 0 deletions core/src/main/scala-2.12/cats/derived/semiauto.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cats.derived

/**
* allows semi automatically derive each instance. The derivation might need help when
* there are fields with type constructor that comes with instances
* e.g.
* {{{
* scala> case class Foo(bars: List[Bar])
* scala> case class Bar(a: String)
*
* scala> cats.derived.semiauto.show[Foo].show(Foo(List(Bar("a"))))
* res1: String = Foo(bars = $colon$colon(head = Bar(a = a), tl$access$1 = Nil.type()))
* }}}
* Note that semi.show didn't respect the native `Show[List]` instance
*
* You could either derive a Bar instance first
* {{{
* scala> implicit val barShow = cats.derived.semi.show[Bar]
*
* scala> cats.derived.semiauto.show[Foo].show(Foo(List(Bar("a"))))
* res2: String = Foo(bars = List(Bar(a = a)))
* }}}
*
* Or you can take advantage of a controlled auto derivation
* {{{
* scala> implicit val fooShow: Show[Foo] = { |
* import cats.derived.auto.show._ |
* cats.derived.semiauto.show |
* }
* scala> Foo(List(Bar("a"))).show
* res3: String = Foo(bars = List(Bar(a = a)))
* }}}
*/
object semiauto extends SemiAutoInstances
73 changes: 73 additions & 0 deletions core/src/main/scala-2.13/cats/derived/semiauto.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package cats
package derived

import alleycats.{ConsK, Empty, EmptyK, Pure}
import cats.kernel.{CommutativeMonoid, CommutativeSemigroup}

/**
* allows semi automatically derive each instance. The derivation might need help when
* there are fields with type constructor that comes with instances
* e.g.
* {{{
* scala> case class Foo(bars: List[Bar])
* scala> case class Bar(a: String)
*
* scala> cats.derived.semiauto.show[Foo].show(Foo(List(Bar("a"))))
* res1: String = Foo(bars = $colon$colon(head = Bar(a = a), tl$access$1 = Nil.type()))
* }}}
* Note that semi.show didn't respect the native `Show[List]` instance
*
* You could either derive a Bar instance first
* {{{
* scala> implicit val barShow = cats.derived.semi.show[Bar]
*
* scala> cats.derived.semiauto.show[Foo].show(Foo(List(Bar("a"))))
* res2: String = Foo(bars = List(Bar(a = a)))
* }}}
*
* Or you can take advantage of a controlled auto derivation
* {{{
* scala> implicit val fooShow: Show[Foo] = { |
* import cats.derived.auto.show._ |
* cats.derived.semiauto.show |
* }
* scala> Foo(List(Bar("a"))).show
* res3: String = Foo(bars = List(Bar(a = a)))
* }}}
*/
object semiauto {

def eq[A](implicit ev: MkEq[A]): Eq[A] = ev
def partialOrder[A](implicit ev: MkPartialOrder[A]): PartialOrder[A] = ev
def order[A](implicit ev: MkOrder[A]): Order[A] = ev
def hash[A](implicit ev: MkHash[A]): Hash[A] = ev

def show[A](implicit ev: MkShow[A]): Show[A] = ev
def showPretty[A](implicit ev: MkShowPretty[A]): ShowPretty[A] = ev

def empty[A](implicit ev: MkEmpty[A]): Empty[A] = ev
def emptyK[F[_]](implicit F: MkEmptyK[F]): EmptyK[F] = F

def semigroup[T](implicit ev: MkSemigroup[T]): Semigroup[T] = ev
def semigroupK[F[_]](implicit F: MkSemigroupK[F]): SemigroupK[F] = F
def commutativeSemigroup[T](implicit ev: MkCommutativeSemigroup[T]): CommutativeSemigroup[T] = ev

def monoid[A](implicit ev: MkMonoid[A]): Monoid[A] = ev
def monoidK[F[_]](implicit F: MkMonoidK[F]): MonoidK[F] = F
def commutativeMonoid[A](implicit ev: MkCommutativeMonoid[A]): CommutativeMonoid[A] = ev

def functor[F[_]](implicit F: MkFunctor[F]): Functor[F] = F
def contravariant[F[_]](implicit F: MkContravariant[F]): Contravariant[F] = F
def invariant[F[_]](implicit F: MkInvariant[F]): Invariant[F] = F
def pure[F[_]](implicit F: MkPure[F]): Pure[F] = F
def apply[F[_]](implicit F: MkApply[F]): Apply[F] = F
def applicative[F[_]](implicit F: MkApplicative[F]): Applicative[F] = F

def foldable[F[_]](implicit F: MkFoldable[F]): Foldable[F] = F
def reducible[F[_]](implicit F: MkReducible[F]): Reducible[F] = F
def traverse[F[_]](implicit F: MkTraverse[F]): Traverse[F] = F
def nonEmptyTraverse[F[_]](implicit F: MkNonEmptyTraverse[F]): NonEmptyTraverse[F] = F

def consK[F[_]](implicit F: MkConsK[F, F]): ConsK[F] = MkConsK.consK(F)
def iterable[F[_], A](fa: F[A])(implicit F: MkIterable[F]): Iterable[A] = F.iterable(fa)
}
98 changes: 41 additions & 57 deletions core/src/main/scala/cats/derived/package.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package cats
package derived

import cats.kernel.{CommutativeMonoid, CommutativeSemigroup}
import alleycats._
import cats.derived.util.VersionSpecific.Lazy
import cats.kernel.{CommutativeMonoid, CommutativeSemigroup}
import shapeless.{Cached, Refute}
import util.VersionSpecific.Lazy

/**
* Fully automatically derive the instance, note that this derivation is not cached, so it
Expand Down Expand Up @@ -327,6 +327,43 @@ object cached {
}
}

private[derived] abstract class SemiAutoInstances {

def eq[A](implicit ev: Lazy[MkEq[A]]): Eq[A] = ev.value
def partialOrder[A](implicit ev: Lazy[MkPartialOrder[A]]): PartialOrder[A] = ev.value
def order[A](implicit ev: Lazy[MkOrder[A]]): Order[A] = ev.value
def hash[A](implicit ev: Lazy[MkHash[A]]): Hash[A] = ev.value

def show[A](implicit ev: Lazy[MkShow[A]]): Show[A] = ev.value
def showPretty[A](implicit ev: Lazy[MkShowPretty[A]]): ShowPretty[A] = ev.value

def empty[A](implicit ev: Lazy[MkEmpty[A]]): Empty[A] = ev.value
def emptyK[F[_]](implicit F: Lazy[MkEmptyK[F]]): EmptyK[F] = F.value

def semigroup[T](implicit ev: Lazy[MkSemigroup[T]]): Semigroup[T] = ev.value
def semigroupK[F[_]](implicit F: Lazy[MkSemigroupK[F]]): SemigroupK[F] = F.value
def commutativeSemigroup[T](implicit ev: Lazy[MkCommutativeSemigroup[T]]): CommutativeSemigroup[T] = ev.value

def monoid[A](implicit ev: Lazy[MkMonoid[A]]): Monoid[A] = ev.value
def monoidK[F[_]](implicit F: Lazy[MkMonoidK[F]]): MonoidK[F] = F.value
def commutativeMonoid[A](implicit ev: Lazy[MkCommutativeMonoid[A]]): CommutativeMonoid[A] = ev.value

def functor[F[_]](implicit F: Lazy[MkFunctor[F]]): Functor[F] = F.value
def contravariant[F[_]](implicit F: Lazy[MkContravariant[F]]): Contravariant[F] = F.value
def invariant[F[_]](implicit F: Lazy[MkInvariant[F]]): Invariant[F] = F.value
def pure[F[_]](implicit F: Lazy[MkPure[F]]): Pure[F] = F.value
def apply[F[_]](implicit F: Lazy[MkApply[F]]): Apply[F] = F.value
def applicative[F[_]](implicit F: Lazy[MkApplicative[F]]): Applicative[F] = F.value

def foldable[F[_]](implicit F: Lazy[MkFoldable[F]]): Foldable[F] = F.value
def reducible[F[_]](implicit F: Lazy[MkReducible[F]]): Reducible[F] = F.value
def traverse[F[_]](implicit F: Lazy[MkTraverse[F]]): Traverse[F] = F.value
def nonEmptyTraverse[F[_]](implicit F: Lazy[MkNonEmptyTraverse[F]]): NonEmptyTraverse[F] = F.value

def consK[F[_]](implicit F: Lazy[MkConsK[F, F]]): ConsK[F] = MkConsK.consK(F.value)
def iterable[F[_], A](fa: F[A])(implicit F: MkIterable[F]): Iterable[A] = F.iterable(fa)
}

/**
* allows semi automatically derive each instance. The derivation might need help when
* there are fields with type constructor that comes with instances
Expand Down Expand Up @@ -358,58 +395,5 @@ object cached {
* res3: String = Foo(bars = List(Bar(a = a)))
* }}}
*/
object semi {

def empty[A](implicit ev: Lazy[MkEmpty[A]]): Empty[A] = ev.value

def emptyK[F[_]](implicit F: Lazy[MkEmptyK[F]]): EmptyK[F] = F.value

def eq[A](implicit ev: Lazy[MkEq[A]]): Eq[A] = ev.value

def partialOrder[A](implicit ev: Lazy[MkPartialOrder[A]]): PartialOrder[A] = ev.value

def order[A](implicit ev: Lazy[MkOrder[A]]): Order[A] = ev.value

def hash[A](implicit ev: Lazy[MkHash[A]]): Hash[A] = ev.value

def contravariant[F[_]](implicit F: Lazy[MkContravariant[F]]): Contravariant[F] = F.value

def functor[F[_]](implicit F: Lazy[MkFunctor[F]]): Functor[F] = F.value

def apply[F[_]](implicit F: Lazy[MkApply[F]]): Apply[F] = F.value

def applicative[F[_]](implicit F: Lazy[MkApplicative[F]]): Applicative[F] = F.value

def show[A](implicit ev: Lazy[MkShow[A]]): Show[A] = ev.value

def showPretty[A](implicit ev: Lazy[MkShowPretty[A]]): ShowPretty[A] = ev.value

def foldable[F[_]](implicit F: Lazy[MkFoldable[F]]): Foldable[F] = F.value

def reducible[F[_]](implicit F: Lazy[MkReducible[F]]): Reducible[F] = F.value

def traverse[F[_]](implicit F: Lazy[MkTraverse[F]]): Traverse[F] = F.value

def nonEmptyTraverse[F[_]](implicit F: Lazy[MkNonEmptyTraverse[F]]): NonEmptyTraverse[F] = F.value

def monoid[A](implicit ev: Lazy[MkMonoid[A]]): Monoid[A] = ev.value

def commutativeMonoid[A](implicit ev: Lazy[MkCommutativeMonoid[A]]): CommutativeMonoid[A] = ev.value

def monoidK[F[_]](implicit F: Lazy[MkMonoidK[F]]): MonoidK[F] = F.value

def pure[F[_]](implicit F: Lazy[MkPure[F]]): Pure[F] = F.value

def semigroup[T](implicit ev: Lazy[MkSemigroup[T]]): Semigroup[T] = ev.value

def commutativeSemigroup[T](implicit ev: Lazy[MkCommutativeSemigroup[T]]): CommutativeSemigroup[T] = ev.value

def semigroupK[F[_]](implicit F: Lazy[MkSemigroupK[F]]): SemigroupK[F] = F.value

def consK[F[_]](implicit F: Lazy[MkConsK[F, F]]): ConsK[F] = MkConsK.consK(F.value)

def iterable[F[_], A](fa: F[A])(implicit F: MkIterable[F]): Iterable[A] = F.iterable(fa)

def invariant[F[_]](implicit F: Lazy[MkInvariant[F]]): Invariant[F] = F.value
}

@deprecated(message = "Use semiauto instead.", since = "2.1.0")
object semi extends SemiAutoInstances
28 changes: 18 additions & 10 deletions core/src/test/scala/cats/derived/applicative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,10 @@ import cats.laws.discipline.{ApplicativeTests, SerializableTests}
import cats.laws.discipline.SemigroupalTests.Isomorphisms

class ApplicativeSuite extends KittensSuite {
import ApplicativeSuite._
import TestDefns._
import TestEqInstances._

type OptList[A] = Option[List[A]]
type AndInt[A] = (A, Int)
type ListBox[A] = List[Box[A]]

def testApplicative(context: String)(
implicit caseClassWOption: Applicative[CaseClassWOption],
optList: Applicative[OptList],
Expand Down Expand Up @@ -58,12 +55,23 @@ class ApplicativeSuite extends KittensSuite {
}

{
implicit val caseClassWOption: Applicative[CaseClassWOption] = semi.applicative
implicit val optList: Applicative[OptList] = semi.applicative
implicit val andInt: Applicative[AndInt] = semi.applicative
implicit val interleaved: Applicative[Interleaved] = semi.applicative
implicit val listBox: Applicative[ListBox] = semi.applicative
testApplicative("semi")
import semiInstances._
testApplicative("semiauto")
}
}

object ApplicativeSuite {
import TestDefns._

type OptList[A] = Option[List[A]]
type AndInt[A] = (A, Int)
type ListBox[A] = List[Box[A]]

object semiInstances {
implicit val caseClassWOption: Applicative[CaseClassWOption] = semiauto.applicative
implicit val optList: Applicative[OptList] = semiauto.applicative
implicit val andInt: Applicative[AndInt] = semiauto.applicative
implicit val interleaved: Applicative[Interleaved] = semiauto.applicative
implicit val listBox: Applicative[ListBox] = semiauto.applicative
}
}
28 changes: 18 additions & 10 deletions core/src/test/scala/cats/derived/apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,10 @@ import cats.laws.discipline.{ApplyTests, SerializableTests}
import cats.laws.discipline.SemigroupalTests.Isomorphisms

class ApplySuite extends KittensSuite {
import ApplySuite._
import TestDefns._
import TestEqInstances._

type OptList[A] = Option[List[A]]
type AndInt[A] = (A, Int)
type ListBox[A] = List[Box[A]]

def testApply(context: String)(
implicit caseClassWOption: Apply[CaseClassWOption],
optList: Apply[OptList],
Expand Down Expand Up @@ -58,12 +55,23 @@ class ApplySuite extends KittensSuite {
}

{
implicit val caseClassWOption: Apply[CaseClassWOption] = semi.apply
implicit val optList: Apply[OptList] = semi.apply
implicit val andInt: Apply[AndInt] = semi.apply
implicit val interleaved: Apply[Interleaved] = semi.apply
implicit val listBox: Apply[ListBox] = semi.apply
testApply("semi")
import semiInstances._
testApply("semiauto")
}
}

object ApplySuite {
import TestDefns._

type OptList[A] = Option[List[A]]
type AndInt[A] = (A, Int)
type ListBox[A] = List[Box[A]]

object semiInstances {
implicit val caseClassWOption: Apply[CaseClassWOption] = semiauto.apply
implicit val optList: Apply[OptList] = semiauto.apply
implicit val andInt: Apply[AndInt] = semiauto.apply
implicit val interleaved: Apply[Interleaved] = semiauto.apply
implicit val listBox: Apply[ListBox] = semiauto.apply
}
}
Loading