Skip to content

Commit

Permalink
started on Bind
Browse files Browse the repository at this point in the history
  • Loading branch information
fommil committed Sep 9, 2017
1 parent 27e9570 commit beec7b9
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/
28 changes: 28 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// this file exists only so I can have quick access to a REPL
// with the correct dependencies.

scalaVersion in ThisBuild := "2.12.3"
scalacOptions in ThisBuild ++= Seq(
"-language:_",
"-Ypartial-unification",
"-Xfatal-warnings"
)

libraryDependencies ++= Seq(
"com.github.mpilquist" %% "simulacrum" % "0.11.0",
"com.chuusai" %% "shapeless" % "2.3.2" ,
"com.fommil" %% "stalactite" % "0.0.4" ,
"org.scalaz" %% "scalaz-core" % "7.2.15"
)

addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.4")
addCompilerPlugin(
"org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full
)

scalacOptions in (Compile, console) -= "-Xfatal-warnings"
initialCommands in (Compile, console) := Seq(
"shapeless.{ :: => :*:, _ }",
"scalaz._",
"Scalaz._"
).mkString("import ", ",", "")
80 changes: 74 additions & 6 deletions manuscript/book.org
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ libraryDependencies ++= Seq(
"com.github.mpilquist" %% "simulacrum" % "0.11.0",
"com.chuusai" %% "shapeless" % "2.3.2" ,
"com.fommil" %% "stalactite" % "0.0.4" ,
"org.scalaz" %% "scalaz" % "7.2.15"
"org.scalaz" %% "scalaz-core" % "7.2.15"
)

addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.4")
Expand Down Expand Up @@ -4076,7 +4076,7 @@ since =Functor= is so popular it gets the nickname. Likewise

@typeclass trait Contravariant[F[_]] extends InvariantFunctor[F] {
def contramap[A, B](fa: F[A])(f: B => A): F[B]
def xmap[A, B](fa: F[A], f: A => B, g: B => A): F[B] = contramap(fa)(fi)
def xmap[A, B](fa: F[A], f: A => B, g: B => A): F[B] = contramap(fa)(f)
...
}
#+END_SRC
Expand Down Expand Up @@ -4338,7 +4338,7 @@ class ApplicativeBuilder[F[_]: Apply, A, B](a: F[A], b: F[B]) {
}
#+END_SRC

In Chapter 3 we used this:
which is exactly what we used in Chapter 3:

#+BEGIN_SRC scala
(d.getBacklog |@| d.getAgents |@| m.getManaged |@| m.getAlive |@| m.getTime)
Expand Down Expand Up @@ -4382,8 +4382,7 @@ Apply[F].apply5(d.getBacklog, d.getAgents, m.getManaged, m.getAlive, m.getTime)
#+END_SRC

Despite being of most value for dealing with effects, =Apply= provides
convenient syntax for dealing with data structures. It is quite common
to have to deal with multiple =Option= values. Consider rewriting
convenient syntax for dealing with data structures. Consider rewriting

#+BEGIN_SRC scala
for {
Expand Down Expand Up @@ -4448,7 +4447,76 @@ Finally =forever=
repeating an effect without stopping. The instance of =Apply= must be
stack safe or we'll get =StackOverflowError=.

# **** TODO Bind and BindRec
**** Bind and BindRec

=Bind= introduces =bind=, synonymous with =flatMap=, which allows
functions on values to return a value in a context, which is then
bound to original context.

#+BEGIN_SRC scala
@typeclass trait Bind[F[_]] extends Apply[F] {

@op(">>=") def bind[A, B](fa: F[A])(f: A => F[B]): F[B]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = bind(fa)(f)

def join[A](ffa: F[F[A]]): F[A] = bind(ffa)(identity)

def ifM[B](value: F[Boolean], t: =>F[B], f: =>F[B]): F[B] = ...
def mproduct[A, B](fa: F[A])(f: A => F[B]): F[(A, B)] = ...

}
#+END_SRC

The =join= may be familiar if you have ever used =flatten= in the
stdlib, it takes nested contexts and squashes then into one.

=ifM= is a convenient and optimised way to construct a conditional
data structure or effect:

#+BEGIN_SRC scala
scala> List(true, false, true).ifM(List(0), List(1, 1))
res: List[Int] = List(0, 1, 1, 0)
#+END_SRC

Note that, as is the case with =bind=, results are joined. Although
not necessarily implemented as such, you can think of =bind= as being
a =Functor.map= followed by =join=

#+BEGIN_SRC scala
def bind[A, B](fa: F[A])(f: A => F[B]): F[B] = join(map(fa)(f))
#+END_SRC

=ifM= is optimised to cache and reuse the branches as they are used,
compare to the longer form

#+BEGIN_SRC scala
scala> List(true, false, true).flatMap { b => if (b) List(0) else List(1, 1) }
#+END_SRC

which produces a fresh =List(0)= or =List(1, 1)= every time the branch
is invoked. =ap= is optimised in the name manner.

#+BEGIN_ASIDE
These kinds of optimisations are possible in FP because all methods
are deterministic, also known as /referentially transparent/.

If a method returns a different value every time it is called, it is
/impure/ and breaks the reasoning and optimisations that we can
otherwise make.

If the =F= is an effect, perhaps one of our drone or Google algebras,
it does not mean that the output of the call to the algebra is cached.
Rather the reference to the operation is cached. The performance
optimisation of =ifM= is only noticeable for data structures, and more
pronounced with the difficulty of the work in each branch.

We will explore the concept of determinism in more detail in the next
chapter.
#+END_ASIDE

TODO: mproduct
TODO: BindRec

# *** TODO Applicative and Monad
# *** TODO Align
# *** TODO Divide and Divisable
Expand Down
2 changes: 1 addition & 1 deletion manuscript/frontmatter.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ FP-specific features enabled (e.g. in `build.sbt`):
"com.github.mpilquist" %% "simulacrum" % "0.11.0",
"com.chuusai" %% "shapeless" % "2.3.2" ,
"com.fommil" %% "stalactite" % "0.0.4" ,
"org.scalaz" %% "scalaz" % "7.2.15"
"org.scalaz" %% "scalaz-core" % "7.2.15"
)
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.4")
Expand Down
80 changes: 76 additions & 4 deletions manuscript/wip.md
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ since `Functor` is so popular it gets the nickname. Likewise
@typeclass trait Contravariant[F[_]] extends InvariantFunctor[F] {
def contramap[A, B](fa: F[A])(f: B => A): F[B]
def xmap[A, B](fa: F[A], f: A => B, g: B => A): F[B] = contramap(fa)(fi)
def xmap[A, B](fa: F[A], f: A => B, g: B => A): F[B] = contramap(fa)(f)
...
}
~~~~~~~~
Expand Down Expand Up @@ -1147,7 +1147,7 @@ then map over their combined output. Although it's *possible* to use
}
~~~~~~~~

In Chapter 3 we used this:
which is exactly what we used in Chapter 3:

{lang="text"}
~~~~~~~~
Expand Down Expand Up @@ -1193,8 +1193,7 @@ or directly call `applyX`
~~~~~~~~

Despite being of most value for dealing with effects, `Apply` provides
convenient syntax for dealing with data structures. It is quite common
to have to deal with multiple `Option` values. Consider rewriting
convenient syntax for dealing with data structures. Consider rewriting

{lang="text"}
~~~~~~~~
Expand Down Expand Up @@ -1267,6 +1266,79 @@ repeating an effect without stopping. The instance of `Apply` must be
stack safe or we'll get `StackOverflowError`.


### Bind and BindRec

`Bind` introduces `bind`, synonymous with `flatMap`, which allows
functions on values to return a value in a context, which is then
bound to original context.

{lang="text"}
~~~~~~~~
@typeclass trait Bind[F[_]] extends Apply[F] {
@op(">>=") def bind[A, B](fa: F[A])(f: A => F[B]): F[B]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = bind(fa)(f)
def join[A](ffa: F[F[A]]): F[A] = bind(ffa)(identity)
def ifM[B](value: F[Boolean], t: =>F[B], f: =>F[B]): F[B] = ...
def mproduct[A, B](fa: F[A])(f: A => F[B]): F[(A, B)] = ...
}
~~~~~~~~

The `join` may be familiar if you have ever used `flatten` in the
stdlib, it takes nested contexts and squashes then into one.

`ifM` is a convenient and optimised way to construct a conditional
data structure or effect:

{lang="text"}
~~~~~~~~
scala> List(true, false, true).ifM(List(0), List(1, 1))
res: List[Int] = List(0, 1, 1, 0)
~~~~~~~~

Note that, as is the case with `bind`, results are joined. Although
not necessarily implemented as such, you can think of `bind` as being
a `Functor.map` followed by `join`

{lang="text"}
~~~~~~~~
def bind[A, B](fa: F[A])(f: A => F[B]): F[B] = join(map(fa)(f))
~~~~~~~~

`ifM` is optimised to cache and reuse the branches as they are used,
compare to the longer form

{lang="text"}
~~~~~~~~
scala> List(true, false, true).flatMap { b => if (b) List(0) else List(1, 1) }
~~~~~~~~

which produces a fresh `List(0)` or `List(1, 1)` every time the branch
is invoked. `ap` is optimised in the name manner.

A> These kinds of optimisations are possible in FP because all methods
A> are deterministic, also known as *referentially transparent*.
A>
A> If a method returns a different value every time it is called, it is
A> *impure* and breaks the reasoning and optimisations that we can
A> otherwise make.
A>
A> If the `F` is an effect, perhaps one of our drone or Google algebras,
A> it does not mean that the output of the call to the algebra is cached.
A> Rather the reference to the operation is cached. The performance
A> optimisation of `ifM` is only noticeable for data structures, and more
A> pronounced with the difficulty of the work in each branch.
A>
A> We will explore the concept of determinism in more detail in the next
A> chapter.

TODO: mproduct
TODO: BindRec


# What's Next?

You've reached the end of this Early Access book. Please check the
Expand Down
1 change: 1 addition & 0 deletions project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=0.13.16
5 changes: 5 additions & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
scalacOptions ++= Seq("-unchecked", "-deprecation")
ivyLoggingLevel := UpdateLogging.Quiet
addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-RC11")

//addSbtPlugin("com.lucidchart" % "sbt-scalafmt-coursier" % "1.10")
2 changes: 2 additions & 0 deletions project/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ivyLoggingLevel := UpdateLogging.Quiet
addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-RC11")
11 changes: 11 additions & 0 deletions project/scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
align = most
continuationIndent.defnSite = 2
assumeStandardLibraryStripMargin = true
docstrings = JavaDoc
lineEndings = preserve
includeCurlyBraceInSelectChains = false
danglingParentheses = true
spaces.inImportCurlyBraces = true
optIn.annotationNewlines = true

rewrite.rules = [SortImports, RedundantBraces]

0 comments on commit beec7b9

Please sign in to comment.