├── project ├── build.properties ├── plugins.sbt └── AlleycatsDeps.scala ├── version.sbt ├── core └── src │ └── main │ └── scala │ └── alleycats │ ├── syntax │ ├── all.scala │ ├── empty.scala │ └── foldable.scala │ ├── std │ ├── all.scala │ ├── option.scala │ ├── list.scala │ ├── iterable.scala │ ├── try.scala │ └── set.scala │ ├── ConsK.scala │ ├── EmptyK.scala │ ├── Extract.scala │ ├── Pure.scala │ ├── Empty.scala │ ├── Zero.scala │ └── One.scala ├── .gitignore ├── tests └── src │ └── test │ └── scala │ └── alleycats │ └── tests │ ├── SetTests.scala │ ├── IterableTests.scala │ ├── CatsEquality.scala │ └── AlleycatsSuite.scala ├── .jvmopts ├── scripts └── travis-publish.sh ├── laws └── src │ └── main │ └── scala │ └── alleycats │ └── laws │ └── discipline │ └── FlatMapRecTests.scala ├── .travis.yml ├── COPYING ├── scalastyle-config.xml └── README.md /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.12 2 | -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | version in ThisBuild := "0.2.1-SNAPSHOT" 2 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/syntax/all.scala: -------------------------------------------------------------------------------- 1 | package alleycats.syntax 2 | 3 | object all 4 | extends EmptySyntax 5 | with FoldableSyntax 6 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.typelevel" % "sbt-catalysts" % "0.2.6") 2 | 3 | addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0") 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | project/boot 2 | target 3 | .ensime 4 | .ensime_lucene 5 | TAGS 6 | \#*# 7 | *~ 8 | .#* 9 | .lib 10 | .history 11 | .*.swp 12 | .idea 13 | .idea/* 14 | .idea_modules 15 | .DS_Store 16 | .local* 17 | *local* -------------------------------------------------------------------------------- /tests/src/test/scala/alleycats/tests/SetTests.scala: -------------------------------------------------------------------------------- 1 | package alleycats.tests 2 | 3 | import alleycats.laws.discipline._ 4 | 5 | import alleycats.std.all._ 6 | 7 | class SetsTests extends AlleycatsSuite { 8 | 9 | checkAll("FlatMapRec[Set]", FlatMapRecTests[Set].tailRecM[Int]) 10 | 11 | } 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/std/all.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | package std 3 | 4 | import export._ 5 | 6 | @reexports( 7 | EmptyKInstances, 8 | ListInstances, 9 | OptionInstances, 10 | SetInstances, 11 | TryInstances, 12 | IterableInstances 13 | ) object all extends LegacySetInstances with LegacyTryInstances with LegacyIterableInstances 14 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/std/option.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | package std 3 | 4 | import export._ 5 | 6 | @reexports(OptionInstances) 7 | object option 8 | 9 | @exports 10 | object OptionInstances { 11 | @export(Orphan) 12 | implicit val exportOptionEmptyK: EmptyK[Option] = 13 | new EmptyK[Option] { 14 | def empty[A]: Option[A] = None 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/syntax/empty.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | package syntax 3 | 4 | import cats.Eq 5 | 6 | object empty extends EmptySyntax 7 | 8 | trait EmptySyntax { 9 | implicit class EmptyOps[A](a: A)(implicit ev: Empty[A]) { 10 | def isEmpty(implicit ev1: Eq[A]): Boolean = ev.isEmpty(a) 11 | def nonEmpty(implicit ev1: Eq[A]): Boolean = ev.nonEmpty(a) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/syntax/foldable.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | package syntax 3 | 4 | import cats.Foldable 5 | import cats.syntax.foldable._ 6 | 7 | object foldable extends FoldableSyntax 8 | 9 | trait FoldableSyntax { 10 | implicit class ExtraFoldableOps[F[_]: Foldable, A](fa: F[A]) { 11 | def foreach(f: A => Unit): Unit = 12 | fa.foldLeft(()) { (unit, a) => f(a); unit } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.jvmopts: -------------------------------------------------------------------------------- 1 | # see https://weblogs.java.net/blog/kcpeppe/archive/2013/12/11/case-study-jvm-hotspot-flags 2 | -Dfile.encoding=UTF8 3 | -Xms1G 4 | -Xmx3G 5 | -XX:MaxPermSize=512M 6 | -XX:ReservedCodeCacheSize=250M 7 | -XX:+TieredCompilation 8 | -XX:-UseGCOverheadLimit 9 | # effectively adds GC to Perm space 10 | -XX:+CMSClassUnloadingEnabled 11 | # must be enabled for CMSClassUnloadingEnabled to work 12 | -XX:+UseConcMarkSweepGC 13 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/ConsK.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | 3 | import cats.SemigroupK 4 | import export.imports 5 | import simulacrum.typeclass 6 | 7 | @typeclass trait ConsK[F[_]] { 8 | def cons[A](hd: A, tl: F[A]): F[A] 9 | } 10 | 11 | object ConsK extends ConsK0 { 12 | implicit def pureSemigroupKIsConsK[F[_]](implicit p: Pure[F], s: SemigroupK[F]): ConsK[F] = 13 | new ConsK[F] { 14 | def cons[A](hd: A, tl: F[A]): F[A] = s.combineK(p.pure(hd), tl) 15 | } 16 | } 17 | 18 | @imports[ConsK] 19 | trait ConsK0 20 | 21 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/std/list.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | package std 3 | 4 | import export._ 5 | 6 | @reexports(ListInstances) 7 | object list 8 | 9 | @exports 10 | object ListInstances { 11 | @export(Orphan) 12 | implicit val exportListEmptyK: EmptyK[List] = 13 | new EmptyK[List] { 14 | def empty[A]: List[A] = Nil 15 | } 16 | 17 | @export(Orphan) 18 | implicit val exportListConsK: ConsK[List] = 19 | new ConsK[List] { 20 | def cons[A](hd: A, tl: List[A]): List[A] = hd :: tl 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/EmptyK.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | 3 | import export._ 4 | import simulacrum.typeclass 5 | 6 | @typeclass trait EmptyK[F[_]] { self => 7 | def empty[A]: F[A] 8 | 9 | def synthesize[A]: Empty[F[A]] = 10 | new Empty[F[A]] { 11 | def empty: F[A] = self.empty[A] 12 | } 13 | } 14 | 15 | @imports[EmptyK] 16 | object EmptyK 17 | 18 | @exports 19 | object EmptyKInstances { 20 | @export(Instantiated) 21 | implicit def instantiate[F[_], T](implicit ekf: EmptyK[F]): Empty[F[T]] = ekf.synthesize[T] 22 | } 23 | -------------------------------------------------------------------------------- /scripts/travis-publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Example setting to use at command line for testing: 4 | # export TRAVIS_SCALA_VERSION=2.10.6;export TRAVIS_PULL_REQUEST="false";export TRAVIS_BRANCH="master" 5 | 6 | export publish_cmd="publishLocal" 7 | 8 | if [[ $TRAVIS_PULL_REQUEST == "false" && $TRAVIS_BRANCH == "master" && $(cat version.sbt) =~ "-SNAPSHOT" ]]; then 9 | export publish_cmd="publish gitSnapshots publish" 10 | fi 11 | 12 | sbt_cmd="sbt ++$TRAVIS_SCALA_VERSION" 13 | 14 | coverage="$sbt_cmd coverage validateJVM coverageReport && codecov" 15 | 16 | run_cmd="$coverage && $sbt_cmd clean validate $publish_cmd" 17 | eval $run_cmd 18 | -------------------------------------------------------------------------------- /tests/src/test/scala/alleycats/tests/IterableTests.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | package tests 3 | 4 | import cats.{Eval, Foldable} 5 | import cats.laws.discipline._ 6 | 7 | import alleycats.std.all._ 8 | 9 | class IterableTests extends AlleycatsSuite { 10 | 11 | checkAll("Foldable[Iterable]", FoldableTests[Iterable].foldable[Int, Int]) 12 | 13 | test("foldLeft sum == sum"){ 14 | val it = Iterable(1, 2, 3) 15 | Foldable[Iterable].foldLeft(it, 0){ 16 | case (b, a) => a + b 17 | } shouldEqual(it.sum) 18 | } 19 | 20 | test("foldRight early termination"){ 21 | Foldable[Iterable].foldRight(Iterable(1, 2, 3), Eval.now("KO")){ 22 | case (2, _) => Eval.now("OK") 23 | case (a, b) => b 24 | }.value shouldEqual(Eval.now("OK").value) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /project/AlleycatsDeps.scala: -------------------------------------------------------------------------------- 1 | package org 2 | package typelevel 3 | package alleycats 4 | 5 | import sbt._ 6 | import sbtcatalysts.CatalystsPlugin.autoImport._ 7 | 8 | object Dependencies { 9 | 10 | // Versions for libraries and packages 11 | // Package -> version 12 | val versions = Map[String, String]() 13 | 14 | // library definitions and links to their versions 15 | // Note that one version may apply to more than one library. 16 | // Library name -> version key, org, library 17 | val libraries = Map[String, (String, String, String)]() 18 | 19 | // compiler plugins definitions and links to their versions 20 | // Note that one version may apply to more than one plugin. 21 | // Library name -> version key, org, librar, crossVersion 22 | val scalacPlugins = Map[String, (String, String, String, CrossVersion)]() 23 | 24 | // Some helper methods to combine libraries 25 | } 26 | -------------------------------------------------------------------------------- /laws/src/main/scala/alleycats/laws/discipline/FlatMapRecTests.scala: -------------------------------------------------------------------------------- 1 | package alleycats.laws.discipline 2 | 3 | import cats._ 4 | import cats.laws.FlatMapLaws 5 | import cats.laws.discipline._ 6 | import org.scalacheck.Arbitrary 7 | import org.scalacheck.Prop._ 8 | import org.typelevel.discipline.Laws 9 | 10 | 11 | trait FlatMapRecTests[F[_]] extends Laws { 12 | def laws: FlatMapLaws[F] 13 | 14 | def tailRecM[A: Arbitrary](implicit 15 | ArbFA: Arbitrary[F[A]], 16 | ArbAFA: Arbitrary[A => F[A]], 17 | EqFA: Eq[F[A]] 18 | ): RuleSet = { 19 | new DefaultRuleSet( 20 | name = "flatMapTailRec", 21 | parent = None, 22 | "tailRecM consistent flatMap" -> forAll(laws.tailRecMConsistentFlatMap[A] _)) 23 | } 24 | } 25 | 26 | object FlatMapRecTests { 27 | def apply[F[_]: FlatMap]: FlatMapRecTests[F] = 28 | new FlatMapRecTests[F] { def laws: FlatMapLaws[F] = FlatMapLaws[F] } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/Extract.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | 3 | import cats.{Applicative, CoflatMap, Comonad} 4 | import export.imports 5 | import simulacrum.typeclass 6 | 7 | @typeclass trait Extract[F[_]] { 8 | def extract[A](fa: F[A]): A 9 | } 10 | 11 | object Extract extends Extract0 { 12 | // Ideally this would be an exported subclass instance provided by Comonad 13 | implicit def comonadIsExtract[F[_]](implicit ev: Comonad[F]): Extract[F] = 14 | new Extract[F] { 15 | def extract[A](fa: F[A]): A = ev.extract(fa) 16 | } 17 | 18 | // Ideally this would be an instance exported to Comonad 19 | implicit def extractCoflatMapIsComonad[F[_]](implicit e: Extract[F], cf: CoflatMap[F]): Comonad[F] = 20 | new Comonad[F] { 21 | def extract[A](fa: F[A]): A = e.extract(fa) 22 | override def map[A, B](fa: F[A])(f: A => B): F[B] = cf.map(fa)(f) 23 | def coflatMap[A, B](fa: F[A])(f: F[A] => B): F[B] = cf.coflatMap(fa)(f) 24 | } 25 | } 26 | 27 | @imports[Extract] 28 | trait Extract0 29 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/Pure.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | 3 | import cats.{Applicative, FlatMap, Monad} 4 | import export.imports 5 | import simulacrum.typeclass 6 | 7 | @typeclass trait Pure[F[_]] { 8 | def pure[A](a: A): F[A] 9 | } 10 | 11 | object Pure extends Pure0 { 12 | // Ideally this would be an exported subclass instance provided by Applicative 13 | implicit def applicativeIsPure[F[_]](implicit ev: Applicative[F]): Pure[F] = 14 | new Pure[F] { 15 | def pure[A](a: A): F[A] = ev.pure(a) 16 | } 17 | 18 | // Ideally this would be an instance exported to Monad 19 | implicit def pureFlatMapIsMonad[F[_]](implicit p: Pure[F], fm: FlatMap[F]): Monad[F] = 20 | new Monad[F] { 21 | def pure[A](a: A): F[A] = p.pure(a) 22 | override def map[A, B](fa: F[A])(f: A => B): F[B] = fm.map(fa)(f) 23 | def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = fm.flatMap(fa)(f) 24 | def tailRecM[A, B](a: A)(f: (A) => F[Either[A, B]]): F[B] = fm.tailRecM(a)(f) 25 | } 26 | } 27 | 28 | @imports[Pure] 29 | trait Pure0 30 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/Empty.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | 3 | import cats.{Eq, Monoid} 4 | import cats.syntax.eq._ 5 | import export.imports 6 | import simulacrum.typeclass 7 | import scala.collection.generic.CanBuildFrom 8 | 9 | @typeclass trait Empty[A] { 10 | def empty: A 11 | 12 | def isEmpty(a: A)(implicit ev: Eq[A]): Boolean = 13 | empty === a 14 | 15 | def nonEmpty(a: A)(implicit ev: Eq[A]): Boolean = 16 | empty =!= a 17 | } 18 | 19 | object Empty extends EmptyInstances0 { 20 | def apply[A](a: => A): Empty[A] = 21 | new Empty[A] { lazy val empty: A = a } 22 | } 23 | 24 | trait EmptyInstances0 extends EmptyInstances1 { 25 | implicit def iterableIsEmpty[CC[X] <: Iterable[X], A](implicit cbf: CanBuildFrom[CC[A], A, CC[A]]): Empty[CC[A]] = 26 | Empty(cbf().result) 27 | } 28 | 29 | trait EmptyInstances1 extends EmptyInstances2 { 30 | // If Monoid extended Empty then this could be an exported subclass instance provided by Monoid 31 | implicit def monoidIsEmpty[A: Monoid]: Empty[A] = 32 | Empty(Monoid[A].empty) 33 | } 34 | 35 | @imports[Empty] 36 | trait EmptyInstances2 37 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/Zero.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | 3 | import algebra.ring.{AdditiveMonoid, AdditiveSemigroup} 4 | import cats.{Eq, Monoid} 5 | import cats.syntax.eq._ 6 | import export.imports 7 | import simulacrum.typeclass 8 | import scala.collection.generic.CanBuildFrom 9 | 10 | @typeclass trait Zero[A] { 11 | def zero: A 12 | 13 | def isZero(a: A)(implicit ev: Eq[A]): Boolean = 14 | zero === a 15 | 16 | def nonZero(a: A)(implicit ev: Eq[A]): Boolean = 17 | zero =!= a 18 | } 19 | 20 | object Zero extends Zero0 { 21 | def apply[A](a: => A): Zero[A] = 22 | new Zero[A] { lazy val zero: A = a } 23 | 24 | // Ideally this would be an exported subclass instance provided by AdditiveMonoid 25 | implicit def additiveMonoidIsZero[A](implicit ev: AdditiveMonoid[A]): Zero[A] = 26 | Zero(ev.zero) 27 | 28 | // Ideally this would be an instance exported to AdditiveMonoid 29 | implicit def zeroWithSemigroupIsMonoid[A](implicit z: Zero[A], s: AdditiveSemigroup[A]): AdditiveMonoid[A] = 30 | new AdditiveMonoid[A] { 31 | def zero: A = z.zero 32 | def plus(x: A, y: A): A = s.plus(x, y) 33 | } 34 | } 35 | 36 | @imports[Zero] 37 | trait Zero0 38 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/std/iterable.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | package std 3 | 4 | import cats.{Eval, Foldable} 5 | import export._ 6 | 7 | @reexports(IterableInstances) 8 | object iterable extends LegacyIterableInstances 9 | 10 | @exports 11 | object IterableInstances { 12 | @export(Orphan) 13 | implicit val exportIterableFoldable: Foldable[Iterable] = 14 | new Foldable[Iterable] { 15 | override def foldLeft[A, B](fa: Iterable[A], b: B)(f: (B, A) => B): B = fa.foldLeft(b)(f) 16 | 17 | //based upon foldRight of List in Cats 18 | override def foldRight[A, B](fa: Iterable[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { 19 | def loop(as: Iterable[A]): Eval[B] = 20 | if (as.isEmpty) 21 | lb 22 | else { 23 | val h = as.head 24 | val t = as.tail 25 | f(h, Eval.defer(loop(t))) 26 | } 27 | Eval.defer(loop(fa)) 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | // TODO: remove when cats.Foldable support export-hook 35 | trait LegacyIterableInstances { 36 | implicit def legacyIterableFoldable(implicit e: ExportOrphan[Foldable[Iterable]]): Foldable[Iterable] = e.instance 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/One.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | 3 | import algebra.ring.{MultiplicativeMonoid, MultiplicativeSemigroup} 4 | import cats.{Eq, Monoid} 5 | import cats.syntax.eq._ 6 | import export.imports 7 | import simulacrum.typeclass 8 | import scala.collection.generic.CanBuildFrom 9 | 10 | @typeclass trait One[A] { 11 | def one: A 12 | 13 | def isOne(a: A)(implicit ev: Eq[A]): Boolean = 14 | one === a 15 | 16 | def nonOne(a: A)(implicit ev: Eq[A]): Boolean = 17 | one =!= a 18 | } 19 | 20 | object One extends One0 { 21 | def apply[A](a: => A): One[A] = 22 | new One[A] { lazy val one: A = a } 23 | 24 | // Ideally this would be an exported subclass instance provided by MultiplicativeMonoid 25 | implicit def multiplicativeMonoidIsOne[A](implicit ev: MultiplicativeMonoid[A]): One[A] = 26 | One(ev.one) 27 | 28 | // Ideally this would be an instance exported to MultiplicativeMonoid 29 | implicit def oneWithSemigroupIsMonoid[A](implicit z: One[A], s: MultiplicativeSemigroup[A]): MultiplicativeMonoid[A] = 30 | new MultiplicativeMonoid[A] { 31 | def one: A = z.one 32 | def times(x: A, y: A): A = s.times(x, y) 33 | } 34 | } 35 | 36 | @imports[One] 37 | trait One0 38 | -------------------------------------------------------------------------------- /tests/src/test/scala/alleycats/tests/CatsEquality.scala: -------------------------------------------------------------------------------- 1 | package alleycats.tests 2 | 3 | import cats._ 4 | 5 | import org.scalactic._ 6 | import TripleEqualsSupport.AToBEquivalenceConstraint 7 | import TripleEqualsSupport.BToAEquivalenceConstraint 8 | 9 | // The code in this file was taken and only slightly modified from 10 | // https://github.com/bvenners/equality-integration-demo 11 | // Thanks for the great examples, Bill! 12 | 13 | final class CatsEquivalence[T](T: Eq[T]) extends Equivalence[T] { 14 | def areEquivalent(a: T, b: T): Boolean = T.eqv(a, b) 15 | } 16 | 17 | trait LowPriorityStrictCatsConstraints extends TripleEquals { 18 | implicit def lowPriorityCatsCanEqual[A, B](implicit B: Eq[B], ev: A <:< B): CanEqual[A, B] = 19 | new AToBEquivalenceConstraint[A, B](new CatsEquivalence(B), ev) 20 | } 21 | 22 | trait StrictCatsEquality extends LowPriorityStrictCatsConstraints { 23 | override def convertToEqualizer[T](left: T): Equalizer[T] = super.convertToEqualizer[T](left) 24 | implicit override def convertToCheckingEqualizer[T](left: T): CheckingEqualizer[T] = new CheckingEqualizer(left) 25 | override def unconstrainedEquality[A, B](implicit equalityOfA: Equality[A]): CanEqual[A, B] = super.unconstrainedEquality[A, B] 26 | implicit def catsCanEqual[A, B](implicit A: Eq[A], ev: B <:< A): CanEqual[A, B] = 27 | new BToAEquivalenceConstraint[A, B](new CatsEquivalence(A), ev) 28 | } 29 | 30 | object StrictCatsEquality extends StrictCatsEquality 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | sudo: false 3 | install: 4 | - pip install --user codecov 5 | scala: 6 | - 2.10.6 7 | - 2.11.7 8 | script: 9 | - scripts/travis-publish.sh 10 | cache: 11 | directories: 12 | - "$HOME/.sbt/0.13/dependency" 13 | - "$HOME/.sbt/boot/scala*" 14 | - "$HOME/.sbt/launchers" 15 | - "$HOME/.ivy2/cache" 16 | before_cache: 17 | - du -h -d 1 $HOME/.ivy2/cache 18 | - du -h -d 2 $HOME/.sbt/ 19 | - find $HOME/.sbt -name "*.lock" -type f -delete 20 | - find $HOME/.ivy2/cache -name "ivydata-*.properties" -type f -delete 21 | notifications: 22 | webhooks: 23 | urls: 24 | - https://webhooks.gitter.im/e/37da8bd133bb28f86092 25 | on_success: change 26 | on_failure: always 27 | on_start: false 28 | env: 29 | global: 30 | - secure: DbdGtV4Tg3BFb5iizdP76Ri39MytHPtJTR7tkfCSWiblVJ63IIub5kiFX+wHVe3usmR65Hyg/cZekBj9OPFomr8CDZegL+JDlUc+/3mpAOYzkjA9XBszQLC3joag2MEbItxWGZ0PQhNSZrcM7eC27zNkteb7hibw6aSnRfJDGlcoqYu7ETBRvNenUtfeBFbVQp5YpcUttQW6actXtCBm1g65ONdkikKLl1M5+yQa29FqhOPHq2Gz+V+TS1Z2pueigBnWNZ3dIf0kY5UpQhfozVpfdFR9wYiiKERwVkBswakff+us1gwEqb0OjHh9UIvgEceJLajoX51b24wArKX0xyePoTwZur1xpB5C1u6mcwTr0hEqWt8agV1LLtzIDw9vaXEE6m3lEq5t7/WD5WaTud9RvDevlS0NIaWLDLLUXmclwV7zNrkZDRfy30ftljUyQQhE5OCDjFkgTTVJGBo4JoYvjJPbLVMqZn9MMYBxoS6ih0k1NTR/bvJ7QtuBEwWwN+4R6rOdVuHIG846up6rG4J4weypzJ50UebzzHYZACRsGko24SJqirup6ROLcvKKptYbmFFGSjUVCPNGGbNwwqZ/zX/p5rFC55P09suYeBI7DjUxip/xJ3p5nvT2avsfaxWy/RdSsa0OkqSFfnQt6VFxKWOM9RqjMlAZIXnqrXQ= 31 | - secure: AIBM5fCV96EpdeGrrDIzN5+OSaIgXRr8b8SnzUWzDRKLYHMkYMC1b4rggNZhu7EOSUXgDZKLTjLQhW8UposFhsxtvfzsKC/84h7VUQolObpHfsa5u0qg4xMXg2n5bUBwkoisLl8pODVXKCeWttn3jkQjsQ6Nwywm4L3wGTx4wZmCrzX5FMys5WjvDQ5yMgNxVYtXn2igkcgnKxvLWy4cN0tfNRD7nKlJrwftBs8t8H5kaE1SXiFfToxUcri2g1o+7lgIerwuB3lLKAWDaGscDmDEH3YwoGvu/2pTRNgx+38fssNLcxaeFpLajcLPxKm5dJR/1KSgfiQth2+Nu3WJbBGgUAxXmj5UzYrqrrXe4MvMsaMGfaQMBdUSvRrnG+7Luq+V5dcdThBW/Uaa3OJhd1VtOZOew6alsm62leSi/37FSkjrJucPpFfc2FuXHd8es4URpwf6AjgeDCwQFlS6nxhHPpe7PDSVgydrPgD4tdfiTDNRMI6DOsRIQS0NB5dYuotTHWYEH7nRGUjeel9bxgQSuHz1BFd0PPYvZiNsN9nJKm2Q/r38STPM4oSm5c/E+jnmur8oDATJF5hYbYRlgSgreaxawQGE2hpfMLgBRO/xnuVYPQia/xRQTrox3Pb/6REyP31vmwvwoRly2ghNue79ojkL9/goAJlT7sdZ+FI= 32 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/std/try.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | package std 3 | 4 | import cats.Bimonad 5 | import export._ 6 | import scala.annotation.tailrec 7 | import scala.util.{Success, Failure, Try} 8 | 9 | @reexports(TryInstances) 10 | object try_ extends LegacyTryInstances 11 | 12 | @exports 13 | object TryInstances { 14 | // There are various concerns people have over Try's ability to 15 | // satisfy the monad laws. For example, consider the following code: 16 | // 17 | // import scala.util.{Try, Success} 18 | // 19 | // def verify(n: Int): Try[Int] = 20 | // if (n == 0) sys.error("nope") else Success(n) 21 | // 22 | // val x = Try(0).flatMap(verify) 23 | // val y = verify(0) 24 | // 25 | // The monad laws require that `x` and `y` produce the same value, 26 | // but in this case `x` is a `Failure(_)` and `y` is undefined (due 27 | // an error being thrown). 28 | // 29 | // Since `verify` is not a total function, it is arguable whether 30 | // this constitutes a law violation, but there is enough concern 31 | // that the Monad[Try] instance has ended up here in Alleycats. 32 | // 33 | // Furthermore, since Cats has introduced a Bimonad[A], the Monad[Try] 34 | // and Comanad[Try] instances have been replaced by a single Bimonad[Try] 35 | // instance. 36 | // 37 | @export(Orphan) 38 | implicit val tryBimonad: Bimonad[Try] = 39 | new Bimonad[Try] { 40 | def pure[A](a: A): Try[A] = Try(a) 41 | override def map[A, B](fa: Try[A])(f: A => B): Try[B] = fa.map(f) 42 | def flatMap[A, B](fa: Try[A])(f: A => Try[B]): Try[B] = fa.flatMap(f) 43 | def coflatMap[A, B](fa: Try[A])(f: Try[A] => B): Try[B] = Try(f(fa)) 44 | def extract[A](p: Try[A]): A = p.get 45 | 46 | def tailRecM[A, B](a: A)(f: (A) => Try[Either[A, B]]): Try[B] = cats.instances.try_.catsStdInstancesForTry.tailRecM(a)(f) 47 | } 48 | } 49 | 50 | // TODO: remove when cats.{ Monad, Comonad, Bimonad } support export-hook 51 | trait LegacyTryInstances { 52 | implicit def legacyTryBimonad(implicit e: ExportOrphan[Bimonad[Try]]): Bimonad[Try] = e.instance 53 | } 54 | -------------------------------------------------------------------------------- /tests/src/test/scala/alleycats/tests/AlleycatsSuite.scala: -------------------------------------------------------------------------------- 1 | package alleycats 2 | package tests 3 | 4 | 5 | import catalysts.Platform 6 | 7 | import cats._ 8 | import cats.instances.AllInstances 9 | import cats.syntax.{AllSyntax, EqOps} 10 | 11 | import org.scalactic.anyvals.{PosZDouble, PosInt, PosZInt} 12 | import org.scalatest.{FunSuite, Matchers} 13 | import org.scalatest.prop.{Configuration, GeneratorDrivenPropertyChecks} 14 | import org.typelevel.discipline.scalatest.Discipline 15 | 16 | import org.scalacheck.{Arbitrary, Gen} 17 | import org.scalacheck.Arbitrary.arbitrary 18 | 19 | import scala.util.{Failure, Success, Try} 20 | 21 | trait TestSettings extends Configuration with Matchers { 22 | 23 | lazy val checkConfiguration: PropertyCheckConfiguration = 24 | PropertyCheckConfiguration( 25 | minSuccessful = if (Platform.isJvm) PosInt(50) else PosInt(5), 26 | maxDiscardedFactor = if (Platform.isJvm) PosZDouble(5.0) else PosZDouble(50.0), 27 | minSize = PosZInt(0), 28 | sizeRange = if (Platform.isJvm) PosZInt(10) else PosZInt(5), 29 | workers = PosInt(1)) 30 | 31 | lazy val slowCheckConfiguration: PropertyCheckConfiguration = 32 | if (Platform.isJvm) checkConfiguration 33 | else PropertyCheckConfiguration(sizeRange = 1, minSuccessful = 1) 34 | } 35 | 36 | /** 37 | * An opinionated stack of traits to improve consistency and reduce 38 | * boilerplate in Alleycats tests. Derived from Cats. 39 | */ 40 | trait AlleycatsSuite extends FunSuite with Matchers with GeneratorDrivenPropertyChecks with Discipline with TestSettings with AllInstances with AllSyntax with TestInstances with StrictCatsEquality { 41 | implicit override val generatorDrivenConfig: PropertyCheckConfiguration = 42 | checkConfiguration 43 | 44 | // disable Eq syntax (by making `catsSyntaxEq` not implicit), since it collides 45 | // with scalactic's equality 46 | override def catsSyntaxEq[A: Eq](a: A): EqOps[A] = new EqOps[A](a) 47 | } 48 | 49 | sealed trait TestInstances { 50 | // To be replaced by https://github.com/rickynils/scalacheck/pull/170 51 | implicit def arbitraryTry[A: Arbitrary]: Arbitrary[Try[A]] = 52 | Arbitrary(Gen.oneOf( 53 | arbitrary[A].map(Success(_)), 54 | arbitrary[Throwable].map(Failure(_)))) 55 | } 56 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Cats Copyright (c) 2015 Erik Osheim. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | ------ 22 | 23 | Code in Cats is derived in part from Scalaz. The Scalaz license follows: 24 | 25 | Copyright (c) 2009-2014 Tony Morris, Runar Bjarnason, Tom Adams, 26 | Kristian Domagala, Brad Clow, Ricky Clarkson, Paul Chiusano, Trygve 27 | Laugstøl, Nick Partridge, Jason Zaugg. All rights reserved. 28 | 29 | Redistribution and use in source and binary forms, with or without 30 | modification, are permitted provided that the following conditions 31 | are met: 32 | 1. Redistributions of source code must retain the above copyright 33 | notice, this list of conditions and the following disclaimer. 34 | 2. Redistributions in binary form must reproduce the above copyright 35 | notice, this list of conditions and the following disclaimer in the 36 | documentation and/or other materials provided with the distribution. 37 | 3. The name of the author may not be used to endorse or promote products 38 | derived from this software without specific prior written permission. 39 | 40 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 41 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 42 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 43 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 44 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 45 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 46 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 47 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 48 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 49 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50 | -------------------------------------------------------------------------------- /core/src/main/scala/alleycats/std/set.scala: -------------------------------------------------------------------------------- 1 | package alleycats.std 2 | 3 | import cats.{Applicative, Eval, Foldable, Monad, Traverse} 4 | import export._ 5 | 6 | import scala.annotation.tailrec 7 | 8 | @exports 9 | object SetInstances { 10 | // This method advertises parametricity, but relies on using 11 | // universal hash codes and equality, which hurts our ability to 12 | // rely on free theorems. 13 | // 14 | // Another problem some people have pointed out is the 15 | // non-associativity of map when using impure functions. For 16 | // example, consider the following expressions: 17 | // 18 | // import scala.util.Random 19 | // 20 | // val f = (_: Int) => 1 21 | // val g = (_: Int) => Random.nextInt 22 | // 23 | // Set(1, 2, 3).map(f).map(g) 24 | // Set(1, 2, 3).map(f andThen g) 25 | // 26 | // The first Set will contain one random number, and the second will 27 | // contain three. Since `g` is not a function (speaking strictly) 28 | // this would not be considered a law violation, but it still makes 29 | // people uncomfortable. 30 | @export(Orphan) 31 | implicit val setMonad: Monad[Set] = 32 | new Monad[Set] { 33 | def pure[A](a: A): Set[A] = Set(a) 34 | override def map[A, B](fa: Set[A])(f: A => B): Set[B] = fa.map(f) 35 | def flatMap[A, B](fa: Set[A])(f: A => Set[B]): Set[B] = fa.flatMap(f) 36 | 37 | def tailRecM[A, B](a: A)(f: (A) => Set[Either[A, B]]): Set[B] = { 38 | val bldr = Set.newBuilder[B] 39 | 40 | @tailrec def go(set: Set[Either[A, B]]): Unit = { 41 | val lefts = set.foldLeft(Set[A]()) { (memo, either) => 42 | either.fold( 43 | memo + _, 44 | b => { 45 | bldr += b 46 | memo 47 | } 48 | ) 49 | } 50 | if(lefts.isEmpty) 51 | () 52 | else 53 | go(lefts.flatMap(f)) 54 | } 55 | go(f(a)) 56 | bldr.result() 57 | } 58 | } 59 | 60 | // Since iteration order is not guaranteed for sets, folds and other 61 | // traversals may produce different results for input sets which 62 | // appear to be the same. 63 | @export(Orphan) 64 | implicit val setTraverse: Traverse[Set] = 65 | new Traverse[Set] { 66 | def foldLeft[A, B](fa: Set[A], b: B)(f: (B, A) => B): B = 67 | fa.foldLeft(b)(f) 68 | def foldRight[A, B](fa: Set[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = 69 | Foldable.iterateRight(fa.iterator, lb)(f) 70 | def traverse[G[_]: Applicative, A, B](sa: Set[A])(f: A => G[B]): G[Set[B]] = { 71 | val G = Applicative[G] 72 | sa.foldLeft(G.pure(Set.empty[B])) { (buf, a) => 73 | G.map2(buf, f(a))(_ + _) 74 | } 75 | } 76 | } 77 | } 78 | 79 | @reexports(SetInstances) 80 | object set extends LegacySetInstances 81 | 82 | // TODO: remove when cats.{ Set, Traverse } support export-hook 83 | trait LegacySetInstances { 84 | implicit def legacySetMonad(implicit e: ExportOrphan[Monad[Set]]): Monad[Set] = e.instance 85 | 86 | implicit def legacySetTraverse(implicit e: ExportOrphan[Traverse[Set]]): Traverse[Set] = e.instance 87 | } 88 | -------------------------------------------------------------------------------- /scalastyle-config.xml: -------------------------------------------------------------------------------- 1 | 2 | Scalastyle standard configuration 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | false 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Alleycats 2 | 3 | # ❗Deprecated❗ This project was moved into [typelevel/cats](https://github.com/typelevel/cats). 4 | 5 | > "But an outlaw can be defined as somebody who lives outside the law, 6 | > beyond the law and not necessarily against it." 7 | > 8 | > -- Hunter S. Thompson, "The Art of Journalism No. 1" 9 | > The Paris Review, Fall 2000, Issue 156. 10 | 11 | [![Build Status](https://api.travis-ci.org/non/alleycats.png)](https://travis-ci.org/non/alleycats) 12 | [![Chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/non/alleycats) 13 | [![Maven Central](https://img.shields.io/maven-central/v/org.typelevel/alleycats_2.11.svg)](https://maven-badges.herokuapp.com/maven-central/org.typelevel/alleycats_2.11) 14 | 15 | ### Overview 16 | 17 | Alleycats is an extension of the [Cats](http://github.com/non/cats) 18 | project that exists to support types which are not entirely savory or 19 | *law-abiding* but which may prove useful or necessary in some 20 | situations. 21 | 22 | In some cases, a type class instance can almost (but not quite) obey 23 | the required laws (e.g. a `Monad` instance for `scala.util.Try`). In 24 | other cases, type classes which lack laws or constraints may still be 25 | useful in some cases (e.g. `Empty[_]`, a type class which provides 26 | some notion of "emptiness"). 27 | 28 | Rather than argue about whether to permit these types in Cats, we 29 | provide a (slightly-disreputable) home for them here. 30 | 31 | ### Getting Alleycats 32 | 33 | Alleycats is currently available for Scala 2.10, 2.11 and 2.12. 34 | 35 | To get started with SBT, simply add the following to your `build.sbt` 36 | file: 37 | 38 | ```scala 39 | resolvers += Resolver.sonatypeRepo("releases") 40 | 41 | libraryDependencies += "org.typelevel" %% "alleycats-core" % "0.1.9" 42 | ``` 43 | 44 | You can also build Alleycats using 45 | [SBT](http://www.scala-sbt.org/0.13/tutorial/index.html). 46 | 47 | Alleycats uses [sbt-catalysts][] 0.1.9 to provide a consistent view of 48 | its dependencies on Typelevel projects. As of sbt-catalysts 0.1.9 the 49 | base version profile can be found [here][typelevel-deps]. 50 | 51 | [sbt-catalysts]: https://github.com/typelevel/sbt-catalysts 52 | [typelevel-deps]: https://github.com/typelevel/sbt-catalysts/blob/master/src/main/scala/org/typelevel/TypelevelDeps.scala 53 | 54 | 55 | ### Type classes 56 | 57 | Alleycats introduces several new type classes. Here is an overview of 58 | the instances introduced. 59 | 60 | #### Empty[A], Zero[A], and One[A] 61 | 62 | A commonly-requested type class is one that encodes the idea of a set 63 | having an identity element. Normally this would be done by defining a 64 | `Monoid[A]` instance, but in some cases the idea of emptiness is 65 | independent of a particular associative operation. 66 | 67 | In this case, `Empty[A]` may be used in place of `Monoid[A]`. It 68 | provides access to the *identity* element (via the `.empty` method), 69 | and can also provide `.isEmpty` and `.nonEmpty` if an `Eq[A]` is 70 | available. 71 | 72 | The two other type classes, `Zero[A]` and `One[A]`, are similar, 73 | except they correspond to `AdditiveMonoid[A]` and 74 | `MultiplicativeMonoid[A]` (found in the `algebra.ring` package). Their 75 | methods are called `zero` and `one`, respectively. 76 | 77 | While none of these type classes have their own laws, they are 78 | required not to violate the monoid laws when applicable. This means 79 | that if `Empty[A]` and `Semigroup[A]` are both available, that 80 | `Empty[A].empty` **must** function as an identity element for 81 | `Semigroup[A].combine`. In fact, together these instances can be 82 | viewed as a `Monoid[A]` (and there is an implicit method to that 83 | effect). 84 | 85 | The same rules apply to `Zero[A]` and `One[A]` and their respective 86 | associative operations. 87 | 88 | #### Pure[F[\_]] and Extract[F[\_]] 89 | 90 | The `Pure[F]` type class represents the `pure` method of 91 | `Applicative[F]` separated from its `map` and `ap` methods. Like the 92 | previous type classes, if `Pure[F]` and `Apply[F]` are both available 93 | they are required to be consistent (and should provide a valid 94 | `Applicative[F]` instance). 95 | 96 | Similarly, `Extract[F]` represents the `extract` method of 97 | `Comonad[F]` without `coflatMap` and `map` methods. When `Extract[F]` 98 | and `CoflatMap[F]` are available, they should provide a valid 99 | `Comonad[F]` instance. 100 | 101 | #### EmptyK[F[\_]] 102 | 103 | Finally, `EmptyK[F]` generalizes the `empty[A]` method from 104 | `MonoidK[F]`. The pattern here is the same as before -- 105 | `SemigroupK[F]` and `EmptyK[F]` should provide a valid `MonoidK[F]` 106 | instance. 107 | 108 | ### Instances 109 | 110 | Alleycats also provides some "disreputable" type class instances. 111 | 112 | #### Set[\_] instances 113 | 114 | Scala's `Set[_]` takes advantage of the universal availability of 115 | `.hashCode` and `.equals`. This makes it difficult to use 116 | [parametricity](http://failex.blogspot.jp/2013/06/fake-theorems-for-free.html) 117 | to reason about sets, and casts some doubt on their use with functors 118 | and monads. 119 | 120 | Alleycats provides `Monad[Set]` and `Traverse[Set]`. You can import 121 | these instances via `import alleycats.std.set._`. 122 | 123 | #### Try[\_] instances 124 | 125 | Scala's `Try[_]` is intended to replace the need for `try { ... } 126 | catch { ... }` syntax in Scala programs, to ease error-handling, and 127 | to transport exceptions as data. Due to the way `Try` transparently 128 | catches exceptions in `.map` and `.flatMap`, some people are skeptical 129 | that `Try` fulfills the necessary functor/monad laws. 130 | 131 | Alleycats provides a `Monad[Try]`. You can import this instance via 132 | `import alleycats.std.try._`. 133 | 134 | #### Iterable[\_] instances 135 | 136 | Scala's `collection.Iterable[_]` offers no guarantees that it's immutable, 137 | since it abstracts over the `mutable` and `immutable` variants. However it's 138 | the type used to represent a `Map`s `values`, and its often desirable to treat the 139 | values of a map as a `Foldable` collection. Alleycats provides a `Foldable[Iterable]`, eg: 140 | 141 | ``` 142 | import cats.implicits._ 143 | import alleycats.std.iterable._ 144 | 145 | //Result "AppleOrange" 146 | Map(1 -> "Apple", 2 -> "Orange").values.combineAll 147 | ``` 148 | 149 | ### Contributing 150 | 151 | This project's goal is to be very liberal about accepting type class 152 | instances, but to only provide instances which are absent from 153 | Cats. Feel free to open a pull-request on either project -- 154 | law-abiding instances will end up in Cats, and everything else will 155 | end up here. 156 | 157 | We are using the [Cats Gitter channel](https://gitter.im/non/cats) to 158 | discuss the Alleycats project as well. As with the Cats project, 159 | people are expected to follow the 160 | [Typelevel Code of Conduct](http://typelevel.org/conduct.html) when 161 | discussing Alleycats on the Github page, Gitter channel, or in other 162 | venues. 163 | 164 | ### Copyright and License 165 | 166 | All code is available to you under the MIT license, available at 167 | http://opensource.org/licenses/mit-license.php and also in the 168 | [COPYING](COPYING) file. 169 | 170 | Copyright Erik Osheim, 2015. 171 | --------------------------------------------------------------------------------