en nybegynners introduksjon til scalaz

57
© Mesan AS scalaz En nybegynners introduksjon

Upload: trond-marius-ovstetun

Post on 21-May-2015

321 views

Category:

Technology


1 download

DESCRIPTION

My presentation at Javazone 2013 http://jz13.java.no/presentation.html?id=6c09d5d7

TRANSCRIPT

Page 1: En nybegynners introduksjon til scalaz

© Mesan AS

scalaz

En nybegynners introduksjon

Page 2: En nybegynners introduksjon til scalaz

© Mesan AS

Agenda•Bakgrunnen for at jeg har tatt i bruk scalaz

•En vei gjennom rammeverket

•Konklusjoner

•Gjerne spørsmål underveis!

Page 3: En nybegynners introduksjon til scalaz

© Mesan AS

Trond Marius Øvstetun

•Sjesfkonsulent i Mesan

•Utvikler, arkitekt, teamleder +++

Page 4: En nybegynners introduksjon til scalaz

© Mesan AS

Mesan... når standardsystemer ikke er nok

•Systemutvikling – skreddersøm

•Løsningsfokus

•Transaksjonssystemer

•Lang levetid – langsiktighet ogvedlikeholdbarhet

Page 5: En nybegynners introduksjon til scalaz

© Mesan AS

Hvorfor scalaz?

Page 6: En nybegynners introduksjon til scalaz

© Mesan AS

Hvorfor scalaz?•If you are thinking about using Scalaz, stop now while you still have your sanity! ref

•Don’t listen to anyone telling you to use Scalaz!

•Hva er greia med metodene<:::, :::>, <+>, |@|, ★, ☆, <=< ?

Page 7: En nybegynners introduksjon til scalaz

© Mesan AS

Mitt triggerpunkt

Page 8: En nybegynners introduksjon til scalaz

© Mesan AS

unfiltered og json

case req @ POST(Path("/person")) => { val x = Body.string(req) val p: Person = read[Person](x) val id: Int = personer.add(p)

Created ~> Json("id" -> id)}

case class Person(name: String, age: Int)

Page 9: En nybegynners introduksjon til scalaz

© Mesan AS

json

val json = """{"name": "Petter", "age": 22}"""read[Person](json)

val json = """{"name":"Petter"}"""read[Person](json)

=> Person(Petter, 22)

=> org.json4s.package$MappingException: No usable value for age Did not find value which can be converted into int

Page 10: En nybegynners introduksjon til scalaz

© Mesan AS

Validering av input

def add(p: Person): Int = { Validate.isTrue(p.age > 18 && p.age < 60) 1}

case req @ POST(Path("/person")) => { val x = Body.string(req) val p: Person = read[Person](x) val id: Int = personer.add(p)

Created ~> Json("id" -> id)}

Page 11: En nybegynners introduksjon til scalaz

© Mesan AS

Fra en klients perspektiv...

Page 12: En nybegynners introduksjon til scalaz

© Mesan AS

Fra en utviklers perspektiv

Dette føles ikke bra!

Page 13: En nybegynners introduksjon til scalaz

© Mesan AS

validering med scalaz

case req @ POST(Path("/personz")) => { val x = Body.string(req) val p = fromJSON[Person](parse(x)) val id = p map personer.add

id match { case Success(i) => Created ~> Json("id"->i) case Failure(errors) => Forbidden ~> Json("errors" -> jsonErrors(errors).toList) }}

Page 14: En nybegynners introduksjon til scalaz

© Mesan AS

validering med scalaz

case req @ POST(Path("/personz")) => { val x = Body.string(req) val p = fromJSON[Person](parse(x)) val id = p map personer.add

id match { case Success(i) => Created ~> Json("id"->i) case Failure(errors) => Forbidden ~> Json("errors" -> jsonErrors(errors).toList) }}

implicit val personR: JSONR[Person] = Person.applyJSON(field[String]("name"), validate[Int]("age") >==> min(18) >==> max(60))

Page 15: En nybegynners introduksjon til scalaz

© Mesan AS

Fra en klients perspektiv...

Page 16: En nybegynners introduksjon til scalaz

© Mesan AS

Fra en utviklers perspektiv

Mye bedre?! Men?!

>==> fromJSON[Person]

Kleisli[Result, JValue, A]

type EitherNel[+a] = NonEmptyList[Error] \/ a

Page 17: En nybegynners introduksjon til scalaz

© Mesan AS

scalaz

import scalaz._import Scalaz._

Page 18: En nybegynners introduksjon til scalaz

© Mesan AS

Hva er scalaz?•Scalaz is a Scala library for functional programming.

•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.

Page 19: En nybegynners introduksjon til scalaz

© Mesan AS

Hva er scalaz?1.Funksjonelle datastrukturer

2.Syntaks for utvidelse av standard klasser

3.Typeklasser og instanser for disse

Page 20: En nybegynners introduksjon til scalaz

© Mesan AS

Syntaks

utvidelser til standard klasserog til hvordan du skriver kode

Page 21: En nybegynners introduksjon til scalaz

© Mesan AS

Equal

1 == 1=> true

1 == 1.0=> true

val x: Int = 1val y: Person = new Person("Petter")

x == y warning: comparing values of types Int and Person using `==' will always yield false

=> false

y == x warning: Person and Int are unrelated: they will most likely never compare equal

=> false

1 == 1=> false

Page 22: En nybegynners introduksjon til scalaz

© Mesan AS

Typesikker Equal

1 === 1=> true

1 === 1.0 error: could not find implicit value for parameter F0: scalaz.Equal[Any]

1 =/= 1=> false

1 =/= 1.0 error: could not find implicit value for parameter F0: scalaz.Equal[Any]

1 === 2=> false

1 =/= 2=> true

Page 23: En nybegynners introduksjon til scalaz

© Mesan AS

Boolean

true && true=> true

true /\ true=> true

true && false=> false

true /\ false=> false

(if (true) "a" else "b")=> "a"

true ? "a" | "b"=> "a"

true option "a"=> Some("a")

false option "a"=> (None:Option[String])

Page 24: En nybegynners introduksjon til scalaz

© Mesan AS

Option

Some(42)=> Some[Int] = Some(42)

None=> None.type = None

some(42)=> Option[Int] = Some(42)

none[Int]=> Option[Int] = None

42.some=> Some(42)

val o = 42.someo getOrElse 1o | 1=> 42

val n = option.none[Int]n getOrElse 1n | 1=> 1

~o=> 42

~n=> 0

Page 25: En nybegynners introduksjon til scalaz

© Mesan AS

Index

val l = List(1,2,3,4)

l(0)=> 1l(2)=> 3

l(-1)=> IndexOutOfBoundsExceptionl(4)=> IndexOutOfBoundsException

l index 0=> Some(1)l index 2=> Some(3)

l index -1=> Nonel index 4=> None

Page 26: En nybegynners introduksjon til scalaz

© Mesan AS

Alle instanser - Id

"a" ?? "b"=> "a"

val x: String = nullx ?? "asdf"=> "asdf"

def f(i:Int) = i + 1(1 |> f)=> 2

1 + 2 + 3 |> {_ * 6}=> 36

Page 27: En nybegynners introduksjon til scalaz

© Mesan AS

Typeklasser

Page 28: En nybegynners introduksjon til scalaz

© Mesan AS

Hva er en Typeklasse?•En form for et interface som definerer oppførsel til en type

•oppførselen er definert utenfor typen

•alle typer som er medlem i typeklassen har implementasjoner av oppførselen

•ikke et interface som i Java

Page 29: En nybegynners introduksjon til scalaz

© Mesan AS

Typeklasser i Scala

trait Truthy[A] { def truthy(a: A): Boolean}

implicit val intTruthy: Truthy[Int] = new Truthy[Int] { def truthy(a: Int): Boolean = a match { case 0 => false case _ => true }}

intTruthy.truthy(1)=> trueintTruthy.truthy(0)=> false

Page 30: En nybegynners introduksjon til scalaz

© Mesan AS

Typeklasser i Scala

object Truthy { def apply[A](implicit A: Truthy[A]): Truthy[A] = A}

> Truthy[Int].truthy(0)=> falseTruthy[Int].truthy(1)=> true

1.truthy=> true

Page 31: En nybegynners introduksjon til scalaz

© Mesan AS

Typeklasser i Scala

trait TruthyOps[A] { def self: A implicit def F:Truthy[A] def truthy: Boolean = F.truthy(self)}

object ToTruthyOps { implicit def toTOps[A](a: A)(implicit ev: Truthy[A]) = new TruthyOps[A] { def self: A = a implicit def F = ev }}

1.truthy=> true

Page 32: En nybegynners introduksjon til scalaz

© Mesan AS

Typeklasser i Scala

def iffy[A: Truthy, B](t: A)(ifT: =>B)(ifF: =>B): B = { if (t.truthy) ifT else ifF}

iffy(1)("Ja!")("Neeei!")=> Ja!

iffy(0)("Ja!")("Neeei!")=> Neeei!

Page 33: En nybegynners introduksjon til scalaz

© Mesan AS

Typeklasser i Scalaz

Page 34: En nybegynners introduksjon til scalaz

© Mesan AS

Typeklasser i Scalaz

Page 35: En nybegynners introduksjon til scalaz

© Mesan AS

Equaltrait Equal[F] { self => def equal(a1: F, a2: F): Boolean}

implicit val intInstance: Monoid[Int] with Enum[Int] with Show[Int] = new Monoid[Int] with Enum[Int] with Show[Int] {....}

trait EqualOps[F] extends Ops[F] { final def ===(other: F): Boolean = ??? final def =/=(other: F): Boolean = ???}

1 === 1=> true

Page 36: En nybegynners introduksjon til scalaz

© Mesan AS

Equal

case class Car(val model: String)implicit val carEqual = new Equal[Car] { def equal(a1: Car, a2: Car): Boolean = a1.model == a2.model}

Car("Honda") === Car("Toyota")=> false

Car("Honda") === Car("Honda")=> true

Page 37: En nybegynners introduksjon til scalaz

© Mesan AS

SemiGroup

trait Semigroup[F] { self => def append(f1: F, f2: => F): F}

trait SemigroupOps[F] extends Ops[F] { implicit def F: Semigroup[F]

final def |+|(other: => F): F = ??? final def mappend(other: => F): F = ???}

1 |+| 2=> 3

List(1,2) |+| List(3,4)=> List(1,2,3,4)

(1,2) |+| (3,4)=> (4,6)

Page 38: En nybegynners introduksjon til scalaz

© Mesan AS

Monoid

trait Monoid[F] extends Semigroup[F] { self => def zero: F}

trait MonoidOps[F] extends Ops[F] { final def multiply(n: Int): F = ??? final def ifEmpty[A](tv: => A)(fv: => A) = ???}

(0.ifEmpty("Empty")("NonEmpty"))=> "Empty"(1.ifEmpty("Empty")("NonEmpty"))=> "NonEmpty"

"a" multiply 3=> "aaa"~o.none[Int]=> 0~3.some=> 3

Page 39: En nybegynners introduksjon til scalaz

© Mesan AS

Monoid avler Monoid!•Option[A] er Monoid hvis A er Monoid

•Map[K, A] er Monoid hivs A er Monoid

•(A, B) er Monoid hvis A og B er Monoid!

(1,2) |+| (3,4)=> (4,6)

Map(1->1, 2->3) |+| Map(1->4, 3->6)=> Map(1->5, 2->3, 3->6)

(1.some, 2.some) |+| (3.some, o.none[Int])=> (4.some, 2.some)

Page 40: En nybegynners introduksjon til scalaz

© Mesan AS

Og Monider er overalt..•Tall (Int, Double, BigDecimal....)

•Penger (Din egen klasse)

•Dato / Perioder

•Kun fantasien setter grenser...

100.NOK |+| 250.NOK=> Money(350, NOK)

1.day |+| 1.week=> Period(1, 1)

Page 41: En nybegynners introduksjon til scalaz

© Mesan AS

Functor

trait Functor[F[_]] { self => def map[A, B](fa: F[A])(f: A => B): F[B]}

List(1, 2, 3) map {_ + 1}=> List(2,3,4)

trait FunctorOps[F[_],A] extends Ops[F[A]] { final def map[B](f: A => B): F[B] = ??? final def fpair: F[(A, A)] = ??? final def fproduct[B](f: A => B): F[(A, B)] = ??? final def >|[B](b: => B): F[B] = ???}

List(1,2,3) >| "a"=> List("a","a","a")

(List(1,2,3) fpair)=> List((1,1),(2,2),(3,3))

List(1,2,3) fproduct {_*2}=> List((1,2), (2,4), (3,6))

Page 42: En nybegynners introduksjon til scalaz

© Mesan AS

Applicative

trait Applicative[F[_]] extends Apply[F] { self => def point[A](a: => A): F[A]}

trait Apply[F[_]] extends Functor[F] { self => def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B]}

trait ApplyOps[F[_],A] extends Ops[F[A]] { final def <*>[B](f: F[A => B]): F[B] = ??? final def |@|[B](fb: F[B]):ApplicativeBuilder = ???}

Page 43: En nybegynners introduksjon til scalaz

© Mesan AS

Applicative

val f = (_:Int) * 32.some <*> f.some=> 6.some

9.some <*> { (_: Int) + 3 }.some=> 12.some

^(1.some, 2.some) {_ + _}=> 3.some

(1.some |@| 2.some) {_ + _}=> 3.some

Page 44: En nybegynners introduksjon til scalaz

© Mesan AS

Applicative

val f: (Int, Int) => Int = (x:Int, y:Int) => x + y

f(1,2)=> 3

f(1.some, 2.some)=> 3.some

f(1.some, o.none[Int])=> o.none[Int]

Page 45: En nybegynners introduksjon til scalaz

© Mesan AS

Monadtrait BindOps[F[_],A] extends Ops[F[A]] { def flatMap[B](f: A => F[B]) = ??? def >>=[B](f: A => F[B]) = ??? def >>[B](b: => F[B]): F[B] = ???}

val f: Int => Option[Int] = (x:Int) => (x * 10).some(9.some >>= f)

(9.some flatMap f)

(for { x <- 9.some y <- f(x)} yield y)=> 90.some

(9.some >> 4.some)=> 4.some

Page 46: En nybegynners introduksjon til scalaz

© Mesan AS

Validation

endelig

Page 47: En nybegynners introduksjon til scalaz

© Mesan AS

Validation

sealed trait Validation[+E, +A]

case class Success[E, A](a: A)extends Validation[E, A]

case class Failure[E, A](e: E)extends Validation[E, A]

type ValidationNel[+E, +X] =Validation[NonEmptyList[E], X]

Page 48: En nybegynners introduksjon til scalaz

© Mesan AS

Validation

1.success[String]=> scalaz.Validation[String,Int] = Success(1)

1.successNel[String]=> scalaz.ValidationNel[String,Int] = Success(1)

1.successNel=> scalaz.ValidationNel[Nothing,Int] = Success(1)

Page 49: En nybegynners introduksjon til scalaz

© Mesan AS

Validation

"Too young!".failNel[Int]=> Failure(NonEmptyList("Too young!"))

"Too young!".fail[Int]=> Failure("Too young!")

"Too young!".fail=> Failure("Too young!")

Page 50: En nybegynners introduksjon til scalaz

© Mesan AS

Validationcase class User(username:String)

def addUser(username: String): ValidationNel[String, User] = { findUser(username) match { case Some(_) => s"Username '${username}' taken!".failNel case _ => User(username).success }}

def findUser(username: String): Option[User] = { (username === "ovstetun") ? User("ovstetun").some | none}

addUser("per")=> Success(User("per"))

addUser("ovstetun")=> Failure(NonEmptyList("Username 'ovstetun' taken!"))

Page 51: En nybegynners introduksjon til scalaz

© Mesan AS

Validation

val e: Equal[Validation[String, Int]] = Equal[Validation[String, Int]]

val sg: Semigroup[Validation[String, Int]] = Semigroup[Validation[String, Int]]

val m: Monoid[Validation[String, Int]] = Monoid[Validation[String, Int]]

val a: Applicative[({type l[a] = Validation[String, a]})#l] = Validation.ValidationApplicative[String]

val t: Traverse[({type l[a] = Validation[String, a]})#l] = Traverse[({type l[a] = Validation[String, a]})#l]

Page 52: En nybegynners introduksjon til scalaz

© Mesan AS

Validation

case req @ POST(Path("/personz")) => { val x = Body.string(req) val p = fromJSON[Person](parse(x)) val id = p map personer.add

id match { case Success(i) => Created ~> Json("id"->i) case Failure(errors) => Forbidden ~> Json("errors" -> jsonErrors(errors).toList) }}

def add(p: Person): Int = ???

Page 53: En nybegynners introduksjon til scalaz

© Mesan AS

Konklusjoner

Page 54: En nybegynners introduksjon til scalaz

© Mesan AS

Konklusjoner•Scalaz er ikke farlig

•Gir en felles måte å løse like problemer

•Man trenger ikke skjønne hvordan et konsept er implementert for å bruke det

•Validation er killer feature for meg

•Gjenbrukbar, komponerbar og vedlikeholdbar kode

Page 55: En nybegynners introduksjon til scalaz

© Mesan AS

Referanser•Scalaz på github

•http://eed3si9n.com/learning-scalaz/

Page 56: En nybegynners introduksjon til scalaz

© Mesan AS

Q & A

Page 57: En nybegynners introduksjon til scalaz

© Mesan AS

Takk for meg!

[email protected]@ovstetun

github.com/ovstetun/beginner-scalaz