Skip to content

Commit

Permalink
Welcome, Alleycats (#1984)
Browse files Browse the repository at this point in the history
* moved alleycats in

* address feedback / removed duplicated class
  • Loading branch information
kailuowang authored Oct 26, 2017
1 parent 9277979 commit ef76136
Show file tree
Hide file tree
Showing 21 changed files with 615 additions and 4 deletions.
20 changes: 20 additions & 0 deletions alleycats-core/src/main/scala/alleycats/ConsK.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package alleycats

import cats.SemigroupK
import export.imports
import simulacrum.typeclass

@typeclass trait ConsK[F[_]] {
def cons[A](hd: A, tl: F[A]): F[A]
}

object ConsK extends ConsK0 {
implicit def pureSemigroupKIsConsK[F[_]](implicit p: Pure[F], s: SemigroupK[F]): ConsK[F] =
new ConsK[F] {
def cons[A](hd: A, tl: F[A]): F[A] = s.combineK(p.pure(hd), tl)
}
}

@imports[ConsK]
trait ConsK0

36 changes: 36 additions & 0 deletions alleycats-core/src/main/scala/alleycats/Empty.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package alleycats

import cats.{Eq, Monoid}
import cats.syntax.eq._
import export.imports
import simulacrum.typeclass
import scala.collection.generic.CanBuildFrom

@typeclass trait Empty[A] {
def empty: A

def isEmpty(a: A)(implicit ev: Eq[A]): Boolean =
empty === a

def nonEmpty(a: A)(implicit ev: Eq[A]): Boolean =
empty =!= a
}

object Empty extends EmptyInstances0 {
def apply[A](a: => A): Empty[A] =
new Empty[A] { lazy val empty: A = a }
}

trait EmptyInstances0 extends EmptyInstances1 {
implicit def iterableIsEmpty[CC[X] <: Iterable[X], A](implicit cbf: CanBuildFrom[CC[A], A, CC[A]]): Empty[CC[A]] =
Empty(cbf().result)
}

trait EmptyInstances1 extends EmptyInstances2 {
// If Monoid extended Empty then this could be an exported subclass instance provided by Monoid
implicit def monoidIsEmpty[A: Monoid]: Empty[A] =
Empty(Monoid[A].empty)
}

@imports[Empty]
trait EmptyInstances2
22 changes: 22 additions & 0 deletions alleycats-core/src/main/scala/alleycats/EmptyK.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package alleycats

import export._
import simulacrum.typeclass

@typeclass trait EmptyK[F[_]] { self =>
def empty[A]: F[A]

def synthesize[A]: Empty[F[A]] =
new Empty[F[A]] {
def empty: F[A] = self.empty[A]
}
}

@imports[EmptyK]
object EmptyK

@exports
object EmptyKInstances {
@export(Instantiated)
implicit def instantiate[F[_], T](implicit ekf: EmptyK[F]): Empty[F[T]] = ekf.synthesize[T]
}
28 changes: 28 additions & 0 deletions alleycats-core/src/main/scala/alleycats/Extract.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package alleycats

import cats.{CoflatMap, Comonad}
import export.imports
import simulacrum.typeclass

@typeclass trait Extract[F[_]] {
def extract[A](fa: F[A]): A
}

object Extract extends Extract0 {
// Ideally this would be an exported subclass instance provided by Comonad
implicit def comonadIsExtract[F[_]](implicit ev: Comonad[F]): Extract[F] =
new Extract[F] {
def extract[A](fa: F[A]): A = ev.extract(fa)
}

// Ideally this would be an instance exported to Comonad
implicit def extractCoflatMapIsComonad[F[_]](implicit e: Extract[F], cf: CoflatMap[F]): Comonad[F] =
new Comonad[F] {
def extract[A](fa: F[A]): A = e.extract(fa)
override def map[A, B](fa: F[A])(f: A => B): F[B] = cf.map(fa)(f)
def coflatMap[A, B](fa: F[A])(f: F[A] => B): F[B] = cf.coflatMap(fa)(f)
}
}

@imports[Extract]
trait Extract0
24 changes: 24 additions & 0 deletions alleycats-core/src/main/scala/alleycats/One.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package alleycats

import cats.{Eq, Monoid}
import cats.syntax.eq._
import export.imports
import simulacrum.typeclass

@typeclass trait One[A] {
def one: A

def isOne(a: A)(implicit ev: Eq[A]): Boolean =
one === a

def nonOne(a: A)(implicit ev: Eq[A]): Boolean =
one =!= a
}

object One extends One0 {
def apply[A](a: => A): One[A] =
new One[A] { lazy val one: A = a }
}

@imports[One]
trait One0
29 changes: 29 additions & 0 deletions alleycats-core/src/main/scala/alleycats/Pure.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package alleycats

import cats.{Applicative, FlatMap, Monad}
import export.imports
import simulacrum.typeclass

@typeclass trait Pure[F[_]] {
def pure[A](a: A): F[A]
}

object Pure extends Pure0 {
// Ideally this would be an exported subclass instance provided by Applicative
implicit def applicativeIsPure[F[_]](implicit ev: Applicative[F]): Pure[F] =
new Pure[F] {
def pure[A](a: A): F[A] = ev.pure(a)
}

// Ideally this would be an instance exported to Monad
implicit def pureFlatMapIsMonad[F[_]](implicit p: Pure[F], fm: FlatMap[F]): Monad[F] =
new Monad[F] {
def pure[A](a: A): F[A] = p.pure(a)
override def map[A, B](fa: F[A])(f: A => B): F[B] = fm.map(fa)(f)
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = fm.flatMap(fa)(f)
def tailRecM[A, B](a: A)(f: (A) => F[Either[A, B]]): F[B] = fm.tailRecM(a)(f)
}
}

@imports[Pure]
trait Pure0
24 changes: 24 additions & 0 deletions alleycats-core/src/main/scala/alleycats/Zero.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package alleycats

import cats.Eq
import cats.syntax.eq._
import export.imports
import simulacrum.typeclass

@typeclass trait Zero[A] {
def zero: A

def isZero(a: A)(implicit ev: Eq[A]): Boolean =
zero === a

def nonZero(a: A)(implicit ev: Eq[A]): Boolean =
zero =!= a
}

object Zero extends Zero0 {
def apply[A](a: => A): Zero[A] =
new Zero[A] { lazy val zero: A = a }
}

@imports[Zero]
trait Zero0
13 changes: 13 additions & 0 deletions alleycats-core/src/main/scala/alleycats/std/all.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package alleycats
package std

import export._

@reexports(
EmptyKInstances,
ListInstances,
OptionInstances,
SetInstances,
TryInstances,
IterableInstances
) object all extends LegacySetInstances with LegacyTryInstances with LegacyIterableInstances
27 changes: 27 additions & 0 deletions alleycats-core/src/main/scala/alleycats/std/iterable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package alleycats
package std

import cats.{Eval, Foldable}
import export._

@reexports(IterableInstances)
object iterable extends LegacyIterableInstances

@exports
object IterableInstances {
@export(Orphan)
implicit val exportIterableFoldable: Foldable[Iterable] =
new Foldable[Iterable] {
override def foldLeft[A, B](fa: Iterable[A], b: B)(f: (B, A) => B): B = fa.foldLeft(b)(f)

override def foldRight[A, B](fa: Iterable[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Foldable.iterateRight(fa, lb)(f)
}

}

// TODO: remove when cats.Foldable support export-hook
trait LegacyIterableInstances {
implicit def legacyIterableFoldable(implicit e: ExportOrphan[Foldable[Iterable]]): Foldable[Iterable] = e.instance

}
22 changes: 22 additions & 0 deletions alleycats-core/src/main/scala/alleycats/std/list.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package alleycats
package std

import export._

@reexports(ListInstances)
object list

@exports
object ListInstances {
@export(Orphan)
implicit val exportListEmptyK: EmptyK[List] =
new EmptyK[List] {
def empty[A]: List[A] = Nil
}

@export(Orphan)
implicit val exportListConsK: ConsK[List] =
new ConsK[List] {
def cons[A](hd: A, tl: List[A]): List[A] = hd :: tl
}
}
16 changes: 16 additions & 0 deletions alleycats-core/src/main/scala/alleycats/std/option.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package alleycats
package std

import export._

@reexports(OptionInstances)
object option

@exports
object OptionInstances {
@export(Orphan)
implicit val exportOptionEmptyK: EmptyK[Option] =
new EmptyK[Option] {
def empty[A]: Option[A] = None
}
}
87 changes: 87 additions & 0 deletions alleycats-core/src/main/scala/alleycats/std/set.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package alleycats.std

import cats.{Applicative, Eval, Foldable, Monad, Traverse}
import export._

import scala.annotation.tailrec

@exports
object SetInstances {
// This method advertises parametricity, but relies on using
// universal hash codes and equality, which hurts our ability to
// rely on free theorems.
//
// Another problem some people have pointed out is the
// non-associativity of map when using impure functions. For
// example, consider the following expressions:
//
// import scala.util.Random
//
// val f = (_: Int) => 1
// val g = (_: Int) => Random.nextInt
//
// Set(1, 2, 3).map(f).map(g)
// Set(1, 2, 3).map(f andThen g)
//
// The first Set will contain one random number, and the second will
// contain three. Since `g` is not a function (speaking strictly)
// this would not be considered a law violation, but it still makes
// people uncomfortable.
@export(Orphan)
implicit val setMonad: Monad[Set] =
new Monad[Set] {
def pure[A](a: A): Set[A] = Set(a)
override def map[A, B](fa: Set[A])(f: A => B): Set[B] = fa.map(f)
def flatMap[A, B](fa: Set[A])(f: A => Set[B]): Set[B] = fa.flatMap(f)

def tailRecM[A, B](a: A)(f: (A) => Set[Either[A, B]]): Set[B] = {
val bldr = Set.newBuilder[B]

@tailrec def go(set: Set[Either[A, B]]): Unit = {
val lefts = set.foldLeft(Set[A]()) { (memo, either) =>
either.fold(
memo + _,
b => {
bldr += b
memo
}
)
}
if (lefts.isEmpty)
()
else
go(lefts.flatMap(f))
}
go(f(a))
bldr.result()
}
}

// Since iteration order is not guaranteed for sets, folds and other
// traversals may produce different results for input sets which
// appear to be the same.
@export(Orphan)
implicit val setTraverse: Traverse[Set] =
new Traverse[Set] {
def foldLeft[A, B](fa: Set[A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b)(f)
def foldRight[A, B](fa: Set[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Foldable.iterateRight(fa, lb)(f)
def traverse[G[_]: Applicative, A, B](sa: Set[A])(f: A => G[B]): G[Set[B]] = {
val G = Applicative[G]
sa.foldLeft(G.pure(Set.empty[B])) { (buf, a) =>
G.map2(buf, f(a))(_ + _)
}
}
}
}

@reexports(SetInstances)
object set extends LegacySetInstances

// TODO: remove when cats.{ Set, Traverse } support export-hook
trait LegacySetInstances {
implicit def legacySetMonad(implicit e: ExportOrphan[Monad[Set]]): Monad[Set] = e.instance

implicit def legacySetTraverse(implicit e: ExportOrphan[Traverse[Set]]): Traverse[Set] = e.instance
}
Loading

0 comments on commit ef76136

Please sign in to comment.