diff --git a/.scalafmt.conf b/.scalafmt.conf index 8152ba73..d8ccdb58 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,3 +1,4 @@ +version = "2.6.4" style = defaultWithAlign maxColumn = 80 diff --git a/README.md b/README.md index 68644112..ca5c9373 100644 --- a/README.md +++ b/README.md @@ -42,20 +42,16 @@ val fib: BigDecimal => BigDecimal = scheme.ghylo( ``` ```scala -scala> fib(0) -res0: BigDecimal = 0 - -scala> fib(1) -res1: BigDecimal = 1 - -scala> fib(2) -res2: BigDecimal = 1 - -scala> fib(10) -res3: BigDecimal = 55 - -scala> fib(100) -res4: BigDecimal = 354224848179261915075 +fib(0) +// res0: BigDecimal = 0 +fib(1) +// res1: BigDecimal = 1 +fib(2) +// res2: BigDecimal = 1 +fib(10) +// res3: BigDecimal = 55 +fib(100) +// res4: BigDecimal = 354224848179261915075 ``` An anamorphism followed by a histomorphism is also known as a @@ -69,20 +65,16 @@ val fibAlt: BigDecimal => BigDecimal = ``` ```scala -scala> fibAlt(0) -res5: BigDecimal = 0 - -scala> fibAlt(1) -res6: BigDecimal = 1 - -scala> fibAlt(2) -res7: BigDecimal = 1 - -scala> fibAlt(10) -res8: BigDecimal = 55 - -scala> fibAlt(100) -res9: BigDecimal = 354224848179261915075 +fibAlt(0) +// res5: BigDecimal = 0 +fibAlt(1) +// res6: BigDecimal = 1 +fibAlt(2) +// res7: BigDecimal = 1 +fibAlt(10) +// res8: BigDecimal = 55 +fibAlt(100) +// res9: BigDecimal = 354224848179261915075 ``` What if we want to do two things at once? Let's calculate a @@ -106,20 +98,16 @@ val sumSquares: BigDecimal => BigDecimal = scheme.ghylo( ``` ```scala -scala> sumSquares(0) -res11: BigDecimal = 0 - -scala> sumSquares(1) -res12: BigDecimal = 1 - -scala> sumSquares(2) -res13: BigDecimal = 5 - -scala> sumSquares(10) -res14: BigDecimal = 385 - -scala> sumSquares(100) -res15: BigDecimal = 338350 +sumSquares(0) +// res10: BigDecimal = 0 +sumSquares(1) +// res11: BigDecimal = 1 +sumSquares(2) +// res12: BigDecimal = 5 +sumSquares(10) +// res13: BigDecimal = 385 +sumSquares(100) +// res14: BigDecimal = 338350 ``` Now we can zip the two algebras into one so that we calculate @@ -134,20 +122,16 @@ val fused: BigDecimal => (BigDecimal, BigDecimal) = ``` ```scala -scala> fused(0) -res16: (BigDecimal, BigDecimal) = (0,0) - -scala> fused(1) -res17: (BigDecimal, BigDecimal) = (1,1) - -scala> fused(2) -res18: (BigDecimal, BigDecimal) = (1,5) - -scala> fused(10) -res19: (BigDecimal, BigDecimal) = (55,385) - -scala> fused(100) -res20: (BigDecimal, BigDecimal) = (354224848179261915075,338350) +fused(0) +// res15: (BigDecimal, BigDecimal) = (0, 0) +fused(1) +// res16: (BigDecimal, BigDecimal) = (1, 1) +fused(2) +// res17: (BigDecimal, BigDecimal) = (1, 5) +fused(10) +// res18: (BigDecimal, BigDecimal) = (55, 385) +fused(100) +// res19: (BigDecimal, BigDecimal) = (354224848179261915075, 338350) ``` Droste includes [athema](athema), a math expression parser/processor, diff --git a/build.sbt b/build.sbt index 80bbadf6..6fe71acc 100644 --- a/build.sbt +++ b/build.sbt @@ -170,35 +170,35 @@ lazy val athemaJVM = athema.jvm lazy val athemaJS = athema.js lazy val readme = (project in file("modules/readme")) - .enablePlugins(TutPlugin) + .enablePlugins(MdocPlugin) .dependsOn(coreJVM) .dependsOn(athemaJVM) .settings(noPublishSettings) .disablePlugins(MimaPlugin) .settings( - scalacOptions in Tut ~= { - _.filterNot( - Set("-Ywarn-unused-import", "-Yno-predef", "-Ywarn-unused:imports")) - }, - tutTargetDirectory := (baseDirectory in LocalRootProject).value + mdocExtraArguments := Seq( + "--in", baseDirectory.value + "/src/main/tut/README.md", + "--out", (baseDirectory in LocalRootProject).value + "/README.md" + ) ) /////////////// //// DOCS //// /////////////// -lazy val docs = (project in file("docs")) +lazy val docs = (project in file("microsite")) .dependsOn(coreJVM) + .dependsOn(macrosJVM) .dependsOn(athemaJVM) - .settings(moduleName := "droste-docs") + .settings(moduleName := "droste-microsite") .settings(micrositeSettings: _*) .settings(noPublishSettings: _*) + .settings(libraryDependencies ++= paradiseDep(scalaVersion.value)) + .settings(micrositeCompilingDocsTool := WithMdoc) .enablePlugins(MicrositesPlugin) .disablePlugins(ProjectPlugin) .disablePlugins(MimaPlugin) - .settings( - scalacOptions in Tut ~= (_ filterNot Set("-Ywarn-unused-import", "-Xlint").contains) - ) + .settings(mdocIn := tutSourceDirectory.value) ////////////////// //// ALIASES ///// @@ -208,4 +208,4 @@ addCommandAlias( "ci-test", ";+clean;+test" ) -addCommandAlias("ci-docs", ";readme/tut") +addCommandAlias("ci-docs", "github; docs/mdoc; readme/mdoc") diff --git a/docs/src/main/resources/microsite/data/menu.yml b/docs/src/main/resources/microsite/data/menu.yml index 32fd2012..9bdcf6b1 100644 --- a/docs/src/main/resources/microsite/data/menu.yml +++ b/docs/src/main/resources/microsite/data/menu.yml @@ -1,3 +1,15 @@ options: - title: Quick Start - url: docs/ \ No newline at end of file + url: docs/ + + - title: Typeclasses + url: docs/core/typeclasses + + - title: Fixpoint types + url: docs/core/fixpoint-types + + - title: Recursion schemes + url: docs/core/recursion-schemes + + - title: Annotations + url: docs/core/annotations diff --git a/docs/src/main/tut/docs/core/README.md b/docs/src/main/tut/docs/core/README.md index 609109fa..fda1ec33 100644 --- a/docs/src/main/tut/docs/core/README.md +++ b/docs/src/main/tut/docs/core/README.md @@ -6,6 +6,6 @@ permalink: /docs/core/ # Core -* [Data Structures](dataStructures/) -* [Type Classes](typeClasses/) -* [Recursion Schemes](recursionSchemes/) +* [Fixpoint types](fixpoint-types/) +* [Typeclasses](typeclasses/) +* [Recursion Schemes](recursion-schemes/) diff --git a/docs/src/main/tut/docs/core/annotations/README.md b/docs/src/main/tut/docs/core/annotations/README.md new file mode 100644 index 00000000..f6931294 --- /dev/null +++ b/docs/src/main/tut/docs/core/annotations/README.md @@ -0,0 +1,79 @@ +--- +layout: docs +title: annotations +permalink: /docs/core/annotations/ +--- + +# annotations + +Droste provides a couple of very useful annotations in the +`droste-macros` module. + +## @deriveFixedPoint + +`@deriveFixedPoint` can be used in a plain old recursive ADT to +generate the non-recursive counterpart. + +There are some restrictions in order to use `@deriveFixedPoint`: +- the annotee should be either a `sealed trait` or `sealed abstract class` +- cases of the ADT should be declared inside the companion object +- recursion should appear on positive postion, somethink like follows will not compile: + +```scala mdoc:fail +import higherkindness.droste.macros.deriveFixedPoint + +@deriveFixedPoint sealed trait X +object X{ + // here, X appears in negative postition + case class Y[A](f: X => Int) extends X +} +``` + + +This annotation will create a `object fixedpoint` within the companion +object of the annotated `sealed trait` or `sealed abstract class` with +the following: + +- Generate the non-recursive ADT +- create a `Traverse` instance for it +- create a Recursive => NonRecursive[A] coalgebra +- create a NonRecursive[A] => Recursive algebra +- create a `Basis` instance + +```scala mdoc +import cats.instances.list._ + +import higherkindness.droste.Basis +import higherkindness.droste.syntax.all._ +import higherkindness.droste.macros.deriveFixedPoint + +@deriveFixedPoint sealed trait Expr +object Expr { + case class Val(i: Int) extends Expr + case class Sum(a: Expr, b: Expr) extends Expr + + def `val`[A](i: Int): fixedpoint.ExprF[A] = fixedpoint.ValF(i) + def sum[A](a: A, b: A): fixedpoint.ExprF[A] = fixedpoint.SumF(a, b) +} + +import Expr._ +import Expr.fixedpoint._ + +def program[T: Basis[ExprF, ?]]: T = + sum[T]( + sum[T]( + `val`[T](1).embed, + `val`[T](2).embed + ).embed, + sum[T]( + `val`[T](3).embed, + `val`[T](4).embed + ).embed + ).embed + +val numbersToBeAdded = program[Expr].collect[List[Int], Int] { + case Val(x) => x +} + +println(numbersToBeAdded) +``` diff --git a/docs/src/main/tut/docs/core/dataStructures/README.md b/docs/src/main/tut/docs/core/dataStructures/README.md deleted file mode 100644 index 303edf52..00000000 --- a/docs/src/main/tut/docs/core/dataStructures/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: docs -title: Data Structures -permalink: /docs/core/dataStructures/ ---- - -# Data Structures diff --git a/docs/src/main/tut/docs/core/fixpoint-types/README.md b/docs/src/main/tut/docs/core/fixpoint-types/README.md new file mode 100644 index 00000000..605bb096 --- /dev/null +++ b/docs/src/main/tut/docs/core/fixpoint-types/README.md @@ -0,0 +1,99 @@ +--- +layout: docs +title: fixpoint types +permalink: /docs/core/fixpoint-types/ +--- + +# Fixpoint types + +The basic idea of recursion schemes is that we factor the recursion +out of recursive data types. We do so by converting recursive data +types: + +```scala mdoc +sealed trait Expr +case class Sum(a: Expr, b: Expr) extends Expr +case class Val(a: Int) extends Expr +``` + +into non-recursive data types, in which the recursion is factored to a +type parameter. + +```scala mdoc +sealed trait ExprF[A] +case class SumF[A](a: A, b: A) extends ExprF[A] +case class ValF[A](a: Int) extends ExprF[A] +``` + +As you can see, we replace occurences of explicit recursion with the +type parameter `A`. + +However, how do we create values using our new ADT? Imagine we want +to represent the `1 + 2` expression. In the first version of +`Expr`, it's easy: + +```scala mdoc +val sum: Expr = Sum(Val(1), Val(2)) +``` + +However, using the second type is not as easy, if we try to follow the +same approach, we see that something doesn't work: + +```scala mdoc +val sumF: ExprF[ExprF[Int]] = SumF(ValF(1), ValF(2)) +``` + +What's the type of `sumF` now? If we'd start substituting we'd get +something like `ExprF[ExprF[...]]`, and that's not what we want. + +Introducing fixpoint types. + +Fixpoint types are the missing piece in the previous approach to +datatypes, they tie the recursive knot in order to avoid scenarios +such as `ExprF[ExprF[ExprF[...]]]`. + +## Fix + +Fix is the simplest of fixpoint data types, and it's declaration is as +follows: + +```scala +case class Fix[F[_]](unFix: F[Fix[F]]) +``` + +Even though it may look extrange, the idea is quite simple. Let's get +back to the previous example. Using `Fix` we can now define our value +sumF as follows: + +```scala +val sumF: Fix[ExprF] = Fix(SumF(Fix(ValF(1)), Fix(ValF(2)))) +``` + +## Mu + +`Mu` is a fixpoint datatype that is declared as the fold (`cata`) of a +datastructure. + +## Nu + +`Nu` is a fixpoint type declared as the unfold (`ana`) of a datastructure. + +## Coattr + +`Coattr`, also known as `Free`, is a fixpoint type in which the leaves +of the tree are annotated with an additional value. + +## Attr + +`Attr`, also known as `Cofree`, is a fixpoint type in which al levels +of the tree are annotated with an additional value. Using `Attr` is +very useful when you need to add an annotation, or attribute, to the +values in your pattern functor. + +`Attr` can be used for: +* [annotating an AST with types](https://brianmckenna.org/blog/type_annotation_cofree) +* [annotating an AST with positions in the source file](https://github.com/haskell-nix/hnix/blob/master/src/Nix/Expr/Types/Annotated.hs). + +# Pattern Functors + +Pattern functors are the diff --git a/docs/src/main/tut/docs/core/recursion-schemes/README.md b/docs/src/main/tut/docs/core/recursion-schemes/README.md new file mode 100644 index 00000000..5de18c9e --- /dev/null +++ b/docs/src/main/tut/docs/core/recursion-schemes/README.md @@ -0,0 +1,43 @@ +--- +layout: docs +title: Recursion Schemes +permalink: /docs/core/recursion-schemes/ +--- + +# Recursion Schemes + +## Basics + +### Folds + +Folds are used to consume data structures + +### Unfolds + +Unfolds are used to produce data structures + +### Refolds + +Refolds go one way and then the other. + +## monadic recursion schemes + +Monadic recursion schemes are monadic version of already known +recursion schemes. With monadic version we refer to recursion schemes +in which the return values is wrapped inside a monad: + +```scala +def cataM[M[_]: Monad, F[_]: Traverse, R, B]( + algebraM: AlgebraM[M, F, B] +)(implicit project: Project[F, R]): R => M[B] +``` + +They are marked with an `M` suffix in their name, +and the big difference is that they require a `Traverse` instance in +the pattern functor. Some examples are `cataM`, `anaM`, or `histoM`. + +Monadic recursion schemes are useful when we need to + +## Generalized + +## gather / scatter diff --git a/docs/src/main/tut/docs/core/recursionSchemes/README.md b/docs/src/main/tut/docs/core/recursionSchemes/README.md deleted file mode 100644 index 8ce8dc08..00000000 --- a/docs/src/main/tut/docs/core/recursionSchemes/README.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -layout: docs -title: Recursion Schemes -permalink: /docs/core/recursionSchemes/ ---- - -# Recursion Schemes - diff --git a/docs/src/main/tut/docs/core/typeClasses/README.md b/docs/src/main/tut/docs/core/typeClasses/README.md deleted file mode 100644 index b343f574..00000000 --- a/docs/src/main/tut/docs/core/typeClasses/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: docs -title: Type Classes -permalink: /docs/core/typeClasses/ ---- - -# Type Classes diff --git a/docs/src/main/tut/docs/core/typeclasses/README.md b/docs/src/main/tut/docs/core/typeclasses/README.md new file mode 100644 index 00000000..faf07c36 --- /dev/null +++ b/docs/src/main/tut/docs/core/typeclasses/README.md @@ -0,0 +1,37 @@ +--- +layout: docs +title: Type Classes +permalink: /docs/core/typeclasses/ +--- + +# Type Classes + +There are two main typeclasses in droste, `Project[F[_]]` and +`Embed[F[_]]` . + + +## Embed + +All [fixpoint types](/docs/core/fixpoint-types/) that allow _lifting_ a pattern functor inside them +have an instance of `Embed`. `Embed` is declared as follows: + +```scala +trait Embed[F[_], R] { + def algebra: Algebra[F, R] +} +``` + +## Project + +All [fixpoint types](/docs/core/fixpoint-types) from which we can extract a pattern functor have +instances for `Project`. Project is declared as follows: + +```scala +trait Project[F[_], R] { + def coalgebra: Coalgebra[F, R] +} +``` + +## Basis + +Basis is a typeclass for types that are both an `Embed` and `Project`. diff --git a/modules/readme/src/main/tut/README.md b/modules/readme/src/main/tut/README.md index 8535bdf2..6c195e78 100644 --- a/modules/readme/src/main/tut/README.md +++ b/modules/readme/src/main/tut/README.md @@ -22,7 +22,7 @@ Fibonacci values can be done with a histomorphism if we model natural numbers as a chain of `Option`. We can easily unfold with an anamorphism and then fold to our result with a histomorphism. -```tut:silent +```scala mdoc:silent import higherkindness.droste._ import higherkindness.droste.data._ import cats.implicits._ @@ -41,7 +41,7 @@ val fib: BigDecimal => BigDecimal = scheme.ghylo( natCoalgebra.scatter(Scatter.ana)) ``` -```tut +```scala mdoc fib(0) fib(1) fib(2) @@ -53,13 +53,13 @@ An anamorphism followed by a histomorphism is also known as a dynamorphism. Recursion scheme animals like dyna are available in the zoo: -```tut:silent +```scala mdoc:silent val fibAlt: BigDecimal => BigDecimal = scheme.zoo.dyna(fibAlgebra, natCoalgebra) ``` -```tut +```scala mdoc fibAlt(0) fibAlt(1) fibAlt(2) @@ -70,7 +70,7 @@ fibAlt(100) What if we want to do two things at once? Let's calculate a Fibonacci value and the sum of all squares. -```tut:silent +```scala mdoc:silent val fromNatAlgebra: Algebra[Option, BigDecimal] = Algebra { case Some(n) => n + 1 case None => 0 @@ -87,7 +87,7 @@ val sumSquares: BigDecimal => BigDecimal = scheme.ghylo( natCoalgebra.scatter(Scatter.ana)) ``` -```tut +```scala mdoc sumSquares(0) sumSquares(1) sumSquares(2) @@ -98,7 +98,7 @@ sumSquares(100) Now we can zip the two algebras into one so that we calculate both results in one pass. -```tut:silent +```scala mdoc:silent val fused: BigDecimal => (BigDecimal, BigDecimal) = scheme.ghylo( fibAlgebra.gather(Gather.histo) zip @@ -106,7 +106,7 @@ val fused: BigDecimal => (BigDecimal, BigDecimal) = natCoalgebra.scatter(Scatter.ana)) ``` -```tut +```scala mdoc fused(0) fused(1) fused(2) diff --git a/project/ProjectPlugin.scala b/project/ProjectPlugin.scala index d6261a80..62298e73 100644 --- a/project/ProjectPlugin.scala +++ b/project/ProjectPlugin.scala @@ -7,7 +7,6 @@ import sbtcrossproject.CrossProject import sbtcrossproject.CrossPlugin.autoImport._ import scalajscrossproject.ScalaJSCrossPlugin.autoImport._ import scalanativecrossproject.ScalaNativeCrossPlugin.autoImport._ -import sbtrelease.ReleasePlugin.autoImport._ object ProjectPlugin extends AutoPlugin { @@ -84,38 +83,6 @@ object ProjectPlugin extends AutoPlugin { cancelable in Global := true, crossScalaVersions := List("2.12.10", "2.13.1"), scalaVersion := "2.12.10" - ) ++ publishSettings - - lazy val publishSettings = Seq( - releaseCrossBuild := true, - homepage := Some(url("https://github.com/andyscott/droste")), - licenses := Seq( - "Apache 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")), - publishMavenStyle := true, - publishArtifact in Test := false, - pomIncludeRepository := (_ => false), - publishTo := { - val nexus = "https://oss.sonatype.org/" - if (isSnapshot.value) - Some("snapshots" at nexus + "content/repositories/snapshots") - else - Some("releases" at nexus + "service/local/staging/deploy/maven2") - }, - autoAPIMappings := true, - scmInfo := Some( - ScmInfo( - url("https://github.com/andyscott/droste"), - "scm:git:git@github.com:andyscott/droste.git" - ) - ), - developers := List( - Developer( - "andyscott", - "Andy Scott", - "andy.g.scott@gmail.com", - url("https://twitter.com/andygscott") - ) ) - ) } diff --git a/project/plugins.sbt b/project/plugins.sbt index 5f98129d..26766fe5 100755 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,12 +3,15 @@ resolvers += Resolver.url( url("https://dl.bintray.com/typesafe/sbt-plugins") )(Resolver.ivyStylePatterns) -addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0") -addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.0.0") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.1.1") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.3.9") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1") -addSbtPlugin("io.crashbox" % "sbt-gpg" % "0.2.1") -addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.13") -addSbtPlugin("com.47deg" % "sbt-microsites" % "1.2.0") -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.7.0") +addSbtPlugin("com.47deg" % "sbt-microsites" % "1.2.0") +addSbtPlugin("com.alejandrohdezma" % "sbt-github" % "0.9.0") +addSbtPlugin("com.alejandrohdezma" % "sbt-github-mdoc" % "0.9.0") +addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.5") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.7.0") +addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.0.0") +addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.1.1") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.3.9") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.2.13") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.2") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1") diff --git a/scripts/ci-jobs.sh b/scripts/ci-jobs.sh index 017f82c2..833245f8 100755 --- a/scripts/ci-jobs.sh +++ b/scripts/ci-jobs.sh @@ -11,7 +11,7 @@ case "$1" in sbt ';+clean;+test' ;; "format") - ./scalafmt --test + sbt scalafmtAll ;; "coverage") sbt 'project coverage' test coverageReport