Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e5a9b1a
Showing
12 changed files
with
1,475 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
Dogs Copyright (c) 2015 Michael (stew) O'Connor. | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the "Software"), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
of the Software, and to permit persons to whom the Software is furnished to do | ||
so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. | ||
|
||
------ | ||
|
||
Code in Dogs is derived in part from Scalaz. The Scalaz license follows: | ||
|
||
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. All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions | ||
are met: | ||
1. Redistributions of source code must retain the above copyright | ||
notice, this list of conditions and the following disclaimer. | ||
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. | ||
3. The name of the author may not be used to endorse or promote products | ||
derived from this software without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
## Dogs | ||
|
||
This project is intended to be a companion to the | ||
[Cats project](https://github.com/non/cats). | ||
|
||
It intents to be a library containing data structures which facilitate | ||
pure functional programming in the Scala programming language. Some of | ||
these are replacements for structures already present in the Scala | ||
standard library, but with improvements in safety, some are data | ||
structures for which there is no analogue in the Scala standard | ||
library. | ||
|
||
### EXPERIMENTAL | ||
|
||
This project is in its infancy, it should be considered pre-alpha, it | ||
might never go anywhere, it could change drastically along the way if | ||
it does go somewhere. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
DList | ||
Zipper | ||
FingerTree | ||
Map |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
name := "dogs" | ||
|
||
scalaVersion := "2.11.2" | ||
|
||
scalacOptions := Seq( | ||
"-deprecation", | ||
"-encoding", "utf8", | ||
"-language:postfixOps", | ||
"-language:higherKinds", | ||
"-target:jvm-1.7", | ||
"-unchecked", | ||
"-Xcheckinit", | ||
"-Xfuture", | ||
"-Xlint", | ||
"-Xfatal-warnings", | ||
"-Yno-adapted-args", | ||
"-Ywarn-dead-code", | ||
"-Ywarn-value-discard") | ||
|
||
libraryDependencies ++= Seq( | ||
"org.spire-math" %% "cats" % "0.3.0-SNAPSHOT", | ||
"org.scalacheck" %% "scalacheck" % "1.12.5" % "test", | ||
compilerPlugin("org.spire-math" %% "kind-projector" % "0.6.3") | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
sbt.version=0.13.8 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
resolvers += Resolver.url( | ||
"tpolecat-sbt-plugin-releases", | ||
url("http://dl.bintray.com/content/tpolecat/sbt-plugin-releases"))( | ||
Resolver.ivyStylePatterns) | ||
|
||
addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.3.2") | ||
addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.0") | ||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") | ||
addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.3") | ||
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.8.1") | ||
addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.0") | ||
addSbtPlugin("pl.project13.scala"% "sbt-jmh" % "0.2.3") | ||
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.6.0") | ||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0") | ||
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.4") | ||
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.5") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,262 @@ | ||
package dogs | ||
|
||
import cats._ | ||
import cats.data._ | ||
|
||
/** | ||
* A Double-ended queue, based on the Bankers Double Ended Queue as | ||
* described by C. Okasaki in "Purely Functional Data Structures" | ||
* | ||
* A queue that allows items to be put onto either the front (cons) | ||
* or the back (snoc) of the queue in constant time, and constant | ||
* time access to the element at the very front or the very back of | ||
* the queue. Dequeueing an element from either end is constant time | ||
* when amortized over a number of dequeues. | ||
* | ||
* This queue maintains an invariant that whenever there are at least | ||
* two elements in the queue, neither the front list nor back list | ||
* are empty. In order to maintain this invariant, a dequeue from | ||
* either side which would leave that side empty constructs the | ||
* resulting queue by taking elements from the opposite side | ||
*/ | ||
sealed abstract class Dequeue[A] { | ||
import Maybe._ | ||
import IList._ | ||
import Dequeue._ | ||
|
||
def isEmpty: Boolean | ||
|
||
def frontMaybe: Maybe[A] | ||
def backMaybe: Maybe[A] | ||
|
||
/** | ||
* dequeue from the front of the queue | ||
*/ | ||
def uncons: Maybe[(A, Dequeue[A])] = this match { | ||
case EmptyDequeue() => Maybe.notThere | ||
case SingletonDequeue(a) => There((a, EmptyDequeue())) | ||
case FullDequeue(OneAnd(f, INil()), 1, OneAnd(single, nil), 1) => There((f, SingletonDequeue(single))) | ||
case FullDequeue(OneAnd(f, INil()), 1, OneAnd(x, ICons(xx, xs)), bs) => { | ||
val xsr = reverseNEL(OneAnd(xx, xs)) | ||
There((f, FullDequeue(xsr, bs-1, OneAnd(x, INil()), 1))) | ||
} | ||
case FullDequeue(OneAnd(f, ICons(ff, fs)), s, back, bs) => There((f, FullDequeue(OneAnd(ff, fs), s-1, back, bs))) | ||
} | ||
|
||
/** | ||
* dequeue from the back of the queue | ||
*/ | ||
def unsnoc: Maybe[(A, Dequeue[A])] = this match { | ||
case EmptyDequeue() => Maybe.notThere | ||
case SingletonDequeue(a) => There((a, EmptyDequeue())) | ||
case FullDequeue(OneAnd(single, INil()), 1, OneAnd(b, INil()), 1) => There((b, SingletonDequeue(single))) | ||
case FullDequeue(OneAnd(x, ICons(xx,xs)), fs, OneAnd(b, INil()), 1) => { | ||
val xsr = reverseNEL(OneAnd(xx, xs)) | ||
There((b, FullDequeue(OneAnd(x, INil()), 1, xsr, fs-1))) | ||
} | ||
|
||
case FullDequeue(front, fs, OneAnd(b, ICons(bb,bs)), s) => There((b, FullDequeue(front, fs, OneAnd(bb,bs), s-1))) | ||
} | ||
|
||
/** | ||
* enqueue to the front of the queue | ||
*/ | ||
def cons(a: A): Dequeue[A] = this match { | ||
case EmptyDequeue() => SingletonDequeue(a) | ||
case SingletonDequeue(single) => FullDequeue(OneAnd(a, INil()), 1, OneAnd(single, INil()), 1 ) | ||
case FullDequeue(front, fs, back, bs) => FullDequeue(OneAnd(a, ICons(front.head, front.tail)), fs+1, back, bs) | ||
} | ||
|
||
/** | ||
* enqueue on to the back of the queue | ||
*/ | ||
def snoc(a: A): Dequeue[A] = this match { | ||
case EmptyDequeue() => SingletonDequeue(a) | ||
case SingletonDequeue(single) => FullDequeue(OneAnd(single, INil[A]()), 1, OneAnd(a, INil[A]()), 1 ) | ||
case FullDequeue(front, fs, back, bs) => FullDequeue(front, fs, OneAnd(a, ICons(back.head, back.tail)), bs+1) | ||
} | ||
|
||
/** | ||
* alias for cons | ||
*/ | ||
def +:(a: A): Dequeue[A] = cons(a) | ||
|
||
/** | ||
* alias for snoc | ||
*/ | ||
def :+(a: A): Dequeue[A] = snoc(a) | ||
|
||
/** | ||
* convert this queue to a stream of elements from front to back | ||
*/ | ||
def toStreaming: Streaming[A] = streaming.unfold(this)(_.uncons) | ||
|
||
/** | ||
* convert this queue to a stream of elements from back to front | ||
*/ | ||
def toBackStream: Streaming[A] = streaming.unfold(this)(_.unsnoc) | ||
|
||
/** | ||
* convert this queue to a list of elements from front to back | ||
*/ | ||
def toIList: IList[A] = this match { | ||
case EmptyDequeue() => INil() | ||
case SingletonDequeue(a) => ICons(a, INil()) | ||
case FullDequeue(front, fs, back, bs) => front.head +: (front.tail ++ (back.tail reverse_::: ICons(back.head, INil()))) | ||
} | ||
|
||
/** | ||
* convert this queue to a list of elements from back to front | ||
*/ | ||
def toBackIList: IList[A] = this match { | ||
case EmptyDequeue() => INil() | ||
case SingletonDequeue(a) => ICons(a, INil()) | ||
case FullDequeue(front, fs, back, bs) => back.head +: (back.tail ++ (front.tail.reverse ++ (ICons(front.head, INil())))) | ||
} | ||
|
||
/** | ||
* Append another Deuque to this dequeue | ||
*/ | ||
def ++(other: Dequeue[A]): Dequeue[A] = this match { | ||
case EmptyDequeue() => other | ||
case SingletonDequeue(a) => a +: other | ||
case FullDequeue(f,fs,b,bs) => other match { | ||
case EmptyDequeue() => this | ||
case SingletonDequeue(a) => this :+ a | ||
case FullDequeue(of,ofs,ob,obs) => | ||
FullDequeue(OneAnd(f.head, (f.tail ++ | ||
((b.head +: b.tail) reverse_::: | ||
ICons(of.head, of.tail)))), | ||
fs + bs + ofs, | ||
ob, | ||
obs) | ||
} | ||
} | ||
|
||
def foldLeft[B](b: B)(f: (B,A) => B): B = this match { | ||
case EmptyDequeue() => b | ||
case SingletonDequeue(a) => f(b, a) | ||
case FullDequeue(front,_,back,_) => { | ||
val frontb = front.tail.foldLeft(f(b,front.head))(f) | ||
val backb = back.tail.foldRight(frontb)((a, b) => f(b,a)) | ||
f(backb,back.head) | ||
} | ||
} | ||
|
||
def foldRight[B](b: B)(f: (A,B) => B): B = this match { | ||
case EmptyDequeue() => b | ||
case SingletonDequeue(a) => f(a, b) | ||
case FullDequeue(front,_,back,_) => { | ||
val backb = back.tail.foldLeft(f(back.head, b))((b,a) => f(a,b)) | ||
val frontb = front.tail.foldRight(backb)(f) | ||
f(front.head, frontb) | ||
} | ||
} | ||
|
||
|
||
def map[B](f: A => B): Dequeue[B] = { | ||
this match { | ||
case EmptyDequeue() => EmptyDequeue() | ||
case SingletonDequeue(a) => SingletonDequeue(f(a)) | ||
case FullDequeue(front, fs, back, bs) => { | ||
val F = Functor[NEL] | ||
FullDequeue(F.map(front)(f), fs, F.map(back)(f), bs) | ||
} | ||
} | ||
} | ||
|
||
def size: Int = this match { | ||
case EmptyDequeue() => 0 | ||
case SingletonDequeue(_) => 1 | ||
case FullDequeue(_, fs, _, bs) => fs + bs | ||
} | ||
|
||
def reverse: Dequeue[A] = this match { | ||
case FullDequeue(front, fs, back, bs) => FullDequeue(back, bs, front, fs) | ||
case x => x | ||
} | ||
} | ||
|
||
object Dequeue extends DequeueInstances { | ||
type NEL[A] = OneAnd[A, IList] | ||
|
||
def apply[A](as: A*) = as.foldLeft[Dequeue[A]](empty)((q,a) ⇒ q :+ a) | ||
|
||
def fromFoldable[F[_],A](fa: F[A])(implicit F: Foldable[F]): Dequeue[A] = | ||
F.foldLeft[A,Dequeue[A]](fa,empty)((q,a) ⇒ q :+ a) | ||
|
||
def empty[A]: Dequeue[A] = EmptyDequeue() | ||
|
||
private def reverseNEL[A](fa: NEL[A]): NEL[A] = { | ||
@annotation.tailrec | ||
def loop(xs: IList[A], acc: IList[A]): NEL[A] = | ||
(xs: @unchecked) match { | ||
case ICons(h, INil()) => | ||
OneAnd(h, acc) | ||
case ICons(h, t) => | ||
loop(t, h :: acc) | ||
} | ||
loop(fa.head :: fa.tail, INil()) | ||
} | ||
} | ||
|
||
/** | ||
* special case of the queue when it contains just a single element | ||
* which can be accessed from either side of the queue | ||
*/ | ||
private[dogs] final case class SingletonDequeue[A](single: A) extends Dequeue[A] { | ||
override def isEmpty = false | ||
override def frontMaybe = Maybe.there(single) | ||
override def backMaybe = Maybe.there(single) | ||
} | ||
|
||
/** | ||
* a queue which has at least two elements, it is guaranteed that the | ||
* front list and back lists cannot be empty | ||
*/ | ||
private[dogs] final case class FullDequeue[A](front: OneAnd[A, IList], fsize: Int, back: OneAnd[A, IList], backSize: Int) extends Dequeue[A] { | ||
override def isEmpty = false | ||
override def frontMaybe = Maybe.there(front.head) | ||
override def backMaybe = Maybe.there(back.head) | ||
} | ||
/** | ||
* a queue which has no elements | ||
*/ | ||
private[dogs] final case object EmptyDequeue extends Dequeue[Nothing] { | ||
override val isEmpty = true | ||
override val frontMaybe = Maybe.notThere | ||
override val backMaybe = Maybe.notThere | ||
|
||
def apply[A]() = this.asInstanceOf[Dequeue[A]] | ||
def unapply[A](q: Dequeue[A]) = q.isEmpty | ||
} | ||
|
||
sealed abstract class DequeueInstances { | ||
implicit def equalDequeue[A](implicit A0: Eq[A]): Eq[Dequeue[A]] = | ||
new DequeueEqual[A] { | ||
val A = A0 | ||
} | ||
|
||
implicit def dequeueMonoid[A]: Monoid[Dequeue[A]] = new Monoid[Dequeue[A]] { | ||
def empty: Dequeue[A] = Dequeue.empty | ||
def combine(a: Dequeue[A], b :Dequeue[A]): Dequeue[A] = a ++ b | ||
} | ||
|
||
implicit val dequeueInstances: Foldable[Dequeue] with Functor[Dequeue] = new Foldable[Dequeue] with Functor[Dequeue] { | ||
override def foldRight[A,B](fa: Dequeue[A], b: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = fa.foldRight(b)((a,b) => f(a,b)) | ||
|
||
|
||
override def foldLeft[A,B](fa: Dequeue[A], b: B)(f: (B,A)=>B): B = fa.foldLeft(b)(f) | ||
override def foldMap[A,B](fa: Dequeue[A])(f: A => B)(implicit F: Monoid[B]): B = fa.foldLeft(F.empty)((b,a) => F.combine(b, f(a))) | ||
override def map[A,B](fa: Dequeue[A])(f: A => B): Dequeue[B] = fa map f | ||
} | ||
} | ||
|
||
private[dogs] trait DequeueEqual[A] extends Eq[Dequeue[A]] { | ||
implicit def A: Eq[A] | ||
import std.stream._ | ||
|
||
final override def eqv(a: Dequeue[A], b: Dequeue[A]): Boolean = | ||
Eq[Streaming[A]].eqv(a.toStreaming, b.toStreaming) | ||
} | ||
|
Oops, something went wrong.