Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PNWScala 2014: A Skeptic's Look at scalaz's "Gateway Drugs"

PNWScala 2014: A Skeptic's Look at scalaz's "Gateway Drugs"

"We've all seen them on the corner of our local software development neighborhoods: FP purists, shamelessly peddling scalaz to unsuspecting developers. Lured in by promises of Free Monoids, Semigroups, and Endofunctors these developers soon seem lost in throes of ecstatic coding. "

To the skeptical and stubbornly practical among us, the above might ring a little true – especially if read in Rob Serling's voice. Images of gibbering horrors lurking in the depths of mathematical perfection swim before our eyes.

But what if there is true value in the world of scalaz? What if it is possible to use these tools for good (and a little bit of evil – it's fun to use learning for evil!) and profit... Without getting hopelessly lost in the opium dens of FP?

In this talk we will look at some of the "gateway drugs" of scalaz: Validation, NonEmptyList, \/, OptionT, and more. How do they work from a practical standpoint? What is their value for real world applications? And just how fun is it to *really* code with these tools?

Brendan McAdams

November 14, 2014
Tweet

More Decks by Brendan McAdams

Other Decks in Technology

Transcript

  1. scalaz is kind of awesome... • I've been playing with

    scalaz a bit lately • There's a lot in there, and a lot to absorb • I don't understand all of it, and am not an expert • But I'm making it valuable for myself and – by extension – my team
  2. Scala's Either: The limitations • Scala's builtin Either is a

    commonly used tool, allowing Left and Right Projections • By convention, Left indicates an error while Right indicates a success • Good concept, mediocre interaction
  3. The Problem with Either scala> val success = Right("Success!") success:

    scala.util.Right[Nothing,String] = Right(Success!) scala> success.isRight res2: Boolean = true scala> success.isLeft res3: Boolean = false scala> for { | x <- success | } yield x <console>:10: error: value map is not a member of scala.util.Right[Nothing,String] x <- success ^ • Not a Monad. Pain in the ass to extract.
  4. Disjunctions \/ as an Alternative • scalaz \/ (aka Disjunction)

    assumes we mostly want the right (success) value • "Right Bias"
  5. import scalaz._ import Scalaz._ scala> "Success!".right res7: scalaz.\/[Nothing,String] = \/-(Success!)

    scala> "Failure!".left res8: scalaz.\/[String,Nothing] = -\/(Failure!) scala> -\/("Failure!") res9: scalaz.-\/[String] = -\/(Failure!) scala> \/.left("Failure!") res10: scalaz.\/[String,Nothing] = -\/(Failure!) scala> \/-("Success!") res11: scalaz.\/-[String] = \/-(Success!) scala> \/.right("Success!") res12: scalaz.\/[Nothing,String] = \/-(Success!)
  6. Digression: Scala Option • Scala Option is a commonly used

    container, having a None and a Some subtype • Like \/ it also has a bias: towards a value: Some • Comprehension over it has issues with "undiagnosed aborts"
  7. case class User(first: String, last: String) case class DBObject(id: Long,

    user: Option[User]) val brendan = Some(DBObject(1, Some(User("Brendan", "McAdams")))) val someOtherGuy = Some(DBObject(2, None)) def getB = brendan def getOther = someOtherGuy scala> for { | dao <- getB | user <- dao.user | } yield user res13: Option[User] = Some(User(Brendan,McAdams)) scala> for { | dao <- getOther | user <- dao.user | } yield user res14: Option[User] = None • What went wrong?
  8. \/ To the Rescue • I'm going to combine two

    things here ... • Converting Option to \/ • Comprehending over \/s
  9. scala> for { | dao <- getB \/> "No user

    by that ID" | user <- dao.user \/> "Join failed: no user object" | } yield user res15: scalaz.\/[String,User] = \/-(User(Brendan,McAdams)) scala> for { | dao <- getOther \/> "No user by that ID" | user <- dao.user \/> "Join failed: no user object" | } yield user res16: scalaz.\/[String,User] = -\/(Join failed: no user object) scala> None \/> "No object found" res17: scalaz.\/[String,Nothing] = -\/(No object found) scala> None toRightDisjunction "No object found" res19: scalaz.\/[String,Nothing] = -\/(No object found)
  10. Validation • Validation looks similar to \/ (and you can

    convert between them) • Subtypes success and failure • Validation however is not a monad (despite some 6.x examples that show it as one...) • Validation is an applicative functor: Validates all events instead of short circuiting • If any failure in the chain, failure wins: All errors get mashed together
  11. (mg.toValidationNel |@| fv.toValidationNel) { case (modelGroup, firmwareVersion) => mgInfo.deviceModelIds.foreach {

    dmId => ModelCertManager.create(dmId, mgInfo.firmwareVersionId, Some("Full Cert"), "None", user, Some(mgInfo.certDate)) } ModelGroupCreationResult(modelGroup, firmwareVersion) }.disjunction
  12. // Or... ignore the success and just aggregate failure validateModelsNEL(request)

    *> ModelGroupManager.szValidateModelGroupName( request.modelGroupName) *> DeviceModelManager.szValidateModelNames( request.modelNames) *> ModelGroupManager.szValidateUniqueDeviceModelNames( request.modelNames) *> ModelGroupManager.validateSerialNumber(request.serialNumber)
  13. Good Testing Matters • This should go without saying, but

    good tests matter quite a lot • I'm building some cool tools for Scalatest + Scalaz • Will release on of these days...
  14. Good Testing Matters • We use and love ScalaTest <scalatest.org>

    • Specs2 <specs2.org> is pretty awesome as well and has helpers for scalaz at typelevel.org
  15. Resources for Learning More • Eugene Yokata's 'Learning Scalaz' tutorial

    (Linked up to 'Learn You a Haskell For Great Good' - http:// eed3si9n.com/learning-scalaz/ • #scalaz Freenode • Thanks to Rob Norris (@tpolecat), Colt Frederickson (@coltfred), Adelbert Chang (@adelbertchang), Miles Sabin (@milessabin) et al