├── project ├── .geetkeep └── build.properties ├── src ├── main │ └── scala │ │ ├── Fold.scala │ │ ├── CanFold.scala │ │ ├── std.scala │ │ └── Foldl.scala └── test │ └── scala │ ├── FoldProps.scala │ └── FoldSpec.scala ├── .gitignore ├── .travis.yml ├── README.md └── LICENSE /project/.geetkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.1.6 2 | -------------------------------------------------------------------------------- /src/main/scala/Fold.scala: -------------------------------------------------------------------------------- 1 | package fold 2 | 3 | object Fold extends 4 | FoldlFunctions with 5 | FoldlInstances with 6 | CanFoldInstances with 7 | Extensions 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ass 2 | *.log 3 | *.ensime 4 | # sbt specific 5 | dist/* 6 | target/ 7 | lib_managed/ 8 | src_managed/ 9 | project/boot/ 10 | project/plugins/project/ 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | sudo: false 3 | 4 | scala: 5 | - 2.11.8 6 | - 2.12.6 7 | - 2.11.12 8 | 9 | jdk: 10 | - oraclejdk8 11 | 12 | before_install: 13 | - export PATH=${PATH}:./vendor/bundle 14 | 15 | script: 16 | - sbt ++$TRAVIS_SCALA_VERSION test 17 | -------------------------------------------------------------------------------- /src/test/scala/FoldProps.scala: -------------------------------------------------------------------------------- 1 | package foldprops 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop.forAll 5 | import fold._ 6 | import Fold._ 7 | 8 | object FoldProps extends Properties("Fold") { 9 | 10 | property("contains = !(doesNotContain)") = forAll { (x: Int, xs: List[Int]) => 11 | contains(x).foldl(xs) || doesNotContain(x).foldl(xs) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/CanFold.scala: -------------------------------------------------------------------------------- 1 | package fold 2 | 3 | import Extensions._ 4 | import scala.collection._ 5 | 6 | trait CanFold[F[_], B] { 7 | def fold[A](fa: F[B])(fo: Foldl[B, A]): A 8 | def scan[A](fa: F[B])(fo: Foldl[B, A]): Seq[A] 9 | } 10 | 11 | object CanFold extends CanFoldInstances 12 | 13 | trait CanFoldInstances { 14 | 15 | implicit def StdIterable[F[B] <: GenTraversableLike[B, F[B]], B]: CanFold[F, B] = new CanFold[F, B] { 16 | def fold[A](xs: F[B])(fo: Foldl[B, A]): A = 17 | xs.foldWith(fo) 18 | 19 | def scan[A](xs: F[B])(fo: Foldl[B, A]): Seq[A] = 20 | xs.scanWith(fo) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/scala/std.scala: -------------------------------------------------------------------------------- 1 | package fold 2 | 3 | import scala.collection.generic.{ IsTraversableLike} 4 | import scala.collection.{ GenTraversableLike } 5 | 6 | class FoldableCollection[A, R <% GenTraversableLike[_,R]](val r: GenTraversableLike[A,R]) { 7 | def foldWith[S](fo: Foldl[A, S]): S = { 8 | r.foldLeft(fo)(_ step _).extract 9 | } 10 | 11 | def scanWith[S](fo: Foldl[A, S]): Seq[S] = { 12 | r.foldLeft((Seq[Foldl[A, S]](fo))) { (acc, e) => 13 | val nextFo = acc.last.step(e) 14 | acc :+ nextFo 15 | }.map(_.extract) 16 | } 17 | 18 | } 19 | 20 | object Extensions extends Extensions 21 | 22 | trait Extensions { 23 | implicit def toFold[A, R <% GenTraversableLike[_,R]] (r: R) (implicit fr: IsTraversableLike[R]): FoldableCollection[fr.A, R] = 24 | new FoldableCollection(fr conversion r) 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/amarpotghan/scala-fold.svg?branch=master)](https://travis-ci.org/amarpotghan/scala-fold) 2 | # scala-fold 3 | Composable folds in Scala 4 | 5 | # Introduction 6 | 7 | This library defines the `Foldl` data type (a left fold) which can be combined in the applicative style such that resulting 8 | fold requires only one traversal over the given structure. 9 | 10 | Library comes with common folds. You can always define your own fold by providing a step function and initial value. 11 | 12 | Library also comes with an extension method on scala's standard collections `foldWith`. You can use that on standard scala collections as follows, 13 | 14 | ```scala 15 | import fold._ 16 | import Fold._ 17 | 18 | List(1, 2, 3).foldWith(sum[Int]) 19 | 20 | ``` 21 | 22 | # Examples 23 | 24 | ## Simple sum of integers 25 | 26 | ```scala 27 | 28 | scala> import fold._ 29 | import fold._ 30 | 31 | scala> import Fold._ 32 | import Fold._ 33 | 34 | scala> Seq(1, 2, 3).foldWith(sum[Int]) 35 | res1: Int = 6 36 | 37 | ``` 38 | 39 | ## `Foldl`s are Applicatives, so we can compose `Foldl`s using applicative style: 40 | 41 | ```scala 42 | 43 | scala> import fold._ 44 | import fold._ 45 | 46 | scala> import Fold._ 47 | import Fold._ 48 | 49 | scala> import scalaz._ 50 | import scalaz._ 51 | 52 | scala> type Fold[A] = Foldl[Double, A] 53 | defined type alias Fold 54 | 55 | scala> def mean = Apply[Fold].apply2(sum[Double], length[Double, Double])(_ / _) 56 | mean: Fold[Double] 57 | 58 | scala> Seq(1.0, 2.0, 3.0).foldWith(mean) 59 | res1: Double = 2.0 60 | 61 | ``` 62 | Note that combined mean fold traverses List only once! 63 | 64 | ## Using Applicative syntax of Scalaz 65 | 66 | ```scala 67 | 68 | scala> import fold._ 69 | scala> import Fold._ 70 | 71 | scala> import scalaz.syntax.apply._ 72 | import scalaz.syntax.apply._ 73 | 74 | scala> def mean = (sum[Double] |@| length[Double, Double]) (_ / _) 75 | mean: fold.Foldl[Double,Double] 76 | 77 | scala> Seq(1.0, 2.0, 3.0).foldWith(mean) 78 | res3: Double = 2.0 79 | 80 | ``` 81 | ## You can also conveniently use numeric operations on `Foldl`: 82 | 83 | ```scala 84 | 85 | scala> import fold._ 86 | import fold._ 87 | 88 | scala> import Fold._ 89 | import Fold._ 90 | 91 | scala> def mean = sum[Double] / length[Double, Double] 92 | mean: fold.Foldl[Double,Double] 93 | 94 | scala> Seq(1.0, 2.0, 3.0).foldWith(mean) 95 | res2: Double = 2.0 96 | 97 | ``` 98 | `(/)` function uses Foldl's applicative instance, so again List is traversed only once. 99 | 100 | 101 | # Credits 102 | 103 | * [Gabriel Gonzalez](https://github.com/Gabriel439)'s [foldl library](https://hackage.haskell.org/package/foldl) 104 | * Phil Freeman's [purescript-folds](https://github.com/paf31/purescript-folds) 105 | * Max Rabkin’s [Beautiful folding](http://squing.blogspot.sg/2008/11/beautiful-folding.html) 106 | * Conal Elliott's [blogpost](http://conal.net/blog/posts/another-lovely-example-of-type-class-morphisms) 107 | 108 | 109 | Feedbacks welcome! 110 | 111 | # LICENSE 112 | 113 | Distributed under the [Apache 2.0](LICENSE). 114 | Copyright © 2016-2018 Amar Potghan. 115 | -------------------------------------------------------------------------------- /src/test/scala/FoldSpec.scala: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import org.specs2._ 4 | import fold._ 5 | import Fold.{length => foldLength, _} 6 | import scala.collection.{Seq} 7 | import scalaz._ 8 | import std.string._ 9 | 10 | class FoldSpecs extends Specification { 11 | import CanFold._ 12 | def is = sequential ^ s2"""Fold Specs 13 | $lengthSpec 14 | $isEmptySpec 15 | $sumSpec 16 | $productSpec 17 | $allSpec 18 | $anySpec 19 | $orSpec 20 | $andSpec 21 | $headSpec 22 | $lastSpec 23 | $lastOrElseSpec 24 | $reverseSpec 25 | $dedupSpec 26 | $avgSpec 27 | $containsSpec 28 | $doesNotContainSpec 29 | $minimumBySpec 30 | $maximumBySpec 31 | $cProp 32 | $foldMapSpec 33 | $syntaxSpec 34 | $sumScanSpec 35 | $takeWhileSpec 36 | $takeSpec 37 | $dropWhileSpec 38 | """ 39 | 40 | def lengthSpec = foldLength[String, Int].foldl(Seq("1", "2", "3", "4")) must_== 4 41 | 42 | def isEmptySpec = { 43 | isEmpty[String].foldl(Seq[String]()) must_== true 44 | isEmpty[String].foldl(Seq("")) must_== false 45 | } 46 | 47 | def sumSpec = sum[Int].foldl(Seq(1, 1, 1)) must_== 3 48 | def productSpec = product[Int].foldl(Seq(3, 3, 3)) must_== 27 49 | 50 | def allSpec = { 51 | all(identity: Boolean => Boolean).foldl(Seq(true, true, true)) must_== true 52 | all(identity: Boolean => Boolean).foldl(Seq[Boolean]()) must_== true 53 | all(identity: Boolean => Boolean).foldl(Seq(true, false)) must_== false 54 | all((x: Int) => x % 2 == 0).foldl(Seq(2, 4, 6)) must_== true 55 | all((x: Int) => x % 2 == 0).foldl(Seq(2, 3, 6)) must_== false 56 | all((x: Int) => x % 2 == 0).foldl(Seq[Int]()) must_== true 57 | all[String](Function.const(true)).foldl(Seq[String]()) must_== true 58 | } 59 | 60 | def anySpec = { 61 | any(identity: Boolean => Boolean).foldl(Seq(true, true, false)) must_== true 62 | any(identity: Boolean => Boolean).foldl(Seq[Boolean]()) must_== false 63 | any(identity: Boolean => Boolean).foldl(Seq(false, false)) must_== false 64 | any((x: Int) => x % 2 == 0).foldl(Seq(1, 2, 3)) must_== true 65 | any((x: Int) => x % 2 == 0).foldl(Seq(1, 1, 3)) must_== false 66 | any((x: Int) => x % 2 == 0).foldl(Seq[Int]()) must_== false 67 | any[String](Function.const(true)).foldl(Seq[String]()) must_== false 68 | } 69 | 70 | def orSpec = { 71 | or.foldl(Seq(true, true, false)) must_== true 72 | or.foldl(Seq(false, false)) must_== false 73 | or.foldl(Seq[Boolean]()) must_== false 74 | } 75 | 76 | def andSpec = { 77 | and.foldl(Seq(true, true, false)) must_== false 78 | and.foldl(Seq(true, true)) must_== true 79 | and.foldl(Seq[Boolean]()) must_== true 80 | } 81 | 82 | def headSpec = { 83 | head.foldl(Seq("1", "2")) must_== Some("1") 84 | head.foldl(Seq[String]()) must_== None 85 | } 86 | 87 | def lastSpec = last.foldl(Seq(1, 2, 3)) must_== Some(3) 88 | def lastOrElseSpec = lastOrElse(0).foldl(Seq[Int]()) must_== 0 89 | def reverseSpec = reverse.foldl(List(1, 2, 3)) must_== List(3, 2 , 1) 90 | def dedupSpec = dedup.foldl(Seq(1, 2, 3, 3)) must_== List(1, 2, 3) 91 | def dedupOnFoldableSpec = dedup.foldl(List(1, 2, 3, 3)) must_== List(1, 2, 3) 92 | 93 | def avgSpec = { 94 | val avgFold = sum[Double] / foldLength[Double, Double] 95 | avgFold.foldl(Seq(1.0, 2.0, 3.0, 4.0, 5.0)) must_== 3.0 96 | } 97 | 98 | def syntaxSpec = { 99 | Seq(1, 2, 3).foldWith(foldLength[Int, Int]) must_== 3 100 | 101 | def avg = sum[Double] / foldLength[Double, Double] 102 | Seq(1.0, 2.0, 3.0) foldWith avg must_== 2 103 | 104 | } 105 | 106 | def containsSpec = { 107 | contains(1).foldl(Seq(1, 2, 3)) must_== true 108 | contains(4).foldl(Seq(1, 2, 3)) must_== false 109 | contains(4).foldl(Seq[Int]()) must_== false 110 | } 111 | 112 | def doesNotContainSpec = { 113 | doesNotContain(1).foldl(Seq(1, 2, 3)) must_== false 114 | doesNotContain(4).foldl(Seq(1, 2, 3)) must_== true 115 | doesNotContain(4).foldl(Seq[Int]()) must_== true 116 | } 117 | 118 | def cProp = doesNotContain(-1331431553).foldl(Seq[Int]()) must_== ! (contains(-1331431553).foldl(Seq[Int]())) 119 | 120 | def foldMapSpec = { 121 | val x = foldMap((x: Int) => x.toString)((x: String) => x.length()) 122 | x.foldl(Seq(10, 20, 30, 40)) must_== 8 123 | } 124 | 125 | def maximumBySpec = { 126 | maximumBy((x: String) => x.toInt).foldl(Seq("1", "2", "3")) must_== Some("3") 127 | maximumBy((x: String) => x.toInt).foldl(Seq[String]()) must_== None 128 | } 129 | 130 | def minimumBySpec = { 131 | minimumBy((x: String) => x.toInt).foldl(Seq("1", "2", "3")) must_== Some("1") 132 | minimumBy((x: String) => x.toInt).foldl(Seq[String]()) must_== None 133 | } 134 | 135 | def sumScanSpec = { 136 | Seq(1, 2, 3).scanWith(sum[Int]) must_== Seq(0, 1, 3, 6) 137 | } 138 | 139 | def takeWhileSpec = { 140 | Seq(1, 2, 3).foldWith(takeWhile[Int](x => x >= 2)) must_== Seq(1, 2) 141 | Seq[Int]().foldWith(takeWhile[Int](x => x >= 2)) must_== Seq() 142 | Seq(1, 2, 3, 4, 5).foldWith(takeWhile[Int](x => x >= 5)) must_== Seq(1, 2, 3, 4, 5) 143 | } 144 | 145 | def dropWhileSpec = { 146 | Seq(1, 2, 3).foldWith(dropWhile[Int](x => x == 2)) must_== Seq(3) 147 | Seq[Int]().foldWith(dropWhile[Int](x => x >= 2)) must_== Seq() 148 | Seq(1, 2, 3, 4, 5).foldWith(dropWhile[Int](x => x <= 3)) must_== Seq(4, 5) 149 | Seq(1, 2, 3, 4, 5).foldWith(dropWhile[Int](x => x < 0)) must_== Seq(1, 2, 3, 4, 5) 150 | Seq(1, 2, 1, 3, 4, 5).foldWith(dropWhile[Int](x => x < 2)) must_== Seq(2, 1, 3, 4, 5) 151 | Seq(1, 2, 1, 3, 4, 5).foldWith(dropWhile[Int](x => x < 0)) must_== Seq(1, 2, 1, 3, 4, 5) 152 | } 153 | 154 | def takeSpec = { 155 | Seq(1, 2, 3).foldWith(take[Int](1)) must_== Seq(1) 156 | Seq(1, 2, 3).foldWith(take[Int](0)) must_== Seq[Int]() 157 | Seq(1, 2, 3).foldWith(take[Int](-1)) must_== Seq[Int]() 158 | Seq(1, 2, 3).foldWith(take[Int](10)) must_== Seq(1, 2, 3) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/main/scala/Foldl.scala: -------------------------------------------------------------------------------- 1 | package fold 2 | 3 | import CanFold._ 4 | import scala.math.{Ordering => DefaultOrdering} 5 | import scalaz._ 6 | 7 | sealed class Foldl[B, A](val step: B => Foldl[B, A], val done: Unit => A) { 8 | 9 | def foldl[G[_]](xs: G[B])(implicit foldable: CanFold[G, B]): A = 10 | foldable.fold(xs)(this) 11 | 12 | def of[G[_]](xs: G[B])(implicit foldable: CanFold[G, B]): A = foldl(xs) 13 | 14 | def scanl[G[_]](xs: G[B])(implicit foldable: CanFold[G, B]): Seq[A] = 15 | foldable.scan(xs)(this) 16 | 17 | def extract: A = done(()) 18 | 19 | def <*>[C](other: Foldl[B, A => C]): Foldl[B, C] = ap(other) 20 | 21 | def ap[C](other: Foldl[B, A => C]): Foldl[B, C] = 22 | Foldl (b => step(b) <*> other.step(b), unit => other.done(unit)(done(unit))) 23 | 24 | def map[C](f: A => C): Foldl[B, C] = Foldl((b: B) => step(b) map f, f compose done) 25 | 26 | def map2[C, D](other: Foldl[B, C])(f: (A, C) => D): Foldl[B, D] = other <*> map(f.curried) 27 | 28 | def duplicate: Foldl[B, Foldl[B, A]] = map(Function.const(this)) 29 | 30 | def dimap[C, D](f: C => B, g: A => D): Foldl[C, D] = 31 | Foldl[C, D]((c: C) => step(f(c)).dimap(f, g), g compose done) 32 | 33 | def lmap[C](f: C => B): Foldl[C, A] = dimap(f, identity) 34 | 35 | def rmap[D](g: A => D): Foldl[B, D] = dimap(identity, g) 36 | 37 | } 38 | 39 | object Foldl extends FoldlFunctions with FoldlInstances { 40 | 41 | def create[S, B, A](istep: S => B => S, init: S, done: S => A): Foldl[B, A] = { 42 | def construct(init1: S): Foldl[B, A] = new Foldl(b => construct(istep(init1)(b)), (x => done(init1))) 43 | construct(init) 44 | } 45 | 46 | def apply[B, A](istep: A => B => A, init: A): Foldl[B, A] = 47 | create(istep, init, (identity: A => A)) 48 | 49 | def apply[S, B, A](istep: (S, B) => S, init: S, done: S => A): Foldl[B, A] = 50 | create(istep.curried, init, done) 51 | 52 | def createWith[B, A](init: A)(istep: (A, B) => A): Foldl[B, A] = 53 | create(istep.curried, init, (identity : A => A)) 54 | 55 | def apply[B, A](s: B => Foldl[B, A], done: Unit => A): Foldl[B, A] = 56 | new Foldl(s, done) 57 | 58 | def pure[B, A](a: A): Foldl[B, A] = { 59 | def con: Foldl[B, A] = Foldl(_ => con, unit => a) 60 | con 61 | } 62 | } 63 | 64 | trait FoldlFunctions { 65 | import Foldl._ 66 | 67 | def helperFold[A](f: (A, A) => A): Foldl[A, Option[A]] = 68 | createWith[A, Option[A]](None)((acc: Option[A], a: A) => acc.map(x => f(x, a)).orElse(Some(a))) 69 | 70 | def length[B, A](implicit a: Numeric[A]): Foldl[B, A] = 71 | createWith(a.zero)((x: A, _: B) => a.plus(x, a.one)) 72 | 73 | def length[B]: Foldl[B, Int] = 74 | createWith(0)((x: Int, _: B) => x + 1) 75 | 76 | def sum[B](implicit N: Numeric[B]): Foldl[B, B] = 77 | createWith(N.zero)((x: B, y: B) => N.plus(x, y)) 78 | 79 | def product[B](implicit N: Numeric[B]): Foldl[B, B] = 80 | createWith(N.one)((x: B, y: B) => N.times(x, y)) 81 | 82 | def isEmpty[B]: Foldl[B, Boolean] = 83 | createWith(true)((_: Boolean, _: B) => false) 84 | 85 | def head[A]: Foldl[A, Option[A]] = 86 | createWith[A, Option[A]](None)((acc: Option[A], e: A) => acc orElse Some(e)) 87 | 88 | def any[A](p: A => Boolean): Foldl[A, Boolean] = 89 | createWith(false)((acc: Boolean, e: A) => acc || p(e)) 90 | 91 | def all[A](p: A => Boolean): Foldl[A, Boolean] = 92 | createWith(true)((acc: Boolean, e: A) => acc && p(e)) 93 | 94 | def forall[A](p: A => Boolean): Foldl[A, Boolean] = all(p) 95 | 96 | def forany[A](p: A => Boolean): Foldl[A, Boolean] = any(p) 97 | 98 | def and: Foldl[Boolean, Boolean] = createWith(true)(_ && _) 99 | 100 | def or: Foldl[Boolean, Boolean] = createWith(false)(_ || _) 101 | 102 | def take[B](i: Int): Foldl[B, Seq[B]] = 103 | createWith((Seq[B](), 0)) { (x: (Seq[B], Int), e: B) => 104 | x match { 105 | case (xs, ci) => if(ci <= i) (xs :+ e, ci + 1) else (xs, ci + 1) 106 | } 107 | } map (_._1) 108 | 109 | def takeWhile[B](p: B => Boolean): Foldl[B, Seq[B]] = 110 | createWith((Seq[B](), false)) {(x: (Seq[B], Boolean), e: B) => 111 | x match { 112 | case (xs, false) => (xs :+ e, p(e)) 113 | case (xs, true) => (xs, true) 114 | } 115 | } map (_._1) 116 | 117 | def dropWhile[B](p: B => Boolean): Foldl[B, Seq[B]] = { 118 | createWith((false, Seq[B]())){(acc: (Boolean, Seq[B]), e: B) => 119 | acc match { 120 | case (true, xs) => (true, xs :+ e) 121 | case (false, xs) => if(p(e)) (false, xs) else (true, xs :+ e) 122 | } 123 | 124 | } map (_._2) 125 | } 126 | 127 | def maximum[A: DefaultOrdering]: Foldl[A, Option[A]] = 128 | helperFold(implicitly[DefaultOrdering[A]].max _) 129 | 130 | def minimum[A: DefaultOrdering]: Foldl[A, Option[A]] = 131 | helperFold(implicitly[DefaultOrdering[A]].min _) 132 | 133 | def maximumBy[B, A: DefaultOrdering](f: B => A): Foldl[B, Option[B]] = 134 | helperFold {(b1: B, b2: B) => 135 | implicitly[DefaultOrdering[A]].compare(f(b1), f(b2)) match { 136 | case x if x < 0 => b2 137 | case _ => b1 138 | } 139 | } 140 | 141 | def minimumBy[B, A: DefaultOrdering](f: B => A): Foldl[B, Option[B]] = 142 | helperFold((b1: B, b2: B) => 143 | implicitly[DefaultOrdering[A]].compare(f(b1), f(b2)) match { 144 | case x if x < 0 => b1 145 | case _ => b2 146 | } 147 | ) 148 | 149 | def last[A]: Foldl[A, Option[A]] = helperFold((_: A, y: A) => y) 150 | 151 | def lastOrElse[A](a: A): Foldl[A, A] = createWith(a)((_: A, e: A) => e) 152 | 153 | def reverse[A]: Foldl[A, List[A]] = createWith(Nil: List[A])((x: List[A], y:A) => y :: x) 154 | 155 | def contains[A](e: A): Foldl[A, Boolean] = any(_.equals(e)) 156 | 157 | def doesNotContain[A](e: A): Foldl[A, Boolean] = all(!_.equals(e)) 158 | 159 | def dedup[A]: Foldl[A, List[A]] = { 160 | val es = Set[A]() 161 | createWith((es, identity: List[A] => List[A]))( 162 | (tup: (Set[A], List[A] => List[A]), y:A) => tup match { 163 | case (set: Set[A], f: (List[A] => List[A])) => 164 | if(set.contains(y)) 165 | (set, f) 166 | else (set + y, f compose (y :: _)) 167 | }) map { case (_, f: (List[A] => List[A])) => f(List()) } 168 | } 169 | 170 | def foldMap[B, A, M: Monoid](f: B => M)(g: M => A): Foldl[B, A] = { 171 | lazy val i = implicitly[Monoid[M]] 172 | createWith(i.zero)((x: M, e: B) => i.append(x, f(e))) map g 173 | } 174 | 175 | def withMonoid[B: Monoid, A](f: B => A): Foldl[B, A] = 176 | foldMap(identity: B => B)(f) 177 | 178 | def mconcat[B: Monoid, A](f: B => A): Foldl[B, A] = 179 | foldMap(identity: B => B)(f) 180 | 181 | def withMonoid_[B: Monoid]: Foldl[B, B] = 182 | foldMap(identity: B => B)(identity: B => B) 183 | 184 | } 185 | 186 | trait FoldlInstances { 187 | 188 | implicit class FoldlNumeric[B, A: Fractional](x: Foldl[B, A]){ 189 | def -(y: fold.Foldl[B,A]): fold.Foldl[B,A] = 190 | x.map2(y)(implicitly[Numeric[A]].minus) 191 | 192 | def negate: fold.Foldl[B,A] = 193 | x.map(implicitly[Numeric[A]].negate) 194 | 195 | def +(y: fold.Foldl[B,A]): fold.Foldl[B,A] = 196 | x.map2(y)(implicitly[Numeric[A]].plus) 197 | 198 | def *(y: fold.Foldl[B,A]): fold.Foldl[B,A] = 199 | x.map2(y)(implicitly[Numeric[A]].times) 200 | 201 | def /(y: fold.Foldl[B,A]): fold.Foldl[B,A] = 202 | x.map2(y)(implicitly[Fractional[A]].div) 203 | 204 | def compare(y: fold.Foldl[B,A]): Int = 205 | x.map2(y)(implicitly[Numeric[A]].compare).extract 206 | } 207 | 208 | implicit def FoldFunctorApplyApplicative[B] = 209 | new Applicative[({type f[a] = Foldl[B, a]})#f] with Apply[({type f[a] = Foldl[B, a]})#f] with Functor[({type f[a] = Foldl[B, a]})#f] { 210 | override def map[A, C](a: Foldl[B, A])(f: A => C) = 211 | a map f 212 | 213 | def ap[A, C](a: => Foldl[B, A])(f: => Foldl[B, A => C]): Foldl[B, C] = 214 | a ap f 215 | 216 | def point[A](a: => A): Foldl[B, A] = 217 | Foldl.pure(a) 218 | } 219 | 220 | implicit val FoldProfunctor = 221 | new Profunctor[Foldl] { 222 | override def mapfst[B, A, C](fab: fold.Foldl[B, A])(f: C => B): fold.Foldl[C, A] = 223 | fab lmap f 224 | 225 | override def mapsnd[B, A, C](fab: fold.Foldl[B, A])(f: A => C): fold.Foldl[B, C] = 226 | fab rmap f 227 | } 228 | 229 | implicit def FoldlMonoid[B, A: Monoid] = 230 | new Monoid[Foldl[B, A]]{ 231 | def zero = 232 | Applicative[({type f[a] = Foldl[B, a]})#f].point(implicitly[Monoid[A]].zero) 233 | 234 | def append(a: Foldl[B, A], b: => Foldl[B, A]) = 235 | Applicative[({type f[a] = Foldl[B, a]})#f].apply2(a, b)(implicitly[Monoid[A]].append(_, _)) 236 | } 237 | 238 | implicit def FoldComonad[B] = 239 | new Comonad[({type f[a] = Foldl[B, a]})#f] { 240 | def cobind[A, C](fa: Foldl[B,A])(f: Foldl[B,A] => C): fold.Foldl[B, C] = 241 | Foldl((b: B) => fa.step(b).duplicate, (unit: Unit) => fa).map(f) 242 | 243 | def copoint[A](p: fold.Foldl[B,A]): A = 244 | p.extract 245 | 246 | def map[A, C](fa: fold.Foldl[B,A])(f: A => C): fold.Foldl[B,C] = 247 | fa map f 248 | 249 | } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. --------------------------------------------------------------------------------