The dual of folding is unfolding.
The Haskell standard List library defines
the function unfoldr for generating lists.
1
2
Prelude Data.List> unfoldr (\b -> if b ==0 then Nothing else Just (b, b-1)) 10[10,9,8,7,6,5,4,3,2,1]
DList
There’s a data structure called DList that supports DList.unfoldr.
DList, or difference list, is a data structure that supports
constant-time appending.
In 2006 the same author wrote The Essence of the Iterator Pattern.
This paper discusses applicative style by breaking down the GoF Iterator
pattern into two aspects: mapping and accumulating.
Monoidal applicatives
Scalaz implements Monoid[m].applicative to turn any monoids into
an applicative.
There’s a useful method that Traverse introduces called sequence.
1
2
3
4
5
/** Traverse with the identity function */
final def sequence[G[_], B](implicit ev: A === G[B], G: Applicative[G]): G[F[B]] = {
val fgb: F[G[B]] = ev.subst[F](self)
F.sequence(fgb)
}
To include names of values and types into the scope.
To include implicits into the scope.
import scalaz._
First, the names. Typeclasses like Equal[A] and Functor[F[_]]
are implemented as trait,
and are defined under scalaz package.
also the names, but type aliases. scalaz’s package object
declares most of the major type aliases like @@[T, Tag] and Reader[E, A],
which is treated as a specialization of ReaderT transformer.
idInstance is defined as typeclass instance of Id[A] for Traverse[F[_]], Monad[F[_]]
import Scalaz._
1
2
3
4
5
6
7
8
9
10
package scalaz
object Scalaz
extends StateFunctions // Functions related to the state monad
with syntax.ToTypeClassOps // syntax associated withtype classes
with syntax.ToDataOps // syntax associated with Scalaz data structures
with std.AllInstances // Type class instances for the standard library types
with std.AllFunctions // Functions related to standard library types
with syntax.std.ToAllStdOps // syntax associated with standard library types
with IdInstances // Identity typeand instances
StateFunctions
Remember, import brings in names and implicits. First, the names.
StateFunctions defines several functions:
package scalaz
package std
traitAllFunctionsextends ListFunctions
with OptionFunctions
with StreamFunctions
with math.OrderingFunctions
with StringFunctions
objectAllFunctionsextendsAllFunctions
For example, ListFunctions bring in intersperse function that puts
a given element in ever other position:
1
intersperse(List(1, 2, 3), 7)
IdInstances
defines the type alias Id[A] as follows:
1
typeId[+X] = X
std.AllInstances
the fact that list is a monad and that
monad introduces >>= operator are two different things.
one of the most interesting design of scalaz 7 is that it
rigorously separates the two concepts into “instance” and “syntax.”
std.allinstances is a mixin of typeclass instances for built-in (std) data structures:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package scalaz.std
traitallinstancesextends anyvalinstances with functioninstances with listinstances with mapinstances
with optioninstances with setinstances with stringinstances with streaminstances with tupleinstances
with eitherinstances with partialfunctioninstances with typeconstraintinstances
with scalaz.std.math.bigdecimalinstances with scalaz.std.math.bigints
with scalaz.std.math.orderinginstances
with scalaz.std.util.parsing.combinator.parsers
with scalaz.std.java.util.mapinstances
with scalaz.std.java.math.bigintegerinstances
with scalaz.std.java.util.concurrent.callableinstances
with nodeseqinstances
// intentionally omitted: iterableinstancesobjectallinstancesextendsallinstances
syntax.totypeclassops
1
2
3
4
5
6
7
8
9
10
11
12
package scalaz
package syntax
traittotypeclassopsextends tosemigroupops with tomonoidops with togroupops with toequalops with tolengthops with toshowops
with toorderops with toenumops with tometricspaceops with toplusemptyops with toeachops with toindexops
with tofunctorops with topointedops with tocontravariantops with tocopointedops with toapplyops
with toapplicativeops with tobindops with tomonadops with tocojoinops with tocomonadops
with tobifoldableops with tocozipops
with toplusops with toapplicativeplusops with tomonadplusops with totraverseops with tobifunctorops
with tobitraverseops with toarridops with tocomposeops with tocategoryops
with toarrowops with tofoldableops with tochoiceops with tosplitops with tozipops with tounzipops with tomonadwriterops with tolistenablemonadwriterops
introduces methods and operators to scala’s standard types.
1
2
3
4
5
6
7
package scalaz
package syntax
package std
traittoallstdopsextends tobooleanops with tooptionops with tooptionidops with tolistops with tostreamops
with tofunction2ops with tofunction1ops with tostringops with totupleops with tomapops with toeitherops
What’s actually going on is not just the combination of applicative
functors (m ⊠ n), but the combination of applicative functions:
1
2
(⊗)::(Functor m,Functor n) ⇒ (a → m b) → (a → n b) → (a → (m ⊠ n) b)
(f ⊗ g) x = Prod (f x) (g x)
Int is a Monoid, and any Monoid can be treated as an applicative functor,
which is called monoidal applicatives.
The problem is that when we make that into a function, it’s not distinguishable
from Int => Int, but we need Int => [α]Int
TODO
Arrow
An arrow is the term used in category theory as an abstract
notion of thing that behaves like a function.
In Scalaz, these are Function1[A, B], PartialFunction[A, B],
Kleisli[F[_], A, B], and CoKleisli[F[_], A, B]. Arrow
abstracts them all similar to the way other typeclasses abtracts containers.
1
2
3
4
5
6
// Looks like Arrow[=>:[_, _]] extends Category[=>:].
trait Arrow[=>:[_, _]] extends Category[=>:] { self =>
defid[A]: A =>: A
defarr[A, B](f: A => B): A =>: B
deffirst[A, B, C](f: (A =>: B)): ((A, C) =>: (B, C))
}
Category and Compose
1
2
3
4
5
6
7
8
trait Category[=>:[_, _]] extends Compose[=>:] { self =>
/** The left and right identity over `compose`. */
defid[A]: A =>: A
}
trait Compose[=>:[_, _]] { self =>
defcompose[A, B, C](f: B =>: C, g: A =>: B): (A =>: C)
}
compose function composes two arrows into one. Using compose, Compose
introduces the following operators:
The meaning of >>> and <<< depends on the arrow, but for functions,
it’s the same as andThen and compose:
1
2
3
4
5
6
7
8
9
10
11
scala> val f = (_:Int) + 1
f: Int => Int = <function1>
scala> val g = (_:Int) * 100
g: Int => Int = <function1>
scala> (f >>> g)(2)
res0: Int = 300
scala> (f <<< g)(2)
res1: Int = 201
scala> val f = (_:Int) + 1f: Int => Int = <function1>
scala> val g = (_:Int) * 100g: Int => Int = <function1>
// combines two arrows into a new arrow by running
// the two arrows on a pair of values
scala> (f *** g)(1, 2)
res3: (Int, Int) = (2,200)
// combines two arrows into a new arrow by running the
// two arrows on the same value
scala> (f &&& g)(2)
res4: (Int, Int) = (3,200)
Unapply
One thing that I’ve been fighting the Scala compiler over
is the lack of type inference support across the different kinded types
like F[M[_, _]] and F[M[_]], and M[_] and F[M[_]].
1
2
3
4
5
6
7
scala> Applicative[Function1[Int, Int]]
<console>:14: error: Int => Int takes no type parameters, expected: one
Applicative[Function1[Int, Int]]
^
// an instance of Applicative[M[_]] is(* -> *) -> *
scala> Applicative[({type l[A]=Function1[Int, A]})#l]res14: scalaz.Applicative[[A]Int => A] = scalaz.std.FunctionInstances$$anon$2@56ae78ac
One of the way Scalaz helps you out is to provide
meta-instances of typeclass instance called Unapply.
1
2
3
4
5
6
7
8
9
10
traitUnapply[TC[_[_]], MA] {/** The type constructor */type M[_]
/** The type that `M` was applied to */type A
/** The instance of the type class */def TC: TC[M]
/** Evidence that MA =:= M[A] */def apply(ma: MA): M[A]
}
TODO
parallel composition
TODO
Momo
Pure functions don’t imply they are computationally cheap.
Given you have some space in RAM, we could trade some of the
expensive calculations for space by caching the result.
This is called memoization.
1
2
3
sealed trait Memo[@specialized(Int) K, @specialized(Int, Long, Double) V] {defapply(z: K => V): K => V
}
We pass in a potentially expensive function as an input
and you get back a function that behaves the same but may cache the result.
Memo object there are some default implementations of Memo like
Memo.mutableHashMapMemo[K, V], Memo.weakHashMapMemo[K, V], and Memo.arrayMemo[V].
缓存每次运算的结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
scala> val slowFib: Int => Int = {
case0 => 0case1 => 1case n => slowFib(n - 2) + slowFib(n - 1)
}
slowFib: Int => Int = <function1>
scala> slowFib(45)
res2: Int = 1134903170
scala> val memoizedFib: Int => Int = Memo.mutableHashMapMemo {
case0 => 0case1 => 1case n => memoizedFib(n - 2) + memoizedFib(n - 1)
}
memoizedFib: Int => Int = <function1>
scala> memoizedFib(45)
res14: Int = 1134903170
functional programming
An expression e is referentially transparent if every occurrence e can
be replaced with its value without affecting the observable result of the program.
sealedtraitSTRef[S, A] {protectedvar value: A
/**Reads the value pointed at by this reference. */def read: ST[S, A] = returnST(value)
/**Modifies the value at this reference with the given function. */def mod[B](f: A => A): ST[S, STRef[S, A]] = ...
/**Associates this reference with the given value. */def write(a: => A): ST[S, STRef[S, A]] = ...
/**Synonym for write*/def |=(a: => A): ST[S, STRef[S, A]] = ...
/**Swap the value at this reference with the value at another. */def swap(that: STRef[S, A]): ST[S, Unit] = ...
}
sealedtraitSTArray[S, A] {val size: Int
val z: A
privateval value: Array[A] = Array.fill(size)(z)
/**Reads the value at the given index. */def read(i: Int): ST[S, A] = returnST(value(i))
/**Writes the given value to the array, at the given offset. */def write(i: Int, a: A): ST[S, STArray[S, A]] = ...
/**Turns a mutable array into an immutable one which is safe to return. */def freeze: ST[S, ImmutableArray[A]] = ...
/**Fill this array from the given association list. */def fill[B](f: (A, B) => A, xs: Traversable[(Int, B)]): ST[S, Unit] = ...
/**Combine the given value with the value at the given index, using the given function. */def update[B](f: (A, B) => A, i: Int, v: B) = ...
}
/** Reads a character from standard input. */
defgetChar: IO[Char] = ...
/** Writes a character to standard output. */
defputChar(c: Char): IO[Unit] = ...
/** Writes a string to standard output. */
defputStr(s: String): IO[Unit] = ...
/** Writes a string to standard output, followed by a newline.*/
defputStrLn(s: String): IO[Unit] = ...
/** Reads a line of standard input. */
defreadLn: IO[String] = ...
/** Write the given value to standard output. */
defputOut[A](a: A): IO[Unit] = ...
// Mutable variables in the IO monad
defnewIORef[A](a: => A): IO[IORef[A]] = ...
/**Throw the given error in the IO monad. */
defthrowIO[A](e: Throwable): IO[A] = ...
/** An IO action that does nothing. */
val ioUnit: IO[Unit] = ...
}
1
2
3
4
5
6
7
8
scala> val action2 = IO {
val source = scala.io.Source.fromFile("./README.md")
source.getLines.toStream
}
action2: scalaz.effect.IO[scala.collection.immutable.Stream[String]] = scalaz.effect.IOFunctions$$anon$4@bab4387
scala> action2.unsafePerformIO.toListres57: List[String] = List(# Scalaz, "", Scalaz is a Scala library for functional programming., "", It provides purely functional data structures to complement those from the Scala standard library., ...
1
2
3
4
5
6
7
8
defprogram: IO[Unit] = for {
line <- readLn
_ <- putStrLn(line)
} yield ()
scala> (program |+| program).unsafePerformIO
123123