Jan Pustelnik / @gosubpl
http://xkcd.com/435/
By Aerolin55 - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=8266918
By Arian Zwegers (Warsaw, Old Town Square) [CC BY 2.0 (http://creativecommons.org/licenses/by/2.0)], via Wikimedia Commons
By Giramondo1 (Freudenstadt Town Square) [CC BY-SA 2.0 (http://creativecommons.org/licenses/by-sa/2.0)], via Wikimedia Commons
By FruitMonkey Description=Llanharan Town Square, from Bridgend Road facing North. CC BY-SA 3.0
Do you see a PATTERN there?
We will approach Cats and Scalaz from the Patterns angle, starting with some nice syntax extensions
There are more nice syntax helpers, e.g. Order, please check the documentation!
import scalaz._
import Scalaz._
import cats.syntax.show._
import cats.syntax.show._
object Main extends App {
println("abc".show)
println(10.show)
}
abc
10
import cats.syntax.show._
class Foo(val s: String) {}
object Main extends App {
val foo: Foo = new Foo("foo")
println(foo)
}
com.gft.scala.scalarconf.Foo@51d67073
import cats.syntax.show._
class Foo(val s: String) {}
object Main extends App {
val foo: Foo = new Foo("foo")
println(foo.show)
}
ERROR: value show is not a member of
com.gft.scala.scalarconf.Foo
import cats.syntax.show._
class Foo(val s: String) {}
object Main extends App {
val foo: Foo = new Foo("foo")
implicit val fooShow: Show[Foo] =
new Show[Foo] {
def show(foo: Foo) = foo.s
}
println(foo.show)
}
foo
import cats.syntax.show._
class Foo(val s: String) {
override def toString = s
}
object Main extends App {
val foo: Foo = new Foo("foo")
implicit val fooFromToStringShow: Show[Foo] =
Show.fromToString[Foo]
println(foo.show)
}
foo
class Foo(val s: String) {
override def toString = s
}
object Main extends App {
val foo: Foo = new Foo("foo")
implicit val fooFromToStringShow: Show[Foo] =
Show.showFromToString[Foo]
println(foo.show)
}
foo
import cats.syntax.eq._
import cats.syntax.eq._
object Main extends App {
println("abc" === "cde")
println("abc" =!= "cde")
//println("abc" =/= "cde")
println(5 === 5)
//println(5 === "a") // don't
}
false
true
true
import cats.syntax.eq._
object Main extends App {
implicit val fooEqInstance: Eq[Foo] =
new Eq[Foo] {
def eqv(lhs: Foo, rhs: Foo): Boolean = lhs.s === rhs.s
}
println(foo === foo)
}
true
object Main extends App {
implicit val fooEqualInstance: Equal[Foo] =
new Equal[Foo] {
def equal(lhs: Foo, rhs: Foo): Boolean =
lhs.s === rhs.s
}
println(foo === foo)
}
true
Interface satisfying requirements stated above one of the map function
def map[A, B](f: A => B): F[B]
Implementation is every generic collection F that can be parametrised with types A and B as F[A] and F[B] supplied with map function that operates on F[A], takes functions going from A to B and returns F[B], applying f to each element of F[A] and building F[B] from results of this application
def map[B, That](f: A => B)
(implicit bf: CanBuildFrom[Repr, B, That]): That = {
def builder = {
val b = bf(repr)
b.sizeHint(this)
b
}
val b = builder
for (x <- this) b += f(x)
b.result
}
But to actually use this for our custom type, we would have to overcome a couple of obstacles
Can we?
YES!
implicit val optionFunctor: Functor[Option] =
new Functor[Option] {
def map[A,B](fa: Option[A])(f: A => B) = fa map f
}
implicit val listFunctor: Functor[List] =
new Functor[List] {
def map[A,B](fa: List[A])(f: A => B) = fa map f
}
def covariantIdentity[A](fa: F[A]): IsEq[F[A]] =
fa.map(identity) <-> fa
def covariantComposition[A, B, C]
(fa: F[A], f: A => B, g: B => C): IsEq[F[C]] =
fa.map(f).map(g) <-> fa.map(f andThen g)
def transformWithFunc[F[_], A, B](a: F[A], fa: A => B)
(implicit F: Functor[F]) = F.map(a)(fa)
def twice(x: Int) = 2 * x
println(transformWithFunc(List(1, 2, 3), twice))
List(2, 4, 6)
println(transformWithFunc(1.some, twice))
Some(2)
import cats.syntax.cartesian._
val oa: Option[Int] = Some(3)
val ob: Option[Int] = Some(5)
val od = oa |@| ob
val odx = od.map(_ + _)
println(odx)
Some(8)
import cats.syntax.cartesian._
val oe = 3.some |@| 5.some
val oex = od.map(_ + _)
println(oex)
Some(8)
val oe = 3.some |@| 5.some
val oex = od.apply(_ + _)
println(oex)
Some(8)
Cats
def buildTwo[F[_], B](a: F[B], b: F[B])
(implicit F: Cartesian[F]) = a |@| b
Scalaz
def buildTwo[F[_], B](a: F[B], b: F[B])
(implicit F: Apply[F]) = a |@| b
def pure[A](x: A): F[A]
Applicative[Option].pure(1)
Some(1)
Applicative[List].pure(1)
List(1)
import catz.syntax.applicative._
def sequenceA[F[_]: Applicative, A](list: List[F[A]])
: F[List[A]] = list match {
case Nil => (Nil: List[A]).pure[F]
case x :: xs => (x |@| sequenceA(xs)).map { _ :: _ }
}
println(sequenceA(List(1.some, 2.some, 3.some)))
println(sequenceA(List(1.some, none[Int], 2.some)))
println(sequenceA(List(1.some, "a".some)))
//println(sequenceA(List(1.some, List(2))))
Some(List(1, 2, 3))
None
Some(List(1, a))
val on = 1.some |@| none[Int]
println(on.map(_ + _))
None
val propsStringGood =
List("db.user=gosubpl", "db.pass=pass", "db.name=dbase1")
val propsStringBad = List("db.user=gosubpl", "db.pass=pass")
println(propsStringGood.find(_.startsWith("db.user="))
.map(_.replace("db.user=", "")))
println(propsStringBad.find(_.startsWith("db.name="))
.map(_.replace("db.name=", "")))
Some(gosubpl)
None
def readProperty(prop: String)(props: List[String])
: Option[String] =
props.find(_.startsWith(prop + "="))
.map(_.replace(prop + "=", ""))
val dbUser = readProperty("db.user") _
val dbName = readProperty("db.name") _
val dbPass = readProperty("db.pass") _
def dbProps(config: List[String]) =
(dbUser(config) |@| dbName(config) |@| dbPass(config))
// use apply instead of map for Scalaz
println(dbProps(propsStringGood)
.map(_ + " " + _ + " " + _))
println(dbProps(propsStringBad)
.map(_ + " " + _ + " " + _))
Some(gosubpl dbase1 pass)
None
def applyProps(f : Function1[List[String], Option[String]],
s: List[String])
= f(s)
println(sequenceA(List(dbUser, dbName, dbPass)
.map(applyProps(_, propsStringGood))))
Some(List(gosubpl, dbase1, pass))
println(List(dbUser, dbName, dbPass)
.map(applyProps(_, propsStringGood)).sequence)
println(List(dbUser, dbName, dbPass)
.map(applyProps(_, propsStringGood))
.traverse(identity))
println(List(dbUser, dbName, dbPass)
.map(applyProps(_, propsStringGood))
.traverse(_.map(_.toUpperCase())))
Some(List(gosubpl, dbase1, pass))
Some(List(gosubpl, dbase1, pass))
Some(List(GOSUBPL, DBASE1, PASS))
val propsStringBad = List("db.user=gosubpl", "db.pass=pass")
val propsStringVeryBad = List("db.user=gosubpl")
None
None
import cats.data.Validated
Validated[SuccessType,FailureType] = Valid | Invalid
Validation[SuccessType,FailureType] = Succes | Failure
def readPropertyV(prop: String)(props: List[String])
: Validation[String, String] =
props.find(_.startsWith(prop + "="))
.map(_.replace(prop + "=", ""))
match {
case None =>
("Property " + prop + " not found in config")
.failure[String]
case Some(s) => s.success[String]
}
val dbUserV = readPropertyV("db.user") _
val dbNameV = readPropertyV("db.name") _
val dbPassV = readPropertyV("db.pass") _
val propsStringBad =
List("db.user=gosubpl", "db.pass=pass")
val propsStringVeryBad = List("db.user=gosubpl")
def applyPropsV(f: Function1[List[String],
Validation[String, String]],
s: List[String])
= f(s)
println(List(dbUserV, dbNameV, dbPassV)
.map(applyPropsV(_, propsStringBad))
.traverseU(identity))
println(List(dbUserV, dbNameV, dbPassV)
.map(applyPropsV(_, propsStringVeryBad))
.sequenceU)
Failure(Property db.name not found in config)
Failure(Property db.name not found in config
Property db.pass not found in config)
def readPropertyV(prop: String)(props: List[String])
: Validated[String, String] =
Validated.fromOption(
props.find(_.startsWith(prop + "="))
.map(_.replace(prop + "=", "")),
"Property " + prop
+ " not found in config")
val dbUserV = readPropertyV("db.user") _
val dbNameV = readPropertyV("db.name") _
val dbPassV = readPropertyV("db.pass") _
def applyPropsV(f: Function1[List[String],
Validated[String, String]],
s: List[String])
= f(s)
Jan Pustelnik
Slides: http://gosubpl.github.io/scalar-2016-slides