in this function head, it takes a list of A’s,
and returns an A. And it doesn’t matter what the A is:
It could be Ints, Strings, Oranages, Cars, whatever.
Any A would work, and the function is defined for every A that there can be.
Subtype polymorphism
1
defplus[A <: Plus[A]](a1: A, a2: A): A = a1.plus(a2)
Ad-hoc polymorphism
1
def plus[A: Plus](a1: A, a2: A): A = implicitly[Plus[A]].plus(a1, a2)
we can provide separate function definitions for different types of A
we can provide function definitions to types (like Int) without
access to its source code
the function definitions can be enabled or disabled in different scopes
Monoid
1
2
3
4
5
object IntMonoid {
defmappend(a: Int, b: Int): Int = a + b
defmzero: Int = 0
}
defsum(xs: List[Int]): Int = xs.foldLeft(IntMonoid.mzero)(IntMonoid.mappend)
version 2
1
2
3
4
5
6
7
8
9
trait Monoid[A] {
defmappend(a1: A, a2: A): A
defmzero: A
}
object IntMonoid extends Monoid[Int] {
defmappend(a: Int, b: Int): Int = a + b
defmzero: Int = 0
}
defsum[A](xs: List[A], m: Monoid[A]): A = xs.foldLeft(m.mzero)(m.mappend)
version 3
1
2
3
4
5
6
7
defsum[A](xs: List[A])(implicit m: Monoid[A]): A = xs.foldLeft(m.mzero)(m.mappend)
implicit val intMonoid = IntMonoid
// 也可以这样
defsum[A: Monoid](xs:List[A]):A = {
val m = implicity[Monoid[A]]
xs.foldLeft(m.mzero)(m.mappend)
}
我们也可以提供其他Monoid
1
2
3
4
5
val multiMoinoid: Monoid[Int] = new Monoid[Int] {
defmappend(a:Int, b:Int): Int = a * b
defmzero:Int = 1
}
sum(List(1, 2, 3, 4))(multiMonoid)
FoldLeft
1
2
3
4
5
6
7
8
9
10
object FoldLeftList {
def foldLeft[A, B](xs: List[A], b: B, f: (B, A) => B) = xs.foldLeft(b)(f)
}
def sum[A: Monoid](xs: List[A]): A = {
val m = implicitly[Monoid[A]]
FoldLeftList.foldLeft(xs, m.mzero, m.mappend)
}
// 可以这样使用
sum(List(1, 2, 3, 4))(multiMonoid)
版本2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
trait FoldLeft[F[_]] {
def foldLeft[A, B](xs: F[A], b: B, f: (B, A) => B): B
}
object FoldLeft {
implicit val FoldLeftList: FoldLeft[List] = new FoldLeft[List] {
def foldLeft[A, B](xs:List[a], b: B, f: (B, A) => B) = xs.foldLeft(b)(f)
}
}
def sum[M[_]: FoldLeft, A: Monoid](xs: M[A]): A = {
val m = implicitly[Monoid[A]]
val fl = implicitly[FoldLeft[M]]
fl.foldLeft(xs, m.mzero, m.mappend)
}
// 用法如下
sum(List("a", "b", "c"))
Typeclass in Scalaz
We would like to provide an operator.
But we don’t want to enrich just one type,
but enrich all types that has an instance for Monoid.
1
2
3
4
5
6
7
8
9
10
11
12
13
trait MonoidOp[A] {
val F: Monoid[A]
val value: A
def |+|(a2:A) = F.mappend(value, v2)
}
implicit def toMonoidOp[A: Monoid](a: A):MonoidOp[A] = new MonoidOp[A] {
val F = implicitly[Monoid[A]]
val value = a
}
// 简写如下
3 |+| 4
Using the same technique above, Scalaz also provides method
injections for standard library types like Option and Boolean:
1
2
3
4
5
6
7
8
9
10
11
scala> 1.some | 2
res0: Int = 1
scala> Some(1).getOrElse(2)
res1: Int = 1
scala> (1 > 10)? 1 | 2
res3: Int = 2
scala> if (1 > 10) 1else2
res4: Int = 2
typeclasses
It provides purely functional data structures to complement those from
the Scala standard library. It defines a set of foundational
type classes (e.g. Functor, Monad) and corresponding instances
for a large number of data structures.
Instead of the standard ==, Equal enables ===, =/=, and assert_=== syntax
by declaring equal method. The main difference is that === would fail
compilation if you tried to compare Int and String.
Read is sort of the opposite typeclass of Show.
The read function takes a string and returns a type
which is a member of Read.
I could not find Scalaz equivalent for this typeclass.
Enum
Enum members are sequentially ordered types — they can be enumerated.
The main advantage of the Enum typeclass is that we can use its types in list ranges.
They also have defined successors and predecesors,
which you can get with the succ and pred functions.
Instead of the standard to, Enum enables |-> that returns a List by
declaring pred and succ method on top of Order typeclass.
There are a bunch of other operations it enables like
-+-, ---, from, fromStep, pred, predx, succ, succx, |-->, |->, |==>, and |=>.
these are all about stepping forward or backward, and returning ranges.
So far, when we were mapping functions over functors,
we usually mapped functions that take only one parameter.
ut what happens when we map a function like *, which takes two parameters,
over a functor?