En nybegynners introduksjon til scalaz

557 views

Published on

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

Published in: Technology, Business
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
557
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
3
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide
  • én av MANGE mulig veier gjennom rammeverket
  • 13 års erfaring som java-utvikler -> konsulent siden 2005 Java, Scala, HTML/js
  • har mange ganger tenkt at “eg må se på scalaz” for det er kult -> men liksom aldri kommet i gang
  • Mye er endret i versjon 7. lite unicode-metodenavn strukturen er bedre og mer lagvis
  • Hva pushet meg over kanten? Laget en applikasjon med Unfiltered som et json-api Ikke et godt REST-api, så ingen kommentarer om det ;)
  • med json4s (eller lift-json) denne koden har (minst) ett problem - en ting vi ikke kontrollerer...
  • “ Spring+commons-lang måten” -> kaster IllegalArgumentException hvis brutt..
  • live demo i Postman!
  • Mange grunner til det... -> usynlige effekter i kode -> mangel på kontroll -> Runtime-exceptions
  • Uten å gå inn på HVORDAN (for det kommer vi tilbake til) så -> Peke på det mest interessante som skjer her! Result[A] -> enten Success MED objekt i -> eller Failure med feilmeldinger
  • Er eksplisitt på hvordan en entitet hentes ut fra json Valideringsregler legges på
  • live demo i Postman med scalaz og jsonscalaz!
  • MEN noe er magisk Signaturer med navn som Kleisli[EitherNel, A, V] type EitherNel[+a] hva ER dette?
  • Fra github. Dette sa ikke meg så mye...
  • Hvordan EG ser på scalaz IkkeTomListe (NonEmptyList) Trær \/ (høyrevridd Either) Memo -> Memoization av funksjoner
  • Typesikker equals!
  • Typesikker equals!
  • conjunction (AND) disjunction (OR) ternary option-syntaks
  • ?? -> omtrent som en “getOrElse” a |> f -> som en f(a) kan syntaktisk skrive i motsatt rekkefølge av normalt -> tenk “putt inn i funksjon”
  • Gjøre et eksempel som “javascript-aktig-truthy-ting” Implementeres som et trait Dette er måten det er implementert i scalaz, kan gjøres litt annerledes om man vil...
  • ønsker at 1.truthy => true
  • figur, skal si hva noen av disse gjør!
  • Skal si noe om disse...
  • Vi snakket om denne tidligere. Scalaz deler Typeklassen (som kan brukes direkte) Syntaksen den tilbyr i Ops-klassen Også en implisitt konvertering som legger på Ops
  • Representerer det å “logisk legge sammen to objekter” (å legge til i |+|-tilfellet) Ops[F] definerer bare “self: F” som en variabel i en wrappet klasse
  • husk tilbake til Option: ~-operator som gir Monoid[A].zero som resultat dersom tom!
  • Dette gjelder kanskje MEST for SemiGroup-delen, men det meste som er Monoid er også SG! Og det nøstes -> Tuppel av Option[A] er monoid hvis A er Monoid
  • Konsistent måte å legge ting sammen på - uansett hva det er
  • alt som kan mappes over... “ kjør funksjon på en verdi i en kontekst, og putt verdien inn i kontekst igjen”
  • Som functor -> men med en applikativ er også funksjonen i en kontekst!
  • AUTOMATISK lifting av funksjoner til applikative functorer der det trengs!
  • En Monad er noe man kan kalle flatMap på => altså kan brukes i for-comprehensions >>= er alias for flatMap -> fra Haskell >> er en shortcut som bytter ut
  • Hvordan vi lager Success-instanser. Merk at vi kan enten angi hvilken type, eller droppe det (og la det være en del av signaturen)
  • Hvordan vi lager Failure-instanser. Merk at vi kan enten angi hvilken type, eller droppe det (og la det være en del av signaturen)
  • Hvordan et API kan se ut med Validation
  • Det finnes typeklasseinstanser for Validation De fleste “låser” den ene typen Men det lar oss gjøre det vi gjorde i innledningen:
  • Mine konklusjoner rundt Scalaz -> dette gjelder for meg -> men alle bør gjøre opp sin egen mening i motsetning til det mange mener -> scalaz er ikke farlig Felles løsning -> selv om det ikke alltid er enkelt å se at problemet er det samme...
  • OG ta gjerne tak i meg på Mesan-standen i expo-området!
  • En nybegynners introduksjon til scalaz

    1. 1. © Mesan AS scalaz En nybegynners introduksjon
    2. 2. © Mesan AS Agenda • Bakgrunnen for at jeg har tatt i bruk scalaz • En vei gjennom rammeverket • Konklusjoner • Gjerne spørsmål underveis!
    3. 3. © Mesan AS Trond Marius Øvstetun • Sjesfkonsulent i Mesan • Utvikler, arkitekt, teamleder +++
    4. 4. © Mesan AS Mesan ... når standardsystemer ikke er nok •Systemutvikling – skreddersøm •Løsningsfokus •Transaksjonssystemer •Lang levetid – langsiktighet og vedlikeholdbarhet
    5. 5. © Mesan AS Hvorfor scalaz?
    6. 6. © 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 <:::, :::>, <+>, |@|, , , <=< ?★ ☆
    7. 7. © Mesan AS Mitt triggerpunkt
    8. 8. © 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)
    9. 9. © 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
    10. 10. © 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) }
    11. 11. © Mesan AS Fra en klients perspektiv...
    12. 12. © Mesan AS Fra en utviklers perspektiv Dette føles ikke bra!
    13. 13. © 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) } }
    14. 14. © 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))
    15. 15. © Mesan AS Fra en klients perspektiv...
    16. 16. © Mesan AS Fra en utviklers perspektiv Mye bedre?! Men?! >==> fromJSON[Person] Kleisli[Result, JValue, A] type EitherNel[+a] = NonEmptyList[Error] / a
    17. 17. © Mesan AS scalaz import scalaz._ import Scalaz._
    18. 18. © 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.
    19. 19. © Mesan AS Hva er scalaz? 1.Funksjonelle datastrukturer 2.Syntaks for utvidelse av standard klasser 3.Typeklasser og instanser for disse
    20. 20. © Mesan AS Syntaks utvidelser til standard klasser og til hvordan du skriver kode
    21. 21. © Mesan AS Equal 1 == 1 => true 1 == 1.0 => true val x: Int = 1 val 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
    22. 22. © 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
    23. 23. © 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])
    24. 24. © 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.some o getOrElse 1 o | 1 => 42 val n = option.none[Int] n getOrElse 1 n | 1 => 1 ~o => 42 ~n => 0
    25. 25. © Mesan AS Index val l = List(1,2,3,4) l(0) => 1 l(2) => 3 l(-1) => IndexOutOfBoundsException l(4) => IndexOutOfBoundsException l index 0 => Some(1) l index 2 => Some(3) l index -1 => None l index 4 => None
    26. 26. © Mesan AS Alle instanser - Id "a" ?? "b" => "a" val x: String = null x ?? "asdf" => "asdf" def f(i:Int) = i + 1 (1 |> f) => 2 1 + 2 + 3 |> {_ * 6} => 36
    27. 27. © Mesan AS Typeklasser
    28. 28. © 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
    29. 29. © 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) => true intTruthy.truthy(0) => false
    30. 30. © Mesan AS Typeklasser i Scala object Truthy { def apply[A](implicit A: Truthy[A]): Truthy[A] = A } > Truthy[Int].truthy(0) => false Truthy[Int].truthy(1) => true 1.truthy => true
    31. 31. © 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
    32. 32. © 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!
    33. 33. © Mesan AS Typeklasser i Scalaz
    34. 34. © Mesan AS Typeklasser i Scalaz
    35. 35. © Mesan AS Equal trait 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
    36. 36. © 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
    37. 37. © 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)
    38. 38. © 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
    39. 39. © 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)
    40. 40. © 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)
    41. 41. © 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))
    42. 42. © 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= ??? }
    43. 43. © Mesan AS Applicative val f = (_:Int) * 3 2.some <*> f.some => 6.some 9.some <*> { (_: Int) + 3 }.some => 12.some ^(1.some, 2.some) {_ + _} => 3.some (1.some |@| 2.some) {_ + _} => 3.some
    44. 44. © 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]
    45. 45. © Mesan AS Monad trait 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
    46. 46. © Mesan AS Validation endelig
    47. 47. © 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]
    48. 48. © 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)
    49. 49. © Mesan AS Validation "Too young!".failNel[Int] => Failure(NonEmptyList("Too young!")) "Too young!".fail[Int] => Failure("Too young!") "Too young!".fail => Failure("Too young!")
    50. 50. © Mesan AS Validation case 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!"))
    51. 51. © 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]
    52. 52. © 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 = ???
    53. 53. © Mesan AS Konklusjoner
    54. 54. © 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
    55. 55. © Mesan AS Referanser • Scalaz på github • http://eed3si9n.com/learning-scalaz/
    56. 56. © Mesan AS Q & A
    57. 57. © Mesan AS Takk for meg! tmo@mesan.no @ovstetun github.com/ovstetun/beginner-scalaz

    ×