class Foo {
val name: String = "foo"
def print = println(s"I am $name")
}
trait Vendor {
val ids: Set[Int]
val name: String
}
String
's and Int
'sint
, long
, double
, float
a + b == b + a
// Type of types
trait Numeric[A] {
// Operation:
def add(x: A, y: A): A
}
// Rule:
numeric.add(5, 1) mustEqual numeric.add(1, 5)
Numeric
?A
, where A
is able to be added// Valid
val Int = new Numeric[Int] {
def add(x: Int, y: Int) = x + y
}
val Double = new Numeric[Double] {
def add(x: Double, y: Double) = x + y
}
// Not Valid
val String = new Numeric[String] {
def add(x: String, y: String) = x + y
}
Numeric
is a way to categorize type:a
and b
, return a
appended to b
trait Example[A] {
def append(x: A, y: A): A
def identity: A
}
trait Monoid[A] {
def mappend(x: A, y: A): A
def mzero: A
}
val String = new Monoid[String] {
def mappend(x: String, y: String) = x.append(y)
def mzero = ""
}
val Int = new Monoid[Int] {
def mappend(x: Int, y: Int) = x + y
def mzero = 0
}
trait Foo[A]<1> {
def op<2>(arg: A<3>): A<4>
}
Seq
) as a type -- which it is -- but it's really a type of types, an algebraic structureSeq
trait Functor[A] {
def map(f: A => B): Functor[B]
}
A
and returns B
Functor
of type B
trait Functor[A] {
def map(f: A => B<1>): Functor[B]<2>
}
var Foo = function(value) {
this.value = value;
}
Foo.prototype.map = function(f) { // where f is A => B
return new Foo(f(value));
};
Seq
is a Functorval s: Seq[Int] = Seq(1, 2, 3)
val s2 = s.map(_.toString)
// s2.type == Seq[String]
Array
is a Functorvar s = [1, 2, 3];
var s2 = s.map(function(x) { return x.toString(); });
// ["1", "2", "3"]
Option
is a Functorval o: Option[Int] = Some(10)
val o2 = o.map(_.toString)
// o2.type == Option[String]
Future
is a Functorval f: Future[PoiLike] = PlaceService.getPlace(1234)
val f2 = s.map(_.mqId)
// f2.type == Future[Int]
var p = $.get("/api/place/1234");
var p2 = p.pipe(function(json) {
return json.name;
});
Promise.pipe
== Functor.map
fmap
always returns a new Functor
of B
, however the behavior of when fmap "applies" can varry.sealed trait Option[A] extends Functor[A]
case class Some[A](protected val value: A) extends Option[A] {
val map(f: A => B) = Some(f(value))
}
case object None[A] extends Option[A] {
val map(f: A => B) = this
}
val o: Option[String] = Some("hello")
o.map(_.toUpperCase)
// Some("HELLO")
val o2: Option[String] = None
o2.map(_.toUpperCase)
// None
Functor[Functor[A]]
?trait AddressLike(
country: String,
region: Option[String] = None,
locality: Option[String] = None,
...
)
val cityAndState = address.locality.map { city =>
address.region.map { state =>
city + ", " + state
}
}
cityAndState
?Option[Option[String]]
val cityAndState = address.locality.map { city =>
address.region.map { state =>
city + ", " + state
}
}
val cs1 = address.locality.map { city =>
address.region.map { state =>
city + ", " + state
}
}
val cs2 = cs1.flatten
// cs1.type == Option[Option[String]]
// cs2.type == Option[String]
flatMap
is a shortcut for map+map+flattenval cs1 = address.locality.flatMap { city =>
address.region.map { state =>
city + ", " + state
}
}
// cs1.type == Option[String]
Option[Option[String]]
flatMap
is technically part of a Monad which itself is a Functorfor/yield
is another way of writing chained flatMap
'sval o1 = Some("a")
val o2 = Some("b")
val o3 = Some("c")
o1.flatMap { a =>
o2.flatMap { b =>
o3.map { c =>
a + b + c
}
}
}
for {
a <- o1
b <- o2
c <- o3
} yield a + b + c
flatten
and flatMap
are available on all Functors in Scalamap
applies the given function f
returning not the result of f
, but a new Future
such that when the value eventually resolves, f
will be applied to itclass PlaceController extends Controller {
def place(id: String) = Action.async {
val futurePlace: Future[PlaceLike] = PlaceService.getPlace(id)
futurePlace.map { place =>
Ok(views.html.place(place))
}
}
}
futurePlace.map { place=> Ok(views.html.place(place) ...
?onSuccess
and onFailure
?class Future[U] {
def onSuccess[U](pf: PartialFunction[T, U]): Unit
def onFailure[U](pf: PartialFunction[T, U]): Unit
}
Unit
onSuccess
and onFailure
mutate the Future
, providing it a partial function that will be called when the future resolves.
They do not return a new Future
.
They are not Functor-like.
map
only applies to when the future succeeds, how do we get back a new Functor when it fails?recover
def get: Action[AnyContent] = Action.async { request =>
val p = Promise[SimpleResult]()
val f = fetchUserState(request.cookies)
f map {
...
}
f onFailure {
case e => Logger error("Recently viewed: " + e.getMessage)
}
f recover {
case _ => p success Ok
}
p future
}
def get: Action[AnyContent] = Action.async { request =>
fetchUserState(request.cookies)
.map { ... }
.recover { case e =>
Logger.error("Recently viewed: " + e.getMessage)
Ok
}
}
.getOrElse
on Option
's is stupidNone
implicit object StringMonoid extends Monoid[String] {
def mappend(x: String, y: String) = x + y
def mzero = ""
}
implicit def safeValue[A](opt: Option[A])(implicit md: Monoid[A]): A =
opt.getOrElse(md.mzero)
val some: Option[String] = Some("asdf")
val none: Option[String] = None
println(some.toUpperCase + none.toUpperCase)
> "ASDF"
Seq
based on a conditional.val s = Seq.empty
if (cond1) s = s ++ Seq(value1) else s
if (cond2) s = s ++ Seq(value1) else s
if (cond3) s = s ++ Seq(value1) else s
implicit class SeqOps[A](s: Seq[A]) {
def when(cond: => Boolean)(value: A) =
if(cond) s ++ Seq(value) else s
def unless(cond: => Boolean)(value: A) =
when(!cond)(value)
def always(value: A) = when(true)(value)
}
val widgets = Seq.empty
.unless(place.isInstanceOf[BizlocHotel]) { BookingWidget(...) }
.when(place.isInstanceOf[PoiLike]) { CategoryWidget(...) }
.when(place.isInstanceOf[AirportLike]) { DelaysWidget(...) }
.always { RecentlyViewedWidget(...) }
s ++ Seq(value)
is really Monoid AppendSeq.empty
is really Monoid Identityimplicit class MonoidOps[A](origin: A) {
def when(cond: => Boolean)(value: A)(implicit md: Monoid[A]) =
if(cond) md.mappend(origin, value) else origin
def unless(cond: => Boolean)(value: A)(implicit md: Monoid[A]) =
when(!cond)(value)
def always(value: A)(implicit md: Monoid[A]) =
when(true)(value)
}
def cityName(city: LocalityLike) =
city.locality
.when(city.country == "US") { city.region }
String
trait FromString[A] {
def fromString(value: String): A
}
def cityName[A](city: LocalityLike)(implicit md: Monoid[A], fs: FromString[A]) =
md.mzero
.always(fs.fromString(city.locality))
.when(city.country == "US") { fs.fromString(city.region) }
cityName[String](place)
// Or
cityName[Html](place)
// Or event
cityName[Seq[String]](place)