├── .gitignore ├── LICENSE ├── README.md ├── core └── shared │ └── src │ └── main │ └── scala │ └── structures │ ├── Alternative.scala │ ├── Applicative.scala │ ├── Apply.scala │ ├── Contravariant.scala │ ├── Equal.scala │ ├── Exponential.scala │ ├── Extend.scala │ ├── Extract.scala │ ├── FlatMap.scala │ ├── Foldable.scala │ ├── Functor.scala │ ├── Monad.scala │ ├── MonadCombine.scala │ ├── MonadFilter.scala │ ├── Monoid.scala │ ├── MonoidK.scala │ ├── Semigroup.scala │ ├── SemigroupK.scala │ ├── Traverse.scala │ ├── ops.scala │ ├── package.scala │ └── std │ ├── anyvals.scala │ ├── either.scala │ ├── function.scala │ ├── list.scala │ ├── map.scala │ ├── option.scala │ ├── ordering.scala │ └── package.scala ├── discipline └── src │ ├── main │ └── scala │ │ └── structures │ │ └── laws │ │ └── discipline │ │ ├── AlternativeDiscipline.scala │ │ ├── ApplicativeDiscipline.scala │ │ ├── ApplyDiscipline.scala │ │ ├── ContravariantDiscipline.scala │ │ ├── ExponentialDiscipline.scala │ │ ├── FlatMapDiscipline.scala │ │ ├── FunctorDiscipline.scala │ │ ├── MonadCombineDiscipline.scala │ │ ├── MonadDiscipline.scala │ │ ├── MonadFilterDiscipline.scala │ │ ├── MonoidDiscipline.scala │ │ ├── MonoidKDiscipline.scala │ │ ├── SemigroupDiscipline.scala │ │ └── SemigroupKDiscipline.scala │ └── test │ └── scala │ └── structures │ └── laws │ └── discipline │ └── LawTests.scala ├── examples └── src │ └── main │ └── scala │ └── FunctorExamples.scala ├── jsexamples └── src │ └── main │ ├── resources │ └── index.html │ └── scala │ └── JsExamples.scala ├── laws └── src │ └── main │ └── scala │ └── structures │ └── laws │ ├── AlternativeLaws.scala │ ├── ApplicativeLaws.scala │ ├── ApplyLaws.scala │ ├── ContravariantLaws.scala │ ├── ExponentialLaws.scala │ ├── FlatMapLaws.scala │ ├── FunctorLaws.scala │ ├── IsEqual.scala │ ├── MonadCombineLaws.scala │ ├── MonadFilterLaws.scala │ ├── MonadLaws.scala │ ├── MonoidKLaws.scala │ ├── MonoidLaws.scala │ ├── SemigroupKLaws.scala │ ├── SemigroupLaws.scala │ └── package.scala ├── project ├── Build.scala ├── build.properties └── plugins.sbt └── version.sbt /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Michael Pilquist 2 | Based heavily on Scalaz, whose copyright and license information is included below. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 3. Neither the name of the Structures team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | 13 | 14 | Scalaz License 15 | -------------- 16 | 17 | Copyright (c) 2009-2014 Tony Morris, Runar Bjarnason, Tom Adams, Kristian Domagala, Brad Clow, Ricky Clarkson, Paul Chiusano, Trygve Laugstøl, Nick Partridge, Jason Zaugg 18 | All rights reserved. 19 | 20 | Redistribution and use in source and binary forms, with or without 21 | modification, are permitted provided that the following conditions 22 | are met: 23 | 1. Redistributions of source code must retain the above copyright 24 | notice, this list of conditions and the following disclaimer. 25 | 2. Redistributions in binary form must reproduce the above copyright 26 | notice, this list of conditions and the following disclaimer in the 27 | documentation and/or other materials provided with the distribution. 28 | 3. The name of the author may not be used to endorse or promote products 29 | derived from this software without specific prior written permission. 30 | 31 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 32 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 33 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 34 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 35 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 37 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 38 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 39 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 40 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The Scala Structures Library 2 | ============================ 3 | 4 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mpilquist/structures?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | 6 | The Scala Structures library is a zero-dependency Scala library that defines commonly used type classes for functional programming. 7 | 8 | There are two modules, core and laws. The former provides the API of the library and the latter provides [Discipline](http://github.com/typelevel/discipline) based laws for each of the type classes. The laws project is intended to be used in test scope, to verify that instances of the type classes are law abiding. 9 | 10 | ## FAQ 11 | 12 | ### Is this a fork of Scalaz? 13 | 14 | Yes and no. This is a reimplementation of the core type classes which borrows heavily from the ideas in Scalaz. It differs in a number of significant ways though: 15 | 16 | - Structures defines type classes (and a minimal set of types needed for the implementation of those type classes), whereas Scalaz encompasses much more functionality, including data types (e.g., `NonEmptyList`, `Validation`, `\/`), replacements for standard library types (e.g., `Order`), and monad transformers (e.g., `Reader` / `Kleisli`). Structures has a much smaller focus than Scalaz. 17 | - Structures attempts to fit in with the Scala standard library when possible, even when the standard library provides unsafe functionality. This is contrary to Scalaz, which often provides safe alternatives to standard library constructs. For instance, `Maybe` is equivalent to `Option` but omits the unsafe `get` operation, and `Order` is equivalent to `scala.math.Ordering`, but defines the result of a comparison to be an enumerated type rather than an integer. There is great value in using safe alternatives, but providing those alternatives is not in scope of Structures. 18 | - Structures defines a small subset of the type classes defined by Scalaz. For example, Structures does not provide abstractions for modeling categories, arrows, or adjoint functors. 19 | - Structures does not provide special syntax support for working with implicit type class instances of odd shapes. Scalaz provides the `Unapply` type for dealing with odd shapes. 20 | 21 | In summary, Structures models a very small subset of what is provided by Scalaz, and does so in a different way, with a different goal. 22 | 23 | ### Why is minimizing API footprint desirable? Can't I just ignore stuff I don't use? 24 | 25 | One very real cost of a large API is that there is more stuff to change over time. These changes tend to be organized in to versioned releases, with some claim of binary compatibility or incompatibility. Tooling may enforce the binary compatibility claim. However, tooling typically operates on versioned releases rather than individual artifacts like types and methods. As a result, even if you use the stablest part of a large library, you have to ensure the version of the library you use is binary compatible with the version used by all other libraries in your application, or otherwise risk linkage errors resulting from no tool checked binary compatibility. 26 | 27 | Structures aims to be a small, stable library, with binary incompatible releases occurring at the same frequency as major Scala releases. 28 | 29 | ### Will this force library authors to choose between Scalaz and Structures? 30 | 31 | Structures is an experiment. It is focused on minimizing the number of type classes and integrating them closely with Scala language features and the standard library. It is entirely possible that this experiment fails in the sense that it no longer should exist as a library. In which case, the overall effort would still be valuable by demonstrating why a smaller subset of type classes is not useful. 32 | 33 | ### OK, but what about cats? 34 | 35 | The [Typelevel cats project](http://github.com/non/cats) is similar in many ways to Structures. However, cats is more ambitious in what it is attempting. Eventually, cats will grow in to a number of modules that together have similar scope to Scalaz. The focus of Structures is to provide a minimal subset of common FP type classes. As an example of how this goal results in differences, cats will likely include something like [Scalaz's `Unapply` syntax](http://typelevel.org/blog/2013/09/11/using-scalaz-Unapply.html), whereas that is out of scope for Structures. 36 | 37 | In short, we believe that there is room for multiple libraries in this space. 38 | 39 | ## Acknowledgements 40 | 41 | This project is influenced by a number of other projects. Notably: 42 | 43 | - [Scalaz](http://github.com/scalaz/scalaz) 44 | - [Functional Programming in Scala](http://www.manning.com/bjarnason/) 45 | - [Ed Kmett's semigroupoids](https://hackage.haskell.org/package/semigroupoids) 46 | - [Typelevel cats](http://github.com/non/cats) 47 | 48 | This project is also influenced by a number of individuals, who will be added here with their agreement. 49 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Alternative.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | @typeclass trait Alternative[F[_]] extends Any with Applicative[F] with MonoidK[F] { self => 6 | 7 | def asum[G[_]: Foldable, A](gfa: G[F[A]]): F[A] = 8 | Foldable[G].fold(gfa)(toMonoid[A]) 9 | 10 | def compose[G[_]: Alternative]: Alternative[Lambda[X => F[G[X]]]] = 11 | new Alternative.Composite[F, G] { 12 | def F = self 13 | def G = Alternative[G] 14 | } 15 | } 16 | 17 | object Alternative { 18 | 19 | trait Composite[F[_], G[_]] extends Any with Alternative[Lambda[X => F[G[X]]]] with Applicative.Composite[F, G] { 20 | def F: Alternative[F] 21 | def G: Alternative[G] 22 | def empty[A]: F[G[A]] = F.empty 23 | def combine[A](x: F[G[A]], y: => F[G[A]]): F[G[A]] = F.combine(x, y) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Applicative.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | /** 6 | * Type class that describes functors which have a lawful `Apply` instance and 7 | * that support a `pure` method which adheres to the laws described in 8 | * `structures.laws.ApplicativeLaws`. 9 | * 10 | * Also known as idioms. 11 | * 12 | * @see http://strictlypositive.org/IdiomLite.pdf 13 | */ 14 | @typeclass trait Applicative[F[_]] extends Any with Apply[F] { self => 15 | 16 | def pure[A](a: A): F[A] 17 | 18 | override def map[A, B](fa: F[A])(f: A => B): F[B] = 19 | apply(fa)(pure(f)) 20 | 21 | def compose[G[_]: Applicative]: Applicative[Lambda[X => F[G[X]]]] = 22 | new Applicative.Composite[F, G] { 23 | def F = self 24 | def G = Applicative[G] 25 | } 26 | } 27 | 28 | object Applicative { 29 | 30 | trait Composite[F[_], G[_]] extends Any with Applicative[Lambda[X => F[G[X]]]] with Apply.Composite[F, G] { 31 | def F: Applicative[F] 32 | def G: Applicative[G] 33 | def pure[A](a: A): F[G[A]] = F.pure(G.pure(a)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Apply.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.{ typeclass, noop } 4 | 5 | /** 6 | * Type class that describes functors that support an `apply` method which adheres 7 | * to the laws described in `structures.laws.ApplyLaws`. 8 | * 9 | * This type class models a more general version of an [[Applicative]] -- specifically, there's 10 | * no requirement for the `pure` method to exist. 11 | */ 12 | @typeclass trait Apply[F[_]] extends Any with Functor[F] { self => 13 | 14 | def apply[A, B](fa: F[A])(f: F[A => B]): F[B] 15 | 16 | @noop def flip[A, B](f: F[A => B]): F[A] => F[B] = 17 | fa => apply(fa)(f) 18 | 19 | 20 | @noop def apply2[A, B, X](fa: F[A], fb: F[B])(f: F[(A, B) => X]): F[X] = 21 | apply(fb)(apply(fa)(map(f)(ff => (a: A) => (b: B) => ff(a, b)))) 22 | 23 | @noop def apply3[A, B, C, X](fa: F[A], fb: F[B], fc: F[C])(f: F[(A, B, C) => X]): F[X] = 24 | apply(fc)(apply2(fa, fb)(map(f)(ff => (a: A, b: B) => (c: C) => ff(a, b, c)))) 25 | 26 | @noop def apply4[A, B, C, D, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D])(f: F[(A, B, C, D) => X]): F[X] = 27 | apply2(fc, fd)(apply2(fa, fb)(map(f)(ff => (a: A, b: B) => (c: C, d: D) => ff(a, b, c, d)))) 28 | 29 | @noop def apply5[A, B, C, D, E, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E])(f: F[(A, B, C, D, E) => X]): F[X] = 30 | apply2(fd, fe)(apply3(fa, fb, fc)(map(f)(ff => (a: A, b: B, c: C) => (d: D, e: E) => ff(a, b, c, d, e)))) 31 | 32 | @noop def apply6[A, B, C, D, E, G, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G])(f: F[(A, B, C, D, E, G) => X]): F[X] = 33 | apply3(fd, fe, fg)(apply3(fa, fb, fc)(map(f)(ff => (a: A, b: B, c: C) => (d: D, e: E, g: G) => ff(a, b, c, d, e, g)))) 34 | 35 | @noop def apply7[A, B, C, D, E, G, H, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H])(f: F[(A, B, C, D, E, G, H) => X]): F[X] = 36 | apply3(fe, fg, fh)(apply4(fa, fb, fc, fd)(map(f)(ff => (a: A, b: B, c: C, d: D) => (e: E, g: G, h: H) => ff(a, b, c, d, e, g, h)))) 37 | 38 | @noop def apply8[A, B, C, D, E, G, H, I, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H], fi: F[I])(f: F[(A, B, C, D, E, G, H, I) => X]): F[X] = 39 | apply4(fe, fg, fh, fi)(apply4(fa, fb, fc, fd)(map(f)(ff => (a: A, b: B, c: C, d: D) => (e: E, g: G, h: H, i: I) => ff(a, b, c, d, e, g, h, i)))) 40 | 41 | @noop def apply9[A, B, C, D, E, G, H, I, J, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H], fi: F[I], fj: F[J])(f: F[(A, B, C, D, E, G, H, I, J) => X]): F[X] = 42 | apply4(fg, fh, fi, fj)(apply5(fa, fb, fc, fd, fe)(map(f)(ff => (a: A, b: B, c: C, d: D, e: E) => (g: G, h: H, i: I, j: J) => ff(a, b, c, d, e, g, h, i, j)))) 43 | 44 | @noop def apply10[A, B, C, D, E, G, H, I, J, K, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H], fi: F[I], fj: F[J], fk: F[K])(f: F[(A, B, C, D, E, G, H, I, J, K) => X]): F[X] = 45 | apply5(fg, fh, fi, fj, fk)(apply5(fa, fb, fc, fd, fe)(map(f)(ff => (a: A, b: B, c: C, d: D, e: E) => (g: G, h: H, i: I, j: J, k: K) => ff(a, b, c, d, e, g, h, i, j, k)))) 46 | 47 | 48 | 49 | @noop def map2[A, B, X](fa: F[A], fb: F[B])(f: (A, B) => X): F[X] = 50 | apply(fb)(map(fa)(a => (b: B) => f(a, b))) 51 | 52 | @noop def map3[A, B, C, X](fa: F[A], fb: F[B], fc: F[C])(f: (A, B, C) => X): F[X] = 53 | apply(fc)(map2(fa, fb)((a, b) => c => f(a, b, c))) 54 | 55 | @noop def map4[A, B, C, D, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D])(f: (A, B, C, D) => X): F[X] = 56 | map2(tuple2(fc, fd), tuple2(fa, fb)) { case ((c, d), (a, b)) => f(a, b, c, d) } 57 | 58 | @noop def map5[A, B, C, D, E, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E])(f: (A, B, C, D, E) => X): F[X] = 59 | map2(tuple2(fd, fe), tuple3(fa, fb, fc)) { case ((d, e), (a, b, c)) => f(a, b, c, d, e) } 60 | 61 | @noop def map6[A, B, C, D, E, G, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G])(f: (A, B, C, D, E, G) => X): F[X] = 62 | map2(tuple3(fd, fe, fg), tuple3(fa, fb, fc)) { case ((d, e, g), (a, b, c)) => f(a, b, c, d, e, g) } 63 | 64 | @noop def map7[A, B, C, D, E, G, H, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H])(f: (A, B, C, D, E, G, H) => X): F[X] = 65 | map2(tuple3(fe, fg, fh), tuple4(fa, fb, fc, fd)) { case ((e, g, h), (a, b, c, d)) => f(a, b, c, d, e, g, h) } 66 | 67 | @noop def map8[A, B, C, D, E, G, H, I, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H], fi: F[I])(f: (A, B, C, D, E, G, H, I) => X): F[X] = 68 | map2(tuple4(fe, fg, fh, fi), tuple4(fa, fb, fc, fd)) { case ((e, g, h, i), (a, b, c, d)) => f(a, b, c, d, e, g, h, i) } 69 | 70 | @noop def map9[A, B, C, D, E, G, H, I, J, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H], fi: F[I], fj: F[J])(f: (A, B, C, D, E, G, H, I, J) => X): F[X] = 71 | map2(tuple4(fg, fh, fi, fj), tuple5(fa, fb, fc, fd, fe)) { case ((g, h, i, j), (a, b, c, d, e)) => f(a, b, c, d, e, g, h, i, j) } 72 | 73 | @noop def map10[A, B, C, D, E, G, H, I, J, K, X](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H], fi: F[I], fj: F[J], fk: F[K])(f: (A, B, C, D, E, G, H, I, J, K) => X): F[X] = 74 | map2(tuple5(fg, fh, fi, fj, fk), tuple5(fa, fb, fc, fd, fe)) { case ((g, h, i, j, k), (a, b, c, d, e)) => f(a, b, c, d, e, g, h, i, j, k) } 75 | 76 | 77 | 78 | @noop def tuple2[A, B](fa: F[A], fb: F[B]): F[(A, B)] = 79 | map2(fa, fb)((_, _)) 80 | 81 | @noop def tuple3[A, B, C](fa: F[A], fb: F[B], fc: F[C]): F[(A, B, C)] = 82 | map3(fa, fb, fc)((_, _, _)) 83 | 84 | @noop def tuple4[A, B, C, D](fa: F[A], fb: F[B], fc: F[C], fd: F[D]): F[(A, B, C, D)] = 85 | map4(fa, fb, fc, fd)((_, _, _, _)) 86 | 87 | @noop def tuple5[A, B, C, D, E](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E]): F[(A, B, C, D, E)] = 88 | map5(fa, fb, fc, fd, fe)((_, _, _, _, _)) 89 | 90 | @noop def tuple6[A, B, C, D, E, G](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G]): F[(A, B, C, D, E, G)] = 91 | map6(fa, fb, fc, fd, fe, fg)((_, _, _, _, _, _)) 92 | 93 | @noop def tuple7[A, B, C, D, E, G, H](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H]): F[(A, B, C, D, E, G, H)] = 94 | map7(fa, fb, fc, fd, fe, fg, fh)((_, _, _, _, _, _, _)) 95 | 96 | @noop def tuple8[A, B, C, D, E, G, H, I](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H], fi: F[I]): F[(A, B, C, D, E, G, H, I)] = 97 | map8(fa, fb, fc, fd, fe, fg, fh, fi)((_, _, _, _, _, _, _, _)) 98 | 99 | @noop def tuple9[A, B, C, D, E, G, H, I, J](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H], fi: F[I], fj: F[J]): F[(A, B, C, D, E, G, H, I, J)] = 100 | map9(fa, fb, fc, fd, fe, fg, fh, fi, fj)((_, _, _, _, _, _, _, _, _)) 101 | 102 | @noop def tuple10[A, B, C, D, E, G, H, I, J, K](fa: F[A], fb: F[B], fc: F[C], fd: F[D], fe: F[E], fg: F[G], fh: F[H], fi: F[I], fj: F[J], fk: F[K]): F[(A, B, C, D, E, G, H, I, J, K)] = 103 | map10(fa, fb, fc, fd, fe, fg, fh, fi, fj, fk)((_, _, _, _, _, _, _, _, _, _)) 104 | 105 | def compose[G[_]: Apply]: Apply[Lambda[X => F[G[X]]]] = 106 | new Apply.Composite[F, G] { 107 | def F = self 108 | def G = Apply[G] 109 | } 110 | } 111 | 112 | object Apply { 113 | 114 | trait Composite[F[_], G[_]] extends Any with Apply[Lambda[X => F[G[X]]]] with Functor.Composite[F, G] { 115 | def F: Apply[F] 116 | def G: Apply[G] 117 | override def apply[A, B](fa: F[G[A]])(f: F[G[A => B]]): F[G[B]] = { 118 | val flipped: F[G[A] => G[B]] = F.map(f)(G.flip) 119 | F.apply(fa)(flipped) 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Contravariant.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | @typeclass trait Contravariant[F[_]] extends Any with Exponential[F] { self => 6 | 7 | def contramap[A, B](fa: F[A])(f: B => A): F[B] 8 | 9 | override def xmap[A, B](fa: F[A])(f: A => B, g: B => A): F[B] = 10 | contramap(fa)(g) 11 | 12 | def compose[G[_]: Contravariant]: Functor[Lambda[X => F[G[X]]]] = 13 | new Contravariant.Composite[F, G] { 14 | def F = self 15 | def G = Contravariant[G] 16 | } 17 | 18 | override def composeWithFunctor[G[_]: Functor]: Contravariant[Lambda[X => F[G[X]]]] = 19 | new Contravariant.CovariantComposite[F, G] { 20 | def F = self 21 | def G = Functor[G] 22 | } 23 | 24 | override def composeWithContravariant[G[_]: Contravariant]: Functor[Lambda[X => F[G[X]]]] = 25 | compose[G] 26 | } 27 | 28 | object Contravariant { 29 | 30 | trait Composite[F[_], G[_]] extends Any with Functor[Lambda[X => F[G[X]]]] { 31 | def F: Contravariant[F] 32 | def G: Contravariant[G] 33 | def map[A, B](fga: F[G[A]])(f: A => B): F[G[B]] = 34 | F.contramap(fga)(gb => G.contramap(gb)(f)) 35 | } 36 | 37 | trait CovariantComposite[F[_], G[_]] extends Any with Contravariant[Lambda[X => F[G[X]]]] { 38 | def F: Contravariant[F] 39 | def G: Functor[G] 40 | def contramap[A, B](fga: F[G[A]])(f: B => A): F[G[B]] = 41 | F.contramap(fga)(gb => G.map(gb)(f)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Equal.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.{ typeclass, op } 4 | 5 | @typeclass trait Equal[A] { 6 | 7 | @op("===") 8 | def equal(x: A, y: A): Boolean 9 | 10 | @op("=!=") 11 | final def notEqual(x: A, y: A): Boolean = !equal(x, y) 12 | } 13 | 14 | object Equal { 15 | 16 | def natural[A]: Equal[A] = new Equal[A] { 17 | def equal(x: A, y: A) = x == y 18 | } 19 | 20 | def instance[A](f: (A, A) => Boolean): Equal[A] = new Equal[A] { 21 | def equal(x: A, y: A) = f(x, y) 22 | } 23 | 24 | implicit val contravariantInstance: Contravariant[Equal] = new Contravariant[Equal] { 25 | def contramap[A, B](fa: Equal[A])(f: B => A): Equal[B] = 26 | Equal.instance[B]((x, y) => fa.equal(f(x), f(y))) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Exponential.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | /** 6 | * Type class that describes type constructors that support an `xmap` method which adheres 7 | * to the laws described in `structures.laws.ExponentialLaws`. 8 | * 9 | * The name is short for "exponential functor", which is also known as "invariant functor". 10 | */ 11 | @typeclass trait Exponential[F[_]] extends Any { self => 12 | 13 | /** 14 | * Converts the supplied `F[A]` to an `F[B]` using a pair of functions -- `A => B` and `B => A`. 15 | */ 16 | def xmap[A, B](fa: F[A])(f: A => B, g: B => A): F[B] 17 | 18 | def compose[G[_]: Exponential]: Exponential[Lambda[X => F[G[X]]]] = 19 | new Exponential.Composite[F, G] { 20 | def F = self 21 | def G = Exponential[G] 22 | } 23 | 24 | def composeWithFunctor[G[_]: Functor]: Exponential[Lambda[X => F[G[X]]]] = 25 | new Exponential.Composite[F, G] { 26 | def F = self 27 | def G = Functor[G] 28 | } 29 | 30 | def composeWithContravariant[G[_]: Contravariant]: Exponential[Lambda[X => F[G[X]]]] = 31 | new Exponential.Composite[F, G] { 32 | def F = self 33 | def G = Contravariant[G] 34 | } 35 | } 36 | 37 | object Exponential { 38 | 39 | trait Composite[F[_], G[_]] extends Any with Exponential[Lambda[X => F[G[X]]]] { 40 | def F: Exponential[F] 41 | def G: Exponential[G] 42 | def xmap[A, B](fga: F[G[A]])(f: A => B, g: B => A): F[G[B]] = 43 | F.xmap(fga)(ga => G.xmap(ga)(f, g), gb => G.xmap(gb)(g, f)) 44 | } 45 | 46 | trait CovariantComposite[F[_], G[_]] extends Any with Exponential[Lambda[X => F[G[X]]]] { 47 | def F: Exponential[F] 48 | def G: Functor[G] 49 | def xmap[A, B](fga: F[G[A]])(f: A => B, g: B => A): F[G[B]] = 50 | F.xmap(fga)(ga => G.map(ga)(f), gb => G.map(gb)(g)) 51 | } 52 | 53 | trait ContravariantComposite[F[_], G[_]] extends Any with Exponential[Lambda[X => F[G[X]]]] { 54 | def F: Exponential[F] 55 | def G: Contravariant[G] 56 | def xmap[A, B](fga: F[G[A]])(f: A => B, g: B => A): F[G[B]] = 57 | F.xmap(fga)(ga => G.contramap(ga)(g), gb => G.contramap(gb)(f)) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Extend.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | @typeclass trait Extend[F[_]] extends Any with Functor[F] { 6 | def extend[A, B](fa: F[A])(f: F[A] => B): F[B] 7 | def duplicated[A](fa: F[A]): F[F[A]] = 8 | extend(fa)(identity) 9 | } 10 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Extract.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | @typeclass trait Extract[F[_]] extends Any with Extend[F] { 6 | def extract[A](a: F[A]): A 7 | } 8 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/FlatMap.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.{ noop, op, typeclass } 4 | 5 | @typeclass trait FlatMap[F[_]] extends Any with Apply[F] { 6 | 7 | @op(">>=", alias = true) 8 | def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] 9 | 10 | def flatten[A, B](ffa: F[F[A]]): F[A] = 11 | flatMap(ffa)(identity) 12 | 13 | @noop override def apply[A, B](fa: F[A])(f: F[A => B]): F[B] = 14 | flatMap(f)(map(fa)) 15 | } 16 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Foldable.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | @typeclass trait Foldable[F[_]] extends Any { self => 6 | 7 | def foldLeft[A, B](fa: F[A], initial: B)(f: (B, A) => B): B 8 | 9 | def foldRight[A, B](fa: F[A], initial: B)(f: (A, B) => B): B 10 | 11 | def foldMap[A, B](fa: F[A])(f: A => B)(implicit mb: Monoid[B]): B = 12 | foldLeft(fa, mb.empty)((b, a) => mb.combine(b, f(a))) 13 | 14 | def fold[A: Monoid](fa: F[A]): A = 15 | foldMap(fa)(identity) 16 | 17 | def traverse_[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[Unit] = 18 | foldLeft(fa, Applicative[G].pure(()))((acc, a) => Applicative[G].map2(acc, f(a))((_, _) => ())) 19 | 20 | def sequence_[G[_]: Applicative, A, B](fga: F[G[A]]): G[Unit] = 21 | traverse_(fga)(identity) 22 | 23 | def foldK[G[_]: MonoidK, A](fga: F[G[A]]): G[A] = 24 | foldLeft(fga, MonoidK[G].empty[A])((acc, ga) => MonoidK[G].combine(acc, ga)) 25 | 26 | def compose[G[_]: Foldable]: Foldable[Lambda[X => F[G[X]]]] = 27 | new Foldable.Composite[F, G] { 28 | def F = self 29 | def G = Foldable[G] 30 | } 31 | } 32 | 33 | object Foldable { 34 | 35 | trait Composite[F[_], G[_]] extends Any with Foldable[Lambda[X => F[G[X]]]] { 36 | def F: Foldable[F] 37 | def G: Foldable[G] 38 | def foldLeft[A, B](fa: F[G[A]], initial: B)(f: (B, A) => B): B = 39 | F.foldLeft(fa, initial)((acc, ga) => G.foldLeft(ga, acc)(f)) 40 | def foldRight[A, B](fa: F[G[A]], initial: B)(f: (A, B) => B): B = 41 | F.foldRight(fa, initial)((ga, acc) => G.foldRight(ga, acc)(f)) 42 | } 43 | } 44 | 45 | @typeclass trait Foldable1[F[_]] extends Any with Foldable[F] { self => 46 | 47 | def foldLeft1[A, B](fa: F[A])(initial: A => B)(f: (B, A) => B): B 48 | 49 | def foldRight1[A, B](fa: F[A])(initial: A => B)(f: (A, B) => B): B 50 | 51 | override def foldLeft[A, B](fa: F[A], initial: B)(f: (B, A) => B): B = 52 | foldLeft1(fa)(f(initial, _))(f) 53 | 54 | override def foldRight[A, B](fa: F[A], initial: B)(f: (A, B) => B): B = 55 | foldRight1(fa)(f(_, initial))(f) 56 | 57 | def foldMap1[A, B](fa: F[A])(f: A => B)(implicit sb: Semigroup[B]): B = 58 | foldLeft1(fa)(f)((b, a) => sb.combine(b, f(a))) 59 | 60 | def fold1[A: Semigroup](fa: F[A]): A = 61 | foldMap1(fa)(identity) 62 | 63 | def compose[G[_]: Foldable1]: Foldable1[Lambda[X => F[G[X]]]] = 64 | new Foldable1.Composite[F, G] { 65 | def F = self 66 | def G = Foldable1[G] 67 | } 68 | } 69 | 70 | object Foldable1 { 71 | 72 | trait Composite[F[_], G[_]] extends Any with Foldable1[Lambda[X => F[G[X]]]] { 73 | def F: Foldable1[F] 74 | def G: Foldable1[G] 75 | def foldLeft1[A, B](fga: F[G[A]])(initial: A => B)(f: (B, A) => B): B = 76 | F.foldLeft1(fga)(G.foldLeft1(_)(initial)(f))((acc, ga) => G.foldLeft(ga, acc)(f)) 77 | def foldRight1[A, B](fga: F[G[A]])(initial: A => B)(f: (A, B) => B): B = 78 | F.foldRight1(fga)(G.foldRight1(_)(initial)(f))((ga, acc) => G.foldRight(ga, acc)(f)) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Functor.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | /** 6 | * Type class that describes type constructors that support a `map` 7 | * method which adheres to the laws described in 8 | * `structures.laws.FunctorLaws`. 9 | * 10 | * The name is short for "covariant functor". 11 | * 12 | * Note that every functor is an exponential functor, where `xmap` is 13 | * implemented in terms of `map` by ignoring the `B => A` function. 14 | */ 15 | @typeclass trait Functor[F[_]] extends Any with Exponential[F] { self => 16 | 17 | /** 18 | * Converts the supplied `F[A]` in to an `F[B]` using the supplied `A => B`. 19 | */ 20 | def map[A, B](fa: F[A])(f: A => B): F[B] 21 | 22 | override def xmap[A, B](fa: F[A])(f: A => B, g: B => A): F[B] = 23 | map(fa)(f) 24 | 25 | /** empty the fa of the values, preserving the structure */ 26 | def void[A](fa: F[A]): F[Unit] = map(fa)(_ => ()) 27 | 28 | /** Lifts the supplied function in to the `F` type constructor. */ 29 | def lift[A, B](f: A => B): F[A] => F[B] = 30 | fa => map(fa)(f) 31 | 32 | /** Replaces the `A` value in `F[A]` with the supplied value. */ 33 | def as[A, B](fa: F[A], b: B): F[B] = 34 | map(fa)(_ => b) 35 | 36 | /** 37 | * Maps the supplied function over `F[A]`, returning the original value 38 | * and the result of the function application. 39 | */ 40 | def zipWith[A, B](fa: F[A])(f: A => B): F[(A, B)] = 41 | map(fa)(a => (a, f(a))) 42 | 43 | /** 44 | * Compose this functor F with a functor G to produce a composite 45 | * Functor on G[F[_]], with a map method which uses an A => B to 46 | * map a G[F[A]] to a G[F[B]]. 47 | */ 48 | def compose[G[_]: Functor]: Functor[Lambda[X => F[G[X]]]] = 49 | new Functor.Composite[F, G] { 50 | def F = self 51 | def G = Functor[G] 52 | } 53 | 54 | override def composeWithFunctor[G[_]: Functor]: Functor[Lambda[X => F[G[X]]]] = 55 | compose[G] 56 | 57 | override def composeWithContravariant[G[_]: Contravariant]: Contravariant[Lambda[X => F[G[X]]]] = 58 | new Functor.ContravariantComposite[F, G] { 59 | def F = self 60 | def G = Contravariant[G] 61 | } 62 | } 63 | 64 | object Functor { 65 | 66 | trait Composite[F[_], G[_]] extends Any with Functor[Lambda[X => F[G[X]]]] { 67 | def F: Functor[F] 68 | def G: Functor[G] 69 | override def map[A, B](fa: F[G[A]])(f: A => B): F[G[B]] = 70 | F.map(fa)(G.lift(f)) 71 | } 72 | 73 | trait ContravariantComposite[F[_], G[_]] extends Any with Contravariant[Lambda[X => F[G[X]]]] { 74 | def F: Functor[F] 75 | def G: Contravariant[G] 76 | override def contramap[A, B](fa: F[G[A]])(f: B => A): F[G[B]] = 77 | F.map(fa)(ga => G.contramap(ga)(f)) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Monad.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | @typeclass trait Monad[F[_]] extends Any with FlatMap[F] with Applicative[F] { 6 | 7 | override def map[A, B](fa: F[A])(f: A => B): F[B] = 8 | flatMap(fa)(a => pure(f(a))) 9 | } 10 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/MonadCombine.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | /** 6 | * Describes type constructors that have both a monad and a universally quantified monoid. 7 | * 8 | * Equivalently, this describes type constructors that have `MonadFilter` instances and additionally 9 | * define a universally quantified associative operation via the `combine` method. 10 | */ 11 | @typeclass trait MonadCombine[F[_]] extends Any with MonadFilter[F] with Alternative[F] { 12 | 13 | def unite[G[_]: Foldable, A](fga: F[G[A]]): F[A] = 14 | flatMap(fga)(ga => Foldable[G].foldMap(ga)(a => pure(a))(toMonoid[A])) 15 | } 16 | 17 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/MonadFilter.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | /** 6 | * Monad that defines one additional abstract method, `empty`, such that: 7 | * - for all `A`, `fa: F[A]`, `flatMap(empty)(_ => fa) == empty` 8 | * 9 | * This gives rise the the `filter` method and its variants. 10 | */ 11 | @typeclass trait MonadFilter[F[_]] extends Any with Monad[F] { 12 | 13 | def empty[A]: F[A] 14 | 15 | def filter[A](fa: F[A])(f: A => Boolean) = 16 | flatMap(fa)(a => if (f(a)) pure(a) else empty[A]) 17 | 18 | def filterM[A](fa: F[A])(f: A => F[Boolean]) = 19 | flatMap(fa)(a => flatMap(f(a))(b => if (b) pure(a) else empty[A])) 20 | } 21 | 22 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Monoid.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | @typeclass trait Monoid[A] extends Any with Semigroup[A] { 6 | def empty: A 7 | } 8 | 9 | object Monoid { 10 | 11 | def instance[A](empty: A)(combine: (A, => A) => A): Monoid[A] = { 12 | val empty0 = empty 13 | val combine0 = combine 14 | new Monoid[A] { 15 | def empty = empty0 16 | def combine(x: A, y: => A) = combine0(x, y) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/MonoidK.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | /** 6 | * Type class that describes type constructors which can provide a `Monoid[F[A]]` for any type `A`. 7 | */ 8 | @typeclass trait MonoidK[F[_]] extends Any with SemigroupK[F] { self => 9 | 10 | def empty[A]: F[A] 11 | 12 | def toMonoid[A]: Monoid[F[A]] = new Monoid[F[A]] { 13 | def empty = self.empty[A] 14 | def combine(x: F[A], y: => F[A]) = self.combine(x, y) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Semigroup.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.{ typeclass, op } 4 | 5 | @typeclass trait Semigroup[A] extends Any { 6 | @op("|+|") def combine(x: A, y: => A): A 7 | } 8 | 9 | object Semigroup { 10 | 11 | def instance[A](combine: (A, => A) => A): Semigroup[A] = { 12 | val combine0 = combine 13 | new Semigroup[A] { 14 | def combine(x: A, y: => A) = combine0(x, y) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/SemigroupK.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.{ typeclass, op } 4 | 5 | /** 6 | * Type class that describes type constructors which can provide a `Semigroup[F[A]]` for any type `A`. 7 | */ 8 | @typeclass trait SemigroupK[F[_]] extends Any { self => 9 | 10 | @op("<+>") 11 | def combine[A](x: F[A], y: => F[A]): F[A] 12 | 13 | def toSemigroup[A]: Semigroup[F[A]] = new Semigroup[F[A]] { 14 | def combine(x: F[A], y: => F[A]) = self.combine(x, y) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/Traverse.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | import simulacrum.typeclass 4 | 5 | @typeclass trait Traverse[F[_]] extends Any with Functor[F] with Foldable[F] { self => 6 | 7 | def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]] 8 | 9 | def sequence[G[_]: Applicative, A](fa: F[G[A]]): G[F[A]] = 10 | traverse(fa)(identity) 11 | 12 | def compose[G[_]: Traverse]: Traverse[Lambda[X => F[G[X]]]] = 13 | new Traverse.Composite[F, G] { 14 | def F = self 15 | def G = Traverse[G] 16 | } 17 | } 18 | 19 | object Traverse { 20 | 21 | trait Composite[F[_], G[_]] extends Any with Traverse[Lambda[X => F[G[X]]]] with Functor.Composite[F, G] with Foldable.Composite[F, G] { 22 | def F: Traverse[F] 23 | def G: Traverse[G] 24 | def traverse[H[_]: Applicative, A, B](fga: F[G[A]])(f: A => H[B]): H[F[G[B]]] = 25 | F.traverse(fga)(ga => G.traverse(ga)(f)) 26 | } 27 | } 28 | 29 | @typeclass trait Traverse1[F[_]] extends Any with Functor[F] with Foldable1[F] { self => 30 | 31 | def traverse1[G[_]: Apply, A, B](fa: F[A])(f: A => G[B]): G[F[B]] 32 | 33 | def sequence1[G[_]: Apply, A](fa: F[G[A]]): G[F[A]] = 34 | traverse1(fa)(identity) 35 | 36 | def compose[G[_]: Traverse1]: Traverse1[Lambda[X => F[G[X]]]] = 37 | new Traverse1.Composite[F, G] { 38 | def F = self 39 | def G = Traverse1[G] 40 | } 41 | } 42 | 43 | object Traverse1 { 44 | 45 | trait Composite[F[_], G[_]] extends Any with Traverse1[Lambda[X => F[G[X]]]] with Functor.Composite[F, G] with Foldable1.Composite[F, G] { 46 | def F: Traverse1[F] 47 | def G: Traverse1[G] 48 | def traverse1[H[_]: Apply, A, B](fga: F[G[A]])(f: A => H[B]): H[F[G[B]]] = 49 | F.traverse1(fga)(ga => G.traverse1(ga)(f)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/ops.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | object ops extends Equal.ToEqualOps 4 | with Semigroup.ToSemigroupOps 5 | with Monoid.ToMonoidOps 6 | with SemigroupK.ToSemigroupKOps 7 | with MonoidK.ToMonoidKOps 8 | with Exponential.ToExponentialOps 9 | with Contravariant.ToContravariantOps 10 | with Functor.ToFunctorOps 11 | with Apply.ToApplyOps 12 | with Applicative.ToApplicativeOps 13 | with Alternative.ToAlternativeOps 14 | with FlatMap.ToFlatMapOps 15 | with Monad.ToMonadOps 16 | with MonadFilter.ToMonadFilterOps 17 | with MonadCombine.ToMonadCombineOps 18 | with Foldable.ToFoldableOps 19 | with Foldable1.ToFoldable1Ops 20 | with Traverse.ToTraverseOps 21 | with Traverse1.ToTraverse1Ops 22 | with Extend.ToExtendOps 23 | with Extract.ToExtractOps 24 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/package.scala: -------------------------------------------------------------------------------- 1 | package object structures { 2 | 3 | type Id[A] = A 4 | 5 | implicit val IdInstance: Monad[Id] = new Monad[Id] with Extract[Id] { 6 | def pure[A](a: A) = a 7 | def flatMap[A, B](fa: Id[A])(f: A => Id[B]) = f(fa) 8 | def extract[A](a: Id[A]) = a 9 | def extend[A, B](fa: Id[A])(f: Id[A] => B) = f(fa) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/std/anyvals.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package std 3 | 4 | trait anyvals { 5 | implicit val byteMonoid: Monoid[Byte] = Monoid.instance(0: Byte)((x, y) => (x + y).toByte) 6 | implicit val shortMonoid: Monoid[Short] = Monoid.instance(0: Short)((x, y) => (x + y).toShort) 7 | implicit val intMonoid: Monoid[Int] = Monoid.instance(0)(_ + _) 8 | implicit val longMonoid: Monoid[Long] = Monoid.instance(0L)(_ + _) 9 | implicit val stringMonoid: Monoid[String] = Monoid.instance("")(_ + _) 10 | 11 | implicit val byteEqual: Equal[Byte] = Equal.natural 12 | implicit val shortEqual: Equal[Short] = Equal.natural 13 | implicit val intEqual: Equal[Int] = Equal.natural 14 | implicit val longEqual: Equal[Long] = Equal.natural 15 | implicit val stringEqual: Equal[String] = Equal.natural 16 | 17 | } 18 | 19 | object anyvals extends anyvals 20 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/std/either.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package std 3 | 4 | trait either { 5 | 6 | implicit def eitherTraverse[L]: Traverse[Either[L, ?]] = new Traverse[Either[L, ?]] { 7 | def map[A, B](fa: Either[L, A])(f: A => B) = fa.right.map(f) 8 | def foldLeft[A, B](fa: Either[L, A], initial: B)(f: (B, A) => B) = fa.fold(_ => initial, a => f(initial, a)) 9 | def foldRight[A, B](fa: Either[L, A], initial: B)(f: (A, B) => B) = fa.fold(_ => initial, a => f(a, initial)) 10 | def traverse[G[_]: Applicative, A, B](fa: Either[L, A])(f: A => G[B]): G[Either[L, B]] = { 11 | fa.right.map(f).fold(l => Applicative[G].pure(Left(l)), gb => Applicative[G].map(gb)(b => Right(b))) 12 | } 13 | } 14 | 15 | implicit def eitherMonad[L]: Monad[Either[L, ?]] = new Monad[Either[L, ?]] { 16 | def pure[A](a: A) = Right(a) 17 | def flatMap[A, B](fa: Either[L, A])(f: A => Either[L, B]) = fa.right.flatMap(f) 18 | } 19 | 20 | def accumulatingEither[L](implicit L: Semigroup[L]): Applicative[Either[L, ?]] = new Applicative[Either[L, ?]] { 21 | def pure[A](a: A) = Right(a) 22 | def apply[A, B](fa: Either[L, A])(ff: Either[L, A => B]) = (fa, ff) match { 23 | case (Right(a), Right(f)) => Right(f(a)) 24 | case (l, Right(_)) => l.asInstanceOf[Either[L, B]] 25 | case (Right(_), l) => l.asInstanceOf[Either[L, B]] 26 | case (Left(x), Left(y)) => Left(L.combine(x, y)) 27 | } 28 | } 29 | } 30 | 31 | object either extends either 32 | 33 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/std/function.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package std 3 | 4 | trait function { 5 | 6 | implicit def function1Monad[I]: Monad[I => ?] = new Monad[I => ?] { 7 | def pure[A](a: A): I => A = _ => a 8 | override def map[A, B](fa: I => A)(f: A => B): I => B = fa andThen f 9 | def flatMap[A, B](fa: I => A)(f: A => (I => B)): I => B = 10 | i => f(fa(i))(i) 11 | } 12 | 13 | implicit def function1Contravariant[O]: Contravariant[? => O] = new Contravariant[? => O] { 14 | def contramap[A, B](fa: A => O)(f: B => A): B => O = f andThen fa 15 | } 16 | } 17 | 18 | object function extends function 19 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/std/list.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package std 3 | 4 | trait list { 5 | 6 | implicit def listEqual[A](implicit A: Equal[A]): Equal[List[A]] = new Equal[List[A]] { 7 | def equal(x: List[A], y: List[A]) = { 8 | x.size == y.size && { 9 | x.zip(y).forall { case (xx, yy) => A.equal(xx, yy) } 10 | } 11 | } 12 | } 13 | 14 | implicit val list: MonadCombine[List] with Traverse[List] = new MonadCombine[List] with Traverse[List] { 15 | def pure[A](a: A) = List(a) 16 | def flatMap[A, B](fa: List[A])(f: A => List[B]) = fa flatMap f 17 | def empty[A] = Nil 18 | def combine[A](x: List[A], y: => List[A]) = x ++ y 19 | def foldLeft[A, B](fa: List[A], initial: B)(f: (B, A) => B) = fa.foldLeft(initial)(f) 20 | def foldRight[A, B](fa: List[A], initial: B)(f: (A, B) => B) = fa.foldRight(initial)(f) 21 | def traverse[G[_]: Applicative, A, B](fa: List[A])(f: A => G[B]): G[List[B]] = { 22 | fa.reverse.foldLeft(Applicative[G].pure(Nil: List[B])) { (acc, a) => 23 | Applicative[G].map2(f(a), acc)(_ :: _) 24 | } 25 | } 26 | } 27 | 28 | implicit def listMonoid[A]: Monoid[List[A]] = list.toMonoid 29 | } 30 | 31 | object list extends list 32 | 33 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/std/map.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package std 3 | 4 | trait map { 5 | 6 | implicit def mapMonoid[K, V: Semigroup]: Monoid[Map[K, V]] = Monoid.instance(Map.empty[K, V]) { (x, y) => 7 | y.foldLeft(x) { case (acc, (k, v)) => acc.updated(k, (acc.get(k).fold(v)(xv => Semigroup[V].combine(xv, v)))) } 8 | } 9 | 10 | implicit def mapEqual[K, V](implicit V: Equal[V]): Equal[Map[K, V]] = new Equal[Map[K, V]] { 11 | def equal(x: Map[K, V], y: Map[K, V]) = { 12 | x.size == y.size && { 13 | x.forall { case (xk, xv) => 14 | y.get(xk).fold(false)(yv => V.equal(xv, yv)) 15 | } 16 | } 17 | } 18 | } 19 | 20 | implicit def map[K]: FlatMap[Map[K, ?]] = new FlatMap[Map[K, ?]] { 21 | override def map[A, B](fa: Map[K, A])(f: A => B) = 22 | fa.map { case (k, v) => (k, f(v)) } 23 | override def apply[A, B](fa: Map[K, A])(f: Map[K, A => B]) = 24 | fa.flatMap { case (k, v) => f.get(k).map { fab => Map(k -> fab(v)) }.getOrElse(Map.empty) } 25 | def flatMap[A, B](fa: Map[K, A])(f: A => Map[K, B]): Map[K, B] = 26 | fa.flatMap { case (k, v) => f(v).get(k).fold(Map.empty[K, B])(b => Map(k -> b)) } 27 | } 28 | } 29 | 30 | object map extends map 31 | 32 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/std/option.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package std 3 | 4 | trait option { 5 | 6 | implicit def optionMonoid[A: Semigroup]: Monoid[Option[A]] = Monoid.instance(None: Option[A])((x, y) => 7 | x.fold(y)(xx => y.fold(Some(xx))(yy => Some(Semigroup[A].combine(xx, yy))))) 8 | 9 | implicit def optionEqual[A](implicit A: Equal[A]): Equal[Option[A]] = new Equal[Option[A]] { 10 | def equal(x: Option[A], y: Option[A]) = (x, y) match { 11 | case (Some(x), Some(y)) => A.equal(x, y) 12 | case (None, None) => true 13 | case _ => false 14 | } 15 | } 16 | 17 | implicit val option: MonadCombine[Option] with Traverse[Option] = new MonadCombine[Option] with Traverse[Option] { 18 | def pure[A](a: A) = Some(a) 19 | def flatMap[A, B](fa: Option[A])(f: A => Option[B]) = fa flatMap f 20 | def empty[A] = None 21 | def combine[A](fa: Option[A], fb: => Option[A]) = fa orElse fb 22 | def foldLeft[A, B](fa: Option[A], initial: B)(f: (B, A) => B) = fa.foldLeft(initial)(f) 23 | def foldRight[A, B](fa: Option[A], initial: B)(f: (A, B) => B) = fa.fold(initial)(f(_, initial)) 24 | def traverse[G[_]: Applicative, A, B](fa: Option[A])(f: A => G[B]): G[Option[B]] = { 25 | fa.fold(Applicative[G].pure(None: Option[B])) { a => 26 | Applicative[G].map(f(a))(pure) 27 | } 28 | } 29 | } 30 | 31 | 32 | } 33 | 34 | object option extends option 35 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/std/ordering.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package std 3 | 4 | trait ordering { 5 | 6 | implicit val ordering: Contravariant[Ordering] = new Contravariant[Ordering] { 7 | def contramap[A, B](fa: Ordering[A])(f: B => A): Ordering[B] = 8 | fa.on(f) 9 | } 10 | } 11 | 12 | object ordering extends ordering 13 | -------------------------------------------------------------------------------- /core/shared/src/main/scala/structures/std/package.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | package object std extends std.anyvals 4 | with std.list 5 | with std.option 6 | with std.map 7 | with std.either 8 | with std.ordering 9 | with std.function 10 | 11 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/AlternativeDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait AlternativeDiscipline[F[_]] extends ApplicativeDiscipline[F] with MonoidKDiscipline[F] { 9 | 10 | def laws: AlternativeLaws[F] 11 | 12 | def alternative[A, B, C](implicit 13 | arbFA: Arbitrary[F[A]], 14 | arbA: Arbitrary[A], 15 | arbB: Arbitrary[B], 16 | arbC: Arbitrary[C], 17 | arbFAtoB: Arbitrary[F[A => B]], 18 | arbFBtoC: Arbitrary[F[B => C]], 19 | eqFA: Equal[F[A]], 20 | eqFB: Equal[F[B]], 21 | eqFC: Equal[F[C]] 22 | ): RuleSet = new RuleSet { 23 | def name = "alternative" 24 | def bases = Nil 25 | def parents = Seq(applicative[A, B, C], monoidK[A]) 26 | def props = Seq( 27 | "alternative right distributivity" -> forAll { (fa: F[A], ff: F[A => B], fg: F[A => B]) => 28 | laws.alternativeRightDistributivity(fa, ff, fg).isEqual 29 | }, 30 | "alternative right absorption" -> forAll { (ff: F[A => B]) => 31 | laws.alternativeRightAbsorption(ff).isEqual 32 | }, 33 | "alternative left distributivity" -> forAll { (fa: F[A], fa2: F[A], f: A => B) => 34 | laws.alternativeLeftDistributivity(fa, fa2, f).isEqual 35 | } 36 | ) 37 | } 38 | } 39 | 40 | object AlternativeDiscipline { 41 | def apply[F[_]: Alternative]: AlternativeDiscipline[F] = new AlternativeDiscipline[F] { 42 | def laws = AlternativeLaws[F] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/ApplicativeDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait ApplicativeDiscipline[F[_]] extends ApplyDiscipline[F] { 9 | 10 | def laws: ApplicativeLaws[F] 11 | 12 | def applicative[A, B, C](implicit 13 | arbFA: Arbitrary[F[A]], 14 | arbA: Arbitrary[A], 15 | arbB: Arbitrary[B], 16 | arbC: Arbitrary[C], 17 | arbFAtoB: Arbitrary[F[A => B]], 18 | arbFBtoC: Arbitrary[F[B => C]], 19 | eqFA: Equal[F[A]], 20 | eqFB: Equal[F[B]], 21 | eqFC: Equal[F[C]] 22 | ): RuleSet = new DefaultRuleSet( 23 | name = "applicative", 24 | parent = Some(apply[A, B, C]), 25 | props = 26 | "applicative identity" -> forAll { (fa: F[A]) => 27 | laws.applicativeIdentity(fa).isEqual 28 | }, 29 | "applicative composition" -> forAll { (fa: F[A], fab: F[A => B], fbc: F[B => C]) => 30 | laws.applicativeComposition(fa, fab, fbc).isEqual 31 | }, 32 | "applicative homomorphism" -> forAll { (a: A, f: A => B) => 33 | laws.applicativeHomomorphism(a, f).isEqual 34 | }, 35 | "applicative interchange" -> forAll { (a: A, fab: F[A => B]) => 36 | laws.applicativeInterchange(a, fab).isEqual 37 | }, 38 | "applicative map consistency" -> forAll { (fa: F[A], f: A => B) => 39 | laws.applicativeMapConsistency(fa, f).isEqual 40 | } 41 | ) 42 | } 43 | 44 | object ApplicativeDiscipline { 45 | def apply[F[_]: Applicative]: ApplicativeDiscipline[F] = new ApplicativeDiscipline[F] { 46 | def laws = ApplicativeLaws[F] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/ApplyDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait ApplyDiscipline[F[_]] extends FunctorDiscipline[F] { 9 | 10 | def laws: ApplyLaws[F] 11 | 12 | def apply[A, B, C](implicit 13 | arbFA: Arbitrary[F[A]], 14 | arbA: Arbitrary[A], 15 | arbB: Arbitrary[B], 16 | arbC: Arbitrary[C], 17 | arbFAtoB: Arbitrary[F[A => B]], 18 | arbFBtoC: Arbitrary[F[B => C]], 19 | eqFA: Equal[F[A]], 20 | eqFC: Equal[F[C]] 21 | ): RuleSet = new DefaultRuleSet( 22 | name = "apply", 23 | parent = Some(functor[A, B, C]), 24 | props = 25 | "apply associative composition" -> forAll { (fa: F[A], ff: F[A => B], fg: F[B => C]) => 26 | laws.applyAssociativeComposition(fa, ff, fg).isEqual 27 | } 28 | ) 29 | } 30 | 31 | object ApplyDiscipline { 32 | def apply[F[_]: Apply]: ApplyDiscipline[F] = new ApplyDiscipline[F] { 33 | def laws = ApplyLaws[F] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/ContravariantDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait ContravariantDiscipline[F[_]] extends ExponentialDiscipline[F] { 9 | 10 | def laws: ContravariantLaws[F] 11 | 12 | def contravariant[A, B, C](implicit 13 | arbFA: Arbitrary[F[A]], 14 | arbA: Arbitrary[A], 15 | arbB: Arbitrary[B], 16 | arbC: Arbitrary[C], 17 | eqFA: Equal[F[A]], 18 | eqFC: Equal[F[C]] 19 | ): RuleSet = new DefaultRuleSet( 20 | name = "contravariant", 21 | parent = Some(exponential[A, B, C]), 22 | props = 23 | "contravariant identity" -> forAll { (fa: F[A]) => 24 | laws.contravariantIdentity(fa).isEqual 25 | }, 26 | "contravariant composition" -> forAll { (fa: F[A], f: B => A, g: C => B) => 27 | laws.contravariantComposition(fa, f, g).isEqual 28 | } 29 | ) 30 | } 31 | 32 | object ContravariantDiscipline { 33 | def apply[F[_]: Contravariant]: ContravariantDiscipline[F] = new ContravariantDiscipline[F] { 34 | def laws = ContravariantLaws[F] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/ExponentialDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | import org.typelevel.discipline.Laws 8 | 9 | trait ExponentialDiscipline[F[_]] extends Laws { 10 | 11 | def laws: ExponentialLaws[F] 12 | 13 | def exponential[A, B, C](implicit 14 | arbFA: Arbitrary[F[A]], 15 | arbA: Arbitrary[A], 16 | arbB: Arbitrary[B], 17 | arbC: Arbitrary[C], 18 | eqFA: Equal[F[A]], 19 | eqFC: Equal[F[C]] 20 | ): RuleSet = new SimpleRuleSet( 21 | name = "exponential", 22 | props = 23 | "exponential identity" -> forAll { (fa: F[A]) => 24 | laws.exponentialIdentity(fa).isEqual 25 | }, 26 | "exponential composition" -> forAll { (fa: F[A], f1: A => B, f2: B => A, g1: B => C, g2: C => B) => 27 | laws.exponentialComposition(fa, f1, f2, g1, g2).isEqual 28 | } 29 | ) 30 | } 31 | 32 | object ExponentialDiscipline { 33 | def apply[F[_]: Exponential]: ExponentialDiscipline[F] = new ExponentialDiscipline[F] { 34 | def laws = ExponentialLaws[F] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/FlatMapDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait FlatMapDiscipline[F[_]] extends ApplyDiscipline[F] { 9 | 10 | def laws: FlatMapLaws[F] 11 | 12 | def flatMap[A, B, C](implicit 13 | arbFA: Arbitrary[F[A]], 14 | arbFB: Arbitrary[F[B]], 15 | arbFC: Arbitrary[F[C]], 16 | arbA: Arbitrary[A], 17 | arbB: Arbitrary[B], 18 | arbC: Arbitrary[C], 19 | arbFAtoB: Arbitrary[F[A => B]], 20 | arbFBtoC: Arbitrary[F[B => C]], 21 | eqFA: Equal[F[A]], 22 | eqFB: Equal[F[B]], 23 | eqFC: Equal[F[C]] 24 | ): RuleSet = new DefaultRuleSet( 25 | name = "flatMap", 26 | parent = Some(apply[A, B, C]), 27 | props = 28 | "flatMap associativity" -> forAll { (fa: F[A], f: A => F[B], g: B => F[C]) => 29 | laws.flatMapAssociativity(fa, f, g).isEqual 30 | } 31 | ) 32 | } 33 | 34 | object FlatMapDiscipline { 35 | def apply[F[_]: FlatMap]: FlatMapDiscipline[F] = new FlatMapDiscipline[F] { 36 | def laws = FlatMapLaws[F] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/FunctorDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait FunctorDiscipline[F[_]] extends ExponentialDiscipline[F] { 9 | 10 | def laws: FunctorLaws[F] 11 | 12 | def functor[A, B, C](implicit 13 | arbFA: Arbitrary[F[A]], 14 | arbA: Arbitrary[A], 15 | arbB: Arbitrary[B], 16 | arbC: Arbitrary[C], 17 | eqFA: Equal[F[A]], 18 | eqFC: Equal[F[C]] 19 | ): RuleSet = new DefaultRuleSet( 20 | name = "functor", 21 | parent = Some(exponential[A, B, C]), 22 | props = 23 | "covariant identity" -> forAll { (fa: F[A]) => 24 | laws.functorIdentity(fa).isEqual 25 | }, 26 | "covariant composition" -> forAll { (fa: F[A], f: A => B, g: B => C) => 27 | laws.functorComposition(fa, f, g).isEqual 28 | } 29 | ) 30 | } 31 | 32 | object FunctorDiscipline { 33 | def apply[F[_]: Functor]: FunctorDiscipline[F] = new FunctorDiscipline[F] { 34 | def laws = FunctorLaws[F] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/MonadCombineDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait MonadCombineDiscipline[F[_]] extends MonadFilterDiscipline[F] with AlternativeDiscipline[F] { 9 | 10 | def laws: MonadCombineLaws[F] 11 | 12 | def monadCombine[A, B, C](implicit 13 | arbFA: Arbitrary[F[A]], 14 | arbFB: Arbitrary[F[B]], 15 | arbFC: Arbitrary[F[C]], 16 | arbA: Arbitrary[A], 17 | arbB: Arbitrary[B], 18 | arbC: Arbitrary[C], 19 | arbFAtoB: Arbitrary[F[A => B]], 20 | arbFBtoC: Arbitrary[F[B => C]], 21 | eqFA: Equal[F[A]], 22 | eqFB: Equal[F[B]], 23 | eqFC: Equal[F[C]] 24 | ): RuleSet = new RuleSet { 25 | def name = "monad combine" 26 | def bases = Nil 27 | def parents = Seq(monadFilter[A, B, C], alternative[A, B, C]) 28 | def props = Seq( 29 | "monad combine left distributivity" -> forAll { (fa: F[A], fa2: F[A], f: A => F[B]) => 30 | laws.monadCombineLeftDistributivity(fa, fa2, f).isEqual 31 | } 32 | ) 33 | } 34 | } 35 | 36 | object MonadCombineDiscipline { 37 | def apply[F[_]: MonadCombine]: MonadCombineDiscipline[F] = new MonadCombineDiscipline[F] { 38 | def laws = MonadCombineLaws[F] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/MonadDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait MonadDiscipline[F[_]] extends FlatMapDiscipline[F] with ApplicativeDiscipline[F] { 9 | 10 | def laws: MonadLaws[F] 11 | 12 | def monad[A, B, C](implicit 13 | arbFA: Arbitrary[F[A]], 14 | arbFB: Arbitrary[F[B]], 15 | arbFC: Arbitrary[F[C]], 16 | arbA: Arbitrary[A], 17 | arbB: Arbitrary[B], 18 | arbC: Arbitrary[C], 19 | arbFAtoB: Arbitrary[F[A => B]], 20 | arbFBtoC: Arbitrary[F[B => C]], 21 | eqFA: Equal[F[A]], 22 | eqFB: Equal[F[B]], 23 | eqFC: Equal[F[C]] 24 | ): RuleSet = new RuleSet { 25 | def name = "monad" 26 | def bases = Nil 27 | def parents = Seq(flatMap[A, B, C], applicative[A, B, C]) 28 | def props = Seq( 29 | "monad left identity" -> forAll { (a: A, f: A => F[B]) => 30 | laws.monadLeftIdentity(a, f).isEqual 31 | }, 32 | "monad right identity" -> forAll { (fa: F[A]) => 33 | laws.monadRightIdentity(fa).isEqual 34 | } 35 | ) 36 | } 37 | } 38 | 39 | object MonadDiscipline { 40 | def apply[F[_]: Monad]: MonadDiscipline[F] = new MonadDiscipline[F] { 41 | def laws = MonadLaws[F] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/MonadFilterDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait MonadFilterDiscipline[F[_]] extends MonadDiscipline[F] { 9 | 10 | def laws: MonadFilterLaws[F] 11 | 12 | def monadFilter[A, B, C](implicit 13 | arbFA: Arbitrary[F[A]], 14 | arbFB: Arbitrary[F[B]], 15 | arbFC: Arbitrary[F[C]], 16 | arbA: Arbitrary[A], 17 | arbB: Arbitrary[B], 18 | arbC: Arbitrary[C], 19 | arbFAtoB: Arbitrary[F[A => B]], 20 | arbFBtoC: Arbitrary[F[B => C]], 21 | eqFA: Equal[F[A]], 22 | eqFB: Equal[F[B]], 23 | eqFC: Equal[F[C]] 24 | ): RuleSet = new DefaultRuleSet( 25 | name = "monad", 26 | parent = Some(monad[A, B, C]), 27 | props = 28 | "monad filter left distributivity" -> forAll { (f: A => F[B]) => 29 | laws.monadFilterLeftDistributivity(f).isEqual 30 | }, 31 | "monad filter right distributivity" -> forAll { (fa: F[A]) => 32 | laws.monadFilterRightDistributivity(fa).isEqual 33 | } 34 | ) 35 | } 36 | 37 | object MonadFilterDiscipline { 38 | def apply[F[_]: MonadFilter]: MonadFilterDiscipline[F] = new MonadFilterDiscipline[F] { 39 | def laws = MonadFilterLaws[F] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/MonoidDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait MonoidDiscipline[A] extends SemigroupDiscipline[A] { 9 | 10 | def laws: MonoidLaws[A] 11 | 12 | def monoid(implicit 13 | arbA: Arbitrary[A], 14 | eqA: Equal[A] 15 | ): RuleSet = new DefaultRuleSet( 16 | name = "monoid", 17 | parent = Some(semigroup), 18 | props = 19 | "combine left identity" -> forAll { (a: A) => 20 | laws.combineLeftIdentity(a).isEqual 21 | }, 22 | "combine right identity" -> forAll { (a: A) => 23 | laws.combineRightIdentity(a).isEqual 24 | } 25 | ) 26 | } 27 | 28 | object MonoidDiscipline { 29 | def apply[A: Monoid]: MonoidDiscipline[A] = new MonoidDiscipline[A] { 30 | def laws = MonoidLaws[A] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/MonoidKDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | trait MonoidKDiscipline[F[_]] extends SemigroupKDiscipline[F] { 9 | 10 | def laws: MonoidKLaws[F] 11 | 12 | def monoidK[A](implicit 13 | arbFA: Arbitrary[F[A]], 14 | eqFA: Equal[F[A]] 15 | ): RuleSet = new DefaultRuleSet( 16 | name = "monoidK", 17 | parent = Some(semigroupK), 18 | props = 19 | "combine left identity" -> forAll { (a: F[A]) => 20 | laws.combineLeftIdentity(a).isEqual 21 | }, 22 | "combine right identity" -> forAll { (a: F[A]) => 23 | laws.combineRightIdentity(a).isEqual 24 | } 25 | ) 26 | } 27 | 28 | object MonoidKDiscipline { 29 | def apply[F[_]: MonoidK]: MonoidKDiscipline[F] = new MonoidKDiscipline[F] { 30 | def laws = MonoidKLaws[F] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/SemigroupDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | import org.typelevel.discipline.Laws 8 | 9 | trait SemigroupDiscipline[A] extends Laws { 10 | 11 | def laws: SemigroupLaws[A] 12 | 13 | def semigroup(implicit 14 | arbA: Arbitrary[A], 15 | eqA: Equal[A] 16 | ): RuleSet = new SimpleRuleSet( 17 | name = "semigroup", 18 | props = 19 | "combine associativity" -> forAll { (a: A, b: A, c: A) => 20 | laws.combineAssociativity(a, b, c).isEqual 21 | } 22 | ) 23 | } 24 | 25 | object SemigroupDiscipline { 26 | def apply[A: Semigroup]: SemigroupDiscipline[A] = new SemigroupDiscipline[A] { 27 | def laws = SemigroupLaws[A] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /discipline/src/main/scala/structures/laws/discipline/SemigroupKDiscipline.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | import org.typelevel.discipline.Laws 8 | 9 | trait SemigroupKDiscipline[F[_]] extends Laws { 10 | 11 | def laws: SemigroupKLaws[F] 12 | 13 | def semigroupK[A](implicit 14 | arbFA: Arbitrary[F[A]], 15 | eqFA: Equal[F[A]] 16 | ): RuleSet = new SimpleRuleSet( 17 | name = "semigroupK", 18 | props = 19 | "combine associativity" -> forAll { (a: F[A], b: F[A], c: F[A]) => 20 | laws.combineAssociativity(a, b, c).isEqual 21 | } 22 | ) 23 | } 24 | 25 | object SemigroupKDiscipline { 26 | def apply[F[_]: SemigroupK]: SemigroupKDiscipline[F] = new SemigroupKDiscipline[F] { 27 | def laws = SemigroupKLaws[F] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /discipline/src/test/scala/structures/laws/discipline/LawTests.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | package discipline 4 | 5 | import org.typelevel.discipline.scalatest.Discipline 6 | import org.scalatest.FunSuite 7 | import org.scalacheck.Arbitrary 8 | 9 | import structures.std._ 10 | 11 | class LawTests extends FunSuite with Discipline { 12 | checkAll("Byte", MonoidDiscipline[Byte].monoid) 13 | checkAll("Short", MonoidDiscipline[Short].monoid) 14 | checkAll("Int", MonoidDiscipline[Int].monoid) 15 | checkAll("Long", MonoidDiscipline[Long].monoid) 16 | checkAll("String", MonoidDiscipline[String].monoid) 17 | checkAll("List", MonoidDiscipline[List[Int]].monoid) 18 | 19 | checkAll("List", MonadCombineDiscipline[List].monadCombine[Int, String, Long]) 20 | checkAll("Option", MonadCombineDiscipline[Option].monadCombine[Int, String, Long]) 21 | checkAll("Map", FlatMapDiscipline[Map[Int, ?]].flatMap[Int, String, Long]) 22 | 23 | checkAll("Composite Applicative", ApplicativeDiscipline[Lambda[X => List[Option[X]]]](Applicative[List] compose Applicative[Option]).applicative[Int, String, Long]) 24 | 25 | implicit def function1Equal[A, B](implicit arbA: Arbitrary[A], eqB: Equal[B]): Equal[A => B] = Equal.instance { (x, y) => 26 | val samples = Stream.continually(arbA.arbitrary.sample).flatMap(x => x).take(100) 27 | samples.forall { a => x(a) === y(a) } 28 | } 29 | 30 | checkAll("Function1", MonadDiscipline[Int => ?].monad[Int, String, Long]) 31 | checkAll("Function1", ContravariantDiscipline[? => Int].contravariant[Int, String, Long]) 32 | 33 | implicit def arbOrdering[A]: Arbitrary[Ordering[A]] = Arbitrary(Arbitrary.arbitrary[(A, A) => Boolean].map { f => Ordering.fromLessThan(f) }) 34 | implicit def orderingEqual[A](implicit arbA: Arbitrary[A]): Equal[Ordering[A]] = Equal.instance { (x, y) => 35 | val samples = Stream.continually(Arbitrary.arbitrary[(A, A)].sample).flatMap(x => x).take(100) 36 | samples.forall { case (l, r) => x.compare(l, r) == y.compare(l, r) } 37 | } 38 | checkAll("Ordering", ContravariantDiscipline[Ordering].contravariant[Int, String, Long]) 39 | } 40 | -------------------------------------------------------------------------------- /examples/src/main/scala/FunctorExamples.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package example 3 | 4 | /** 5 | * A Functor is a ubiquitous typeclass involving type constructors of 6 | * kind * → *, which is another way of saying types that have a 7 | * single type variable. Examples might be Option, List, Future. 8 | * 9 | * The Functor category involves a single operation, named `map`: 10 | * 11 | * def map[A, B](fa: F[A])(f: A => B): F[B] 12 | * 13 | * This method takes a Function from A => B and turns an F[A] into an F[B] 14 | */ 15 | object FunctorExamples extends App { 16 | 17 | val len: String => Int = _.length 18 | 19 | // 20 | // example instances Instances 21 | // 22 | 23 | // we can trivially create a functor instance for a type which has a 24 | // well behaved map method; 25 | implicit val optionFunctor: Functor[Option] = new Functor[Option] { 26 | def map[A,B](fa: Option[A])(f: A => B) = fa map f 27 | } 28 | implicit val listFunctor: Functor[List] = new Functor[List] { 29 | def map[A,B](fa: List[A])(f: A => B) = fa map f 30 | } 31 | 32 | // 33 | // map 34 | // 35 | 36 | // Option is a functor which always returns a Some with the function 37 | // applied when the Option value is a Some. 38 | assert(Functor[Option].map(Some("adsf"))(len) == Some(4)) 39 | // When the Option is a None, it always returns None 40 | assert(Functor[Option].map(None)(len) == None) 41 | 42 | // List is a functor which applies the function to each element of 43 | // the list. 44 | assert(Functor[List].map(List("qwer", "adsfg"))(len) == List(4,5)) 45 | 46 | // 47 | // lift 48 | // 49 | 50 | // We can use the Funtor to "lift" a function to operate on the Functor type: 51 | val lenOption: Option[String] => Option[Int] = Functor[Option].lift(len) 52 | assert(lenOption(Some("abcd")) == Some(4)) 53 | 54 | 55 | // 56 | // zipWith 57 | // 58 | 59 | // Functor provides a zipWith function which pairs a value with the 60 | // result of applying a function to that value. 61 | val source = List("a", "aa", "b", "ccccc") 62 | val result = Map("a" -> 1, "aa" -> 2, "b" -> 1, "ccccc" -> 5) 63 | 64 | assert(Functor[List].zipWith(source)(len).toMap == result) 65 | 66 | 67 | // 68 | // Composition 69 | // 70 | 71 | // Functors compose! Given any Functor F[_] and any Functor G[_] we 72 | // can compose the two Functors to create a new Functor on F[G[_]]: 73 | val listOpt = Functor[List] compose Functor[Option] 74 | assert(listOpt.map(List(Some(1), None, Some(3)))(_ + 1) == List(Some(2), None, Some(4))) 75 | } 76 | -------------------------------------------------------------------------------- /jsexamples/src/main/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Examples JS 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /jsexamples/src/main/scala/JsExamples.scala: -------------------------------------------------------------------------------- 1 | import org.scalajs.dom 2 | import org.scalajs.dom.{document} 3 | import scala.scalajs.js.JSApp 4 | import scala.scalajs.js.annotation.JSExport 5 | 6 | import structures._ 7 | 8 | object JsExamples extends JSApp { 9 | 10 | private def combineExample(description: String, content: String): Unit = { 11 | val delement = document.createElement("p") 12 | delement.textContent = description 13 | document.body.appendChild(delement) 14 | val celement = document.createElement("p") 15 | celement.textContent = description 16 | document.body.appendChild(celement) 17 | () 18 | } 19 | 20 | def main(): Unit = { 21 | val len: String => Int = _.length 22 | 23 | // 24 | // example instances Instances 25 | // 26 | 27 | // we can trivially create a functor instance for a type which has a 28 | // well behaved map method; 29 | implicit val optionFunctor: Functor[Option] = new Functor[Option] { 30 | def map[A,B](fa: Option[A])(f: A => B) = fa map f 31 | } 32 | implicit val listFunctor: Functor[List] = new Functor[List] { 33 | def map[A,B](fa: List[A])(f: A => B) = fa map f 34 | } 35 | 36 | // 37 | // map 38 | // 39 | 40 | // Option is a functor which always returns a Some with the function 41 | // applied when the Option value is a Some. 42 | assert(Functor[Option].map(Some("adsf"))(len) == Some(4)) 43 | // When the Option is a None, it always returns None 44 | assert(Functor[Option].map(None)(len) == None) 45 | 46 | // List is a functor which applies the function to each element of 47 | // the list. 48 | assert(Functor[List].map(List("qwer", "adsfg"))(len) == List(4,5)) 49 | 50 | // 51 | // lift 52 | // 53 | 54 | // We can use the Funtor to "lift" a function to operate on the Functor type: 55 | val lenOption: Option[String] => Option[Int] = Functor[Option].lift(len) 56 | assert(lenOption(Some("abcd")) == Some(4)) 57 | 58 | 59 | // 60 | // fproduct 61 | // 62 | 63 | // Functor provides a fproduct function which pairs a value with the 64 | // result of applying a function to that value. 65 | val source = List("a", "aa", "b", "ccccc") 66 | val result = Map("a" -> 1, "aa" -> 2, "b" -> 1, "ccccc" -> 5) 67 | 68 | assert(Functor[List].zipWith(source)(len).toMap == result) 69 | 70 | 71 | // 72 | // Composition 73 | // 74 | 75 | // Functors compose! Given any Functor F[_] and any Functor G[_] we 76 | // can compose the two Functors to create a new Functor on F[G[_]]: 77 | val listOpt = Functor[List] compose Functor[Option] 78 | assert(listOpt.map(List(Some(1), None, Some(3)))(_ + 1) == List(Some(2), None, Some(4))) 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/AlternativeLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait AlternativeLaws[F[_]] extends ApplicativeLaws[F] with MonoidKLaws[F] { 5 | 6 | implicit val typeClass: Alternative[F] 7 | 8 | import Alternative.ops._ 9 | import typeClass.{ pure, empty } 10 | 11 | def alternativeRightDistributivity[A, B](fa: F[A], ff: F[A => B], fg: F[A => B]): IsEqual[F[B]] = 12 | fa.apply(ff <+> fg) =?= (fa.apply(ff) <+> fa.apply(fg)) 13 | 14 | def alternativeRightAbsorption[A, B](ff: F[A => B]): IsEqual[F[B]] = 15 | empty[A].apply(ff) =?= empty[B] 16 | 17 | def alternativeLeftDistributivity[A, B](fa: F[A], fa2: F[A], f: A => B): IsEqual[F[B]] = 18 | (fa <+> fa2).map(f) =?= ((fa map f) <+> (fa2 map f)) 19 | } 20 | 21 | object AlternativeLaws { 22 | def apply[F[_]: Alternative]: AlternativeLaws[F] = new AlternativeLaws[F] { 23 | val typeClass = Alternative[F] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/ApplicativeLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait ApplicativeLaws[F[_]] extends ApplyLaws[F] { 5 | 6 | implicit val typeClass: Applicative[F] 7 | 8 | import Applicative.ops._, typeClass.pure 9 | 10 | def applicativeIdentity[A](fa: F[A]): IsEqual[F[A]] = 11 | fa.apply(pure((a: A) => a)) =?= fa 12 | 13 | def applicativeComposition[A, B, C](fa: F[A], fab: F[A => B], fbc: F[B => C]): IsEqual[F[C]] = 14 | fa.apply(fab).apply(fbc) =?= fa.apply(fab.apply(fbc.apply(pure((bc: B => C) => (ab: A => B) => ab andThen bc)))) 15 | 16 | def applicativeHomomorphism[A, B](a: A, f: A => B): IsEqual[F[B]] = 17 | pure(a).apply(pure(f)) =?= pure(f(a)) 18 | 19 | def applicativeInterchange[A, B](a: A, fab: F[A => B]): IsEqual[F[B]] = 20 | pure(a).apply(fab) =?= fab.apply(pure((f: A => B) => f(a))) 21 | 22 | def applicativeMapConsistency[A, B](fa: F[A], f: A => B): IsEqual[F[B]] = 23 | fa.map(f) =?= fa.apply(pure(f)) 24 | } 25 | 26 | object ApplicativeLaws { 27 | def apply[F[_]: Applicative]: ApplicativeLaws[F] = new ApplicativeLaws[F] { 28 | val typeClass = Applicative[F] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/ApplyLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait ApplyLaws[F[_]] extends FunctorLaws[F] { 5 | 6 | implicit val typeClass: Apply[F] 7 | 8 | import Apply.ops._ 9 | 10 | def applyAssociativeComposition[A, B, C](fa: F[A], fab: F[A => B], fbc: F[B => C]): IsEqual[F[C]] = 11 | fa.apply(fab).apply(fbc) =?= fa.apply(fab.apply(fbc.map((bc: B => C) => (ab: A => B) => ab andThen bc))) 12 | } 13 | 14 | object ApplyLaws { 15 | def apply[F[_]: Apply]: ApplyLaws[F] = new ApplyLaws[F] { 16 | val typeClass = Apply[F] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/ContravariantLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait ContravariantLaws[F[_]] extends ExponentialLaws[F] { 5 | 6 | implicit val typeClass: Contravariant[F] 7 | 8 | import Contravariant.ops._ 9 | 10 | def contravariantIdentity[A](fa: F[A]): IsEqual[F[A]] = 11 | fa.contramap[A](identity) =?= fa 12 | 13 | def contravariantComposition[A, B, C](fa: F[A], f: B => A, g: C => B): IsEqual[F[C]] = 14 | fa.contramap(f).contramap(g) =?= fa.contramap(g andThen f) 15 | } 16 | 17 | object ContravariantLaws { 18 | def apply[F[_]: Contravariant]: ContravariantLaws[F] = new ContravariantLaws[F] { 19 | val typeClass = Contravariant[F] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/ExponentialLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait ExponentialLaws[F[_]] { 5 | 6 | implicit val typeClass: Exponential[F] 7 | 8 | import Exponential.ops._ 9 | 10 | def exponentialIdentity[A](fa: F[A]): IsEqual[F[A]] = 11 | fa.xmap[A](identity, identity) =?= fa 12 | 13 | def exponentialComposition[A, B, C](fa: F[A], f1: A => B, f2: B => A, g1: B => C, g2: C => B): IsEqual[F[C]] = 14 | fa.xmap(f1, f2).xmap(g1, g2) =?= fa.xmap(f1 andThen g1, g2 andThen f2) 15 | } 16 | 17 | object ExponentialLaws { 18 | def apply[F[_]: Exponential]: ExponentialLaws[F] = new ExponentialLaws[F] { 19 | val typeClass = Exponential[F] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/FlatMapLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait FlatMapLaws[F[_]] extends ApplyLaws[F] { 5 | 6 | implicit val typeClass: FlatMap[F] 7 | 8 | import FlatMap.ops._ 9 | 10 | def flatMapAssociativity[A, B, C](fa: F[A], f: A => F[B], g: B => F[C]): IsEqual[F[C]] = 11 | fa.flatMap(f).flatMap(g) =?= fa.flatMap { a => f(a).flatMap(g) } 12 | } 13 | 14 | object FlatMapLaws { 15 | def apply[F[_]: FlatMap]: FlatMapLaws[F] = new FlatMapLaws[F] { 16 | val typeClass = FlatMap[F] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/FunctorLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait FunctorLaws[F[_]] extends ExponentialLaws[F] { 5 | 6 | implicit val typeClass: Functor[F] 7 | 8 | import Functor.ops._ 9 | 10 | def functorIdentity[A](fa: F[A]): IsEqual[F[A]] = 11 | fa.map[A](identity) =?= fa 12 | 13 | def functorComposition[A, B, C](fa: F[A], f: A => B, g: B => C): IsEqual[F[C]] = 14 | fa.map(f).map(g) =?= fa.map(f andThen g) 15 | } 16 | 17 | object FunctorLaws { 18 | def apply[F[_]: Functor]: FunctorLaws[F] = new FunctorLaws[F] { 19 | val typeClass = Functor[F] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/IsEqual.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | case class IsEqual[A](lhs: A, rhs: A) { 5 | def isEqual(implicit eq: Equal[A]): Boolean = eq.equal(lhs, rhs) 6 | } 7 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/MonadCombineLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait MonadCombineLaws[F[_]] extends MonadFilterLaws[F] with AlternativeLaws[F] { 5 | 6 | implicit val typeClass: MonadCombine[F] 7 | 8 | import MonadCombine.ops._ 9 | 10 | def monadCombineLeftDistributivity[A, B](fa: F[A], fa2: F[A], f: A => F[B]): IsEqual[F[B]] = 11 | (fa <+> fa2).flatMap(f) =?= ((fa flatMap f) <+> (fa2 flatMap f)) 12 | } 13 | 14 | object MonadCombineLaws { 15 | def apply[F[_]: MonadCombine]: MonadCombineLaws[F] = new MonadCombineLaws[F] { 16 | val typeClass = MonadCombine[F] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/MonadFilterLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait MonadFilterLaws[F[_]] extends MonadLaws[F] { 5 | 6 | implicit val typeClass: MonadFilter[F] 7 | 8 | import MonadFilter.ops._, typeClass.empty 9 | 10 | def monadFilterLeftDistributivity[A, B](f: A => F[B]): IsEqual[F[B]] = 11 | empty[A].flatMap(f) =?= empty[B] 12 | 13 | def monadFilterRightDistributivity[A](fa: F[A]): IsEqual[F[A]] = 14 | fa.flatMap { a => empty[A] } =?= empty[A] 15 | } 16 | 17 | object MonadFilterLaws { 18 | def apply[F[_]: MonadFilter]: MonadFilterLaws[F] = new MonadFilterLaws[F] { 19 | val typeClass = MonadFilter[F] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/MonadLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait MonadLaws[F[_]] extends FlatMapLaws[F] with ApplicativeLaws[F] { 5 | 6 | implicit val typeClass: Monad[F] 7 | 8 | import Monad.ops._, typeClass.pure 9 | 10 | def monadLeftIdentity[A, B](a: A, f: A => F[B]): IsEqual[F[B]] = 11 | pure(a).flatMap(f) =?= f(a) 12 | 13 | def monadRightIdentity[A](fa: F[A]): IsEqual[F[A]] = 14 | fa.flatMap { a => pure(a) } =?= fa 15 | } 16 | 17 | object MonadLaws { 18 | def apply[F[_]: Monad]: MonadLaws[F] = new MonadLaws[F] { 19 | val typeClass = Monad[F] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/MonoidKLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait MonoidKLaws[F[_]] extends SemigroupKLaws[F] { 5 | 6 | implicit val typeClass: MonoidK[F] 7 | 8 | import MonoidK.ops._ 9 | 10 | def combineRightIdentity[A](x: F[A]): IsEqual[F[A]] = 11 | (x <+> typeClass.empty) =?= x 12 | 13 | def combineLeftIdentity[A](x: F[A]): IsEqual[F[A]] = 14 | (typeClass.empty <+> x) =?= x 15 | } 16 | 17 | object MonoidKLaws { 18 | def apply[F[_]: MonoidK]: MonoidKLaws[F] = new MonoidKLaws[F] { 19 | val typeClass = MonoidK[F] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/MonoidLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait MonoidLaws[A] extends SemigroupLaws[A] { 5 | 6 | implicit val typeClass: Monoid[A] 7 | 8 | import Monoid.ops._ 9 | 10 | def combineRightIdentity(x: A): IsEqual[A] = 11 | (x |+| typeClass.empty) =?= x 12 | 13 | def combineLeftIdentity(x: A): IsEqual[A] = 14 | (typeClass.empty |+| x) =?= x 15 | } 16 | 17 | object MonoidLaws { 18 | def apply[A: Monoid]: MonoidLaws[A] = new MonoidLaws[A] { 19 | val typeClass = Monoid[A] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/SemigroupKLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait SemigroupKLaws[F[_]] { 5 | 6 | implicit val typeClass: SemigroupK[F] 7 | 8 | import SemigroupK.ops._ 9 | 10 | def combineAssociativity[A](x: F[A], y: F[A], z: F[A]): IsEqual[F[A]] = 11 | ((x <+> y) <+> z) =?= (x <+> (y <+> z)) 12 | } 13 | 14 | object SemigroupKLaws { 15 | def apply[F[_]: SemigroupK]: SemigroupKLaws[F] = new SemigroupKLaws[F] { 16 | val typeClass = SemigroupK[F] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/SemigroupLaws.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | package laws 3 | 4 | trait SemigroupLaws[A] { 5 | 6 | implicit val typeClass: Semigroup[A] 7 | 8 | import Semigroup.ops._ 9 | 10 | def combineAssociativity(x: A, y: A, z: A): IsEqual[A] = 11 | ((x |+| y) |+| z) =?= (x |+| (y |+| z)) 12 | } 13 | 14 | object SemigroupLaws { 15 | def apply[A: Semigroup]: SemigroupLaws[A] = new SemigroupLaws[A] { 16 | val typeClass = Semigroup[A] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /laws/src/main/scala/structures/laws/package.scala: -------------------------------------------------------------------------------- 1 | package structures 2 | 3 | package object laws { 4 | 5 | implicit class IsEqualSyntax[A](val lhs: A) extends AnyVal { 6 | def =?=(rhs: A): IsEqual[A] = IsEqual(lhs, rhs) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | 4 | import org.scalajs.sbtplugin.ScalaJSPlugin 5 | import ScalaJSPlugin._ 6 | import ScalaJSPlugin.autoImport._ 7 | 8 | object StructuresBuild extends Build { 9 | type PE = Project => Project 10 | 11 | lazy val addKindProjector = 12 | addCompilerPlugin("org.spire-math" % "kind-projector_2.11" % "0.5.2") 13 | 14 | lazy val commonSettings = Seq( 15 | organization := "com.github.mpilquist", 16 | scalaVersion := "2.11.5", 17 | crossScalaVersions := Seq("2.11.5"), 18 | scalacOptions ++= Seq( 19 | "-Xfatal-warnings", 20 | "-Ywarn-dead-code", 21 | "-Ywarn-numeric-widen", 22 | "-Ywarn-value-discard", 23 | "-encoding", "UTF-8", 24 | "-feature", 25 | "-language:higherKinds", 26 | "-language:implicitConversions", 27 | "-deprecation", 28 | "-unchecked", 29 | "-Xcheckinit", 30 | "-Xlint", 31 | "-Xverify", 32 | "-Xfuture", 33 | "-Yclosure-elim", 34 | "-Yinline", 35 | "-Yno-adapted-args" 36 | ), 37 | testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-oD"), 38 | resolvers ++= Seq( 39 | "Sonatype Public" at "https://oss.sonatype.org/content/groups/public/", 40 | "bintray/non" at "http://dl.bintray.com/non/maven"), 41 | initialCommands += """ 42 | import structures._ 43 | import structures.std._ 44 | import structures.ops._ 45 | """ 46 | ) 47 | 48 | lazy val root = project.in(file(".")).aggregate(corejvm, laws, discipline, examples, corejs, jsExamples).settings(commonSettings: _*).settings( 49 | publishArtifact := false 50 | ) 51 | 52 | lazy val core = crossProject.settings(). 53 | settings(commonSettings: _*). 54 | settings( 55 | name := "structures-core", 56 | libraryDependencies ++= Seq( 57 | "com.github.mpilquist" %% "simulacrum" % "0.3.0" % "optional", 58 | "org.scalatest" %% "scalatest" % "2.2.3" % "test" 59 | ), 60 | addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0-M5" cross CrossVersion.full), 61 | addKindProjector 62 | ) 63 | 64 | lazy val corejvm = core.jvm 65 | lazy val corejs = core.js 66 | 67 | lazy val laws = project.dependsOn(corejvm). 68 | settings(commonSettings: _*). 69 | settings( 70 | name := "structures-laws", 71 | libraryDependencies ++= Seq( 72 | "org.scalacheck" %% "scalacheck" % "1.11.3" 73 | ), 74 | addKindProjector 75 | ) 76 | 77 | lazy val discipline = project.dependsOn(laws). 78 | settings(commonSettings: _*). 79 | settings( 80 | name := "structures-laws-discipline", 81 | libraryDependencies ++= Seq( 82 | "org.scalacheck" %% "scalacheck" % "1.11.3", 83 | "org.typelevel" %% "discipline" % "0.2.1", 84 | "org.scalatest" %% "scalatest" % "2.2.3" % "test" 85 | ), 86 | addKindProjector 87 | ) 88 | 89 | lazy val examples = project.dependsOn(corejvm). 90 | settings(commonSettings: _*). 91 | settings( 92 | name := "structures-examples", 93 | addKindProjector 94 | ) 95 | 96 | lazy val jsExamples = project.dependsOn(corejs). 97 | enablePlugins(ScalaJSPlugin). 98 | settings(commonSettings: _*). 99 | settings( 100 | name := "structures-js-examples", 101 | addKindProjector, 102 | libraryDependencies ++= Seq("org.scala-js" %%% "scalajs-dom" % "0.8.0") 103 | ) 104 | } 105 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.7 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.0") 2 | -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | version in ThisBuild := "0.1.0-SNAPSHOT" 2 | --------------------------------------------------------------------------------