SlideShare a Scribd company logo
FUNCTIONALFUNCTIONAL
ERROR HANDLINGERROR HANDLING
WITH CATSWITH CATS
MARK CANLASMARK CANLAS ·· NE SCALA 2020NE SCALA 2020
Functional Error Handling with Cats
Functional Error Handling with Cats
Functional Error Handling with Cats
OPTIONOPTION[_][_]
TRYTRY[_][_]
EITHEREITHER[_, _][_, _]
FUNCTORFUNCTOR[_][_]
APPLICATIVEAPPLICATIVE[_][_]
MONADMONAD[_][_]
ERRORS ASERRORS AS DATADATA
Functional Error Handling with Cats
Functional Error Handling with Cats
PARSING STRINGSPARSING STRINGS
case class AncientRecord(
age: String,
name: String,
rateOfIncome: String
)
case class Citizen(
age: Int,
name: String,
rateOfIncome: Double
)
def parseRecord(rec: AncientRecord): Citizen
Functional Error Handling with Cats
PARSING FUNCTIONSPARSING FUNCTIONS
def parseAge(s: String)
def parseName(s: String)
def parseRate(s: String)
Functional Error Handling with Cats
AA =>  => FF[[BB]]
case class AncientRecord(
age: String,
name: String,
rateOfIncome: String
)
case class Citizen(
age: Int,
name: String,
rateOfIncome: Double
)
// fate of record not guaranteed, via F
def parseRecord(rec: AncientRecord): F[Citizen]
OptionOption[_][_]
def parseAge(s: String): Option[Int]
def parseName(s: String): Option[String]
def parseRate(s: String): Option[Double]
FOR COMPREHENSIONFOR COMPREHENSION
def parseRecord(rec: AncientRecord): Option[Citizen] =
for {
age <- parseAge(rec.age)
name <- parseName(rec.name)
rate <- parseRate(rec.rate)
} yield Citizen(age, name, rate)
PARSING WITH OPTIONPARSING WITH OPTION
def parseRecord(rec: AncientRecord): Option[Citizen] =
for {
age <- parseAge(rec.age) // Some[Int]
name <- parseName(rec.name) // Some[String]
rate <- parseRate(rec.rate) // Some[Double]
} yield
Citizen(age, name, rate) // Some[Citizen]
NOT ALL AVAILABLENOT ALL AVAILABLE
def parseRecord(rec: AncientRecord): Option[Citizen] =
for {
age <- parseAge(rec.age) // Some[Int]
name <- parseName(rec.name) // None
rate <- parseRate(rec.rate) // (does not run)
} yield
Citizen(age, name, rate) // None
FIRST ONE WINSFIRST ONE WINS
FIRST ERROR WINSFIRST ERROR WINS
Functional Error Handling with Cats
Functional Error Handling with Cats
Functional Error Handling with Cats
TryTry[_][_]
def parseAge(s: String): Try[Int]
def parseName(s: String): Try[String]
def parseRate(s: String): Try[Double]
PARSING WITH TRYPARSING WITH TRY
def parseRecord(rec: AncientRecord): Try[Citizen] =
for {
age <- parseAge(rec.age) // Success[Int]
name <- parseName(rec.name) // Failure[String]
rate <- parseRate(rec.rate) // (does not run)
} yield
Citizen(age, name, rate) // Failure[Citizen]
Functional Error Handling with Cats
Name Happy path Sad path
Option[_] Some[_] None
Try[_] Success[_] Failure[_]
Functional Error Handling with Cats
Functional Error Handling with Cats
ERROR ADTERROR ADT
sealed trait ParsingErr
case class UnreadableAge (s: String) extends ParsingErr
case class UnreadableName(s: String) extends ParsingErr
case class UnreadableRate(s: String) extends ParsingErr
Functional Error Handling with Cats
EitherEither[_, _][_, _]
def parseAge(s: String): Either[UnreadableAge, Int]
def parseName(s: String): Either[UnreadableName, String]
def parseRate(s: String): Either[UnreadableRate, Double]
PARSING WITH EITHERPARSING WITH EITHER
def parseRecord(rec: AncientRecord): Either[ParsingErr, Citizen] =
for {
age <- parseAge(rec.age) // Right[UnreadableAge, Int]
name <- parseName(rec.name) // Left[UnreadableName, String]
rate <- parseRate(rec.rate) // (does not run)
} yield
Citizen(age, name, rate) // Left[ParsingErr, Citizen]
Functional Error Handling with Cats
Name Happy path Sad path
Option[_] Some[_] None
Try[_] Success[_] Failure[_]
Either[E, _] Right[E, _] Left[E, _]
Functional Error Handling with Cats
def parseRecord(rec: AncientRecord): Either[ParsingErr, Citizen]
def parseRecord(rec: AncientRecord): Either[ParsingErr, Citizen]
sealed trait HttpResponse
case class HttpSucccess (s: String) extends HttpResponse
case class HttpClientError(s: String) extends HttpResponse
def present(res: Either[ParsingErr, Citizen]): HttpResponse
def parseRecord(rec: AncientRecord): Either[ParsingErr, Citizen]
sealed trait HttpResponse
case class HttpSucccess (s: String) extends HttpResponse
case class HttpClientError(s: String) extends HttpResponse
def present(res: Either[ParsingErr, Citizen]): HttpResponse
def httpError(err: ParsingError): HttpClientError
def httpSuccess(c: Citizen): HttpSucccess
USINGUSING .fold().fold()
val res: Either[ParsingErr, Citizen] = ???
val httpResponse: HttpResponse =
res
.fold(httpError, httpSuccess)
USINGUSING .leftMap().leftMap()
val res: Either[ParsingErr, Citizen] = ???
val httpResponse: HttpResponse =
res // Either[ParsingErr, Citizen]
.map(httpSuccess) // Either[ParsingErr, HttpSucccess]
.leftMap(httpError) // Either[HttpClientError, HttpSucccess]
.merge // HttpResponse
EitherEither[[EE, , AA]] BIFUNCTORBIFUNCTOR
Can map over E
Can map over A
POSTFIX SYNTAXPOSTFIX SYNTAX
import cats.implicits._
def luckyNumber: Either[String, Int] =
if (Random.nextBoolean)
42.asRight
else
"You are unlucky.".asLeft
Functional Error Handling with Cats
Functional Error Handling with Cats
IOIO[_][_]
def parseAge(s: String): IO[Int]
def parseName(s: String): IO[String]
def parseRate(s: String): IO[Double]
PARSING WITH IOPARSING WITH IO
def parseRecord(rec: AncientRecord): IO[Citizen] =
for {
age <- parseAge(rec.age) // successful
name <- parseName(rec.name) // error "raised"
rate <- parseRate(rec.rate) // (does not run)
} yield
Citizen(age, name, rate) // error raised in step 2
Name Happy path Sad path
Option[_] Some[_] None
Try[_] Success[_] Failure[_]
Either[E, _] Right[E, _] Left[E, _]
IO[_] IO[_] IO[_]
Functional Error Handling with Cats
MONAD TYPECLASSMONAD TYPECLASS
import cats._
import cats.effect._
import scala.util._
type EitherParsingErr[A] = Either[ParsingErr, A]
implicitly[Monad[Option]]
implicitly[Monad[Try]]
implicitly[Monad[EitherParsingErr]]
implicitly[Monad[IO]]
ERROR CHANNELSERROR CHANNELS
F[A] shape Payload Error channel
Try[A] A Throwable
IO[A] A Throwable
Either[E, A] A E
MONADERROR TYPECLASSMONADERROR TYPECLASS
import cats._
import cats.effect._
import scala.util._
type EitherParsingErr[A] = Either[ParsingErr, A]
implicitly[MonadError[Try, Throwable]]
implicitly[MonadError[IO, Throwable]]
implicitly[MonadError[EitherParsingErr, ParsingErr]]
TYPECLASS TABLETYPECLASS TABLE
Functor[_]  
Applicative[_]  
Monad[_] MonadError[_, _]
Functional Error Handling with Cats
Functional Error Handling with Cats
Functional Error Handling with Cats
PARSING WITH EITHERPARSING WITH EITHER
def parseRecord(rec: AncientRecord): Either[ParsingErr, Citizen] =
for {
age <- parseAge(rec.age) // Left[ParsingErr, Int]
name <- parseName(rec.name) // (does not run)
rate <- parseRate(rec.rate) // (does not run)
} yield
Citizen(age, name, rate) // only reports first error
GIVEN:GIVEN:
EitherEither[[EE, , AA]]
GIVEN:GIVEN:
EitherEither[[EE, , AA]]
WANT:WANT:
EitherEither[[ListList[[EE], ], AA]]
MONADS START WITH:MONADS START WITH:
EitherEither[[EE, , AA]]
MONADS START WITH:MONADS START WITH:
EitherEither[[EE, , AA]]
MONADS END WITH:MONADS END WITH:
EitherEither[[EE, , AA]]
Functional Error Handling with Cats
Functional Error Handling with Cats
ValidatedValidated[_, _][_, _]
ValidatedValidated[_, _][_, _]
def parseAge(s: String): Validated[UnreadableAge, Int]
def parseName(s: String): Validated[UnreadableName, String]
def parseRate(s: String): Validated[UnreadableRate, Double]
PARSING WITH VALIDATED???PARSING WITH VALIDATED???
def parseRecord(rec: AncientRecord): ???[Citizen] =
for {
age <- ??? // ???
name <- ??? // ???
rate <- ??? // ???
} yield
Citizen(age, name, rate) // ???
ACCUMULATIONACCUMULATION
def combine(
v1: Validated[ParsingErr, Int],
v2: Validated[ParsingErr, String]):
Validated[List[ParsingError], (Int, String)] =
(v1, v2) match {
case (Valid(n), Valid(s)) => Valid((n, s))
case (Invalid(err), Valid(s)) => Invalid(List(err))
case (Valid(n), Invalid(err)) => Invalid(List(err))
case (Invalid(e1), Invalid(e2)) => Invalid(List(e1, e2))
}
EitherEither[[EE, , AA]]
ValidatedValidated[[EE, , AA]]
VALIDATED AND EITHERVALIDATED AND EITHER
Used for capturing errors as data
"Exclusive disjunction"
(payload A or error E but never both)
Bifunctor over A and E
Name Happy path Sad path
Option[_] Some[_] None
Try[_] Success[_] Failure[_]
Either[E, _] Right[E, _] Left[E, _]
IO[_] IO[_] IO[_]
Validated[E, _] Valid[_] Invalid[E]
ERROR CHANNELSERROR CHANNELS
F[A] shape Payload Error channel
Try[A] A Throwable
IO[A] A Throwable
Either[E, A] A E
Validated[E, A] A E
APPLICATIVEERROR TYPECLASSAPPLICATIVEERROR TYPECLASS
import cats._
import cats.effect._
type EitherParsingErr[A] = Either[ParsingErr, A]
type ValidatedParsingErr[A] = Validated[ParsingErr, A]
implicitly[ApplicativeError[IO, Throwable]]
implicitly[ApplicativeError[EitherParsingErr, ParsingErr]]
implicitly[ApplicativeError[ValidatedParsingErr, ParsingErr]]
// does not compile
implicitly[MonadError[ValidatedParsingErr, ParsingErr]]
TYPECLASS TABLETYPECLASS TABLE
Functor[_]  
Applicative[_] ApplicativeError[_, _]
Monad[_] MonadError[_, _]
POSTFIX SYNTAXPOSTFIX SYNTAX
import cats.implicits._
def luckyNumber: Validated[String, Int] =
if (Random.nextBoolean)
42.valid
else
"You are unlucky.".invalid
Functional Error Handling with Cats
Functional Error Handling with Cats
ValidatedValidated[[EE, , AA]]
ValidatedValidated[[EE, , AA]]
ValidatedValidated[[EE, , AA]]
  
ValidatedValidated[[ListList[[EE], ], AA]]
EitherEither[[EE, , AA]]
EitherEither[[EE, , AA]]
EitherEither[[EE, , AA]]
  
EitherEither[[EE, , AA]]
ValidatedValidated[[ListList[[EE], ], AA]]
ValidatedValidated[[ListList[[EE], ], AA]]
ValidatedValidated[[ListList[[EE], ], AA]]
  
ValidatedValidated[[ListList[[EE], ], AA]]
ValidatedValidated[[ListList[_], _][_], _]
import cats.data._
def parseAge(s: String):
Validated[List[UnreadableAge], Int]
def parseName(s: String):
Validated[List[UnreadableName], String]
def parseRate(s: String):
Validated[List[UnreadableRate], Double]
ACCUMULATIONACCUMULATION
def combine(
v1: Validated[List[ParsingErr], Int],
v2: Validated[List[ParsingErr], String]):
Validated[List[ParsingError], (Int, String)] =
(v1, v2) match {
case (Valid(n), Valid(s)) => Valid((n, s))
case (Invalid(err), Valid(s)) => Invalid(err)
case (Valid(n), Invalid(err)) => Invalid(err)
case (Invalid(e1), Invalid(e2)) => Invalid(e1 ::: e2)
}
WITH SEMIGROUPWITH SEMIGROUP
def combine[F : Semigroup](
v1: Validated[F[ParsingErr], Int],
v2: Validated[F[ParsingErr], String]):
Validated[F[ParsingError], (Int, String)] =
(v1, v2) match {
case (Valid(n), Valid(s)) => Valid((n, s))
case (Invalid(err), Valid(s)) => Invalid(err)
case (Valid(n), Invalid(err)) => Invalid(err)
// powered by Semigroup[F]
case (Invalid(e1), Invalid(e2)) => Invalid(e1 |+| e2)
}
ValidatedValidated[[EE, , AA]]
ValidatedValidated[[FF[[EE], ], AA]]
FF :  : SemigroupSemigroup
TUPLE SYNTAXTUPLE SYNTAX
import cats._
import cats.data._
import cats.implicits._
val res: Validated[List[ParsingErr], Citizen] =
(
parseAge(rec.age),
parseName(rec.name),
parseRate(rec.rate)
)
.mapN(Citizen)
Functional Error Handling with Cats
USINGUSING .fold().fold()
val res: Validated[List[ParsingErr], Citizen] = ???
def httpErrors(errs: List[ParsingError]): HttpClientError
def httpSuccess(c: Citizen): HttpSucccess
val httpResponse: HttpResponse =
res
.fold(httpErrors, httpSuccess)
USINGUSING .leftMap().leftMap()
val res: Validated[List[ParsingErr], Citizen] = ???
def httpErrors(errs: List[ParsingError]): HttpClientError
def httpSuccess(c: Citizen): HttpSucccess
val httpResponse: HttpResponse =
res // Validated[List[ParsingErr], Citizen]
.map(httpSuccess) // Validated[List[ParsingErr], HttpSucccess]
.leftMap(httpErrors) // Validated[HttpClientError, HttpSucccess]
.merge // HttpResponse
Functional Error Handling with Cats
FIRST ERROR WINSFIRST ERROR WINS
ACCUMULATEDACCUMULATED
ERRORSERRORS
FIRST ERROR WINSFIRST ERROR WINS
FIRST ERROR WINSFIRST ERROR WINS
Modeling dependent validations
Unwrapping envelopes or decoding
FIRST ERROR WINSFIRST ERROR WINS
Modeling dependent validations
Unwrapping envelopes or decoding
Introducing gates/dependency onto independent
validations
Save on expensive computation
ACCUMULATED ERRORSACCUMULATED ERRORS
ACCUMULATED ERRORSACCUMULATED ERRORS
Modeling independent validations
Field-based structures (maps, records, objects)
ACCUMULATED ERRORSACCUMULATED ERRORS
Modeling independent validations
Field-based structures (maps, records, objects)
Minimize round-trips
ACCUMULATED ERRORSACCUMULATED ERRORS
Modeling independent validations
Field-based structures (maps, records, objects)
Minimize round-trips
Embracing parallelization
Functional Error Handling with Cats
Functional Error Handling with Cats
ListList[_][_]
import cats._
import cats.data._
import cats.implicits._
def parseAge(s: String):
Validated[List[UnreadableAge], Int]
def parseName(s: String):
Validated[List[UnreadableName], String]
def parseRate(s: String):
Validated[List[UnreadableRate], Double]
implicitly[Semigroup[List]]
NonEmptyListNonEmptyList[_][_]
import cats._
import cats.data._
import cats.implicits._
def parseAge(s: String):
Validated[NonEmptyList[UnreadableAge], Int]
def parseName(s: String):
Validated[NonEmptyList[UnreadableName], String]
def parseRate(s: String):
Validated[NonEmptyList[UnreadableRate], Double]
implicitly[Semigroup[NonEmptyList]]
POSTFIX SYNTAXPOSTFIX SYNTAX
import cats.data._
import cats.implicits._
def luckyNumber: Validated[NonEmptyList[String], Int] =
if (Random.nextBoolean)
42.validNel
else
"You are unlucky.".invalidNel
Functional Error Handling with Cats
APPENDINGAPPENDING
List(1, 2, 3) ++ List(4)
APPENDING FREQUENTLYAPPENDING FREQUENTLY
List(1, 2, 3) ++ List(4)
List(1, 2, 3, 4) ++ List(5)
List(1, 2, 3, 4, 5) ++ List(8, 9)
List(1, 2) ++ List(3, 4) ++ List(8, 9)
Functional Error Handling with Cats
LIST ACCUMULATIONLIST ACCUMULATION
// read from left to right
List(1, 2, 3, 4, 5) ++ List(8, 9)
LIST ACCUMULATION (A)LIST ACCUMULATION (A)
// via repeated traversals
List(1, 2, 3, 4, 5) ++ List(8, 9)
List(1, 2, 3, 4) ++ List(5, 8, 9)
List(1, 2, 3) ++ List(4, 5, 8, 9)
List(1, 2) ++ List(3, 4, 5, 8, 9)
List(1) ++ List(2, 3, 4, 5, 8, 9)
List(1, 2, 3, 4, 5, 8, 9)
LIST ACCUMULATION (B)LIST ACCUMULATION (B)
// via reverse list, built using fast prepend
List(1, 2, 3, 4) ++ ReverseList()
List(2, 3, 4) ++ ReverseList(1)
List(3, 4) ++ ReverseList(2, 1)
List(4) ++ ReverseList(3, 2, 1)
List() ++ ReverseList(4, 3, 2, 1)
SEQUENCESSEQUENCES
List(1, 2, 3, 4)
Seq(5)
Vector(6, 7, 8)
Nested(
List(9),
Array(10, 11, 12))
CHAINCHAINED TOGETHERED TOGETHER
Chain(
List(1, 2, 3, 4),
Seq(5),
Vector(6, 7, 8),
Chain(
List(9),
Array(10, 11, 12)))
ChainChain[_][_]
def parseAge(s: String):
Validated[Chain[UnreadableAge], Int]
def parseName(s: String):
Validated[Chain[UnreadableName], String]
def parseRate(s: String):
Validated[Chain[UnreadableRate], Double]
implicitly[Semigroup[Chain]]
NonEmptyChainNonEmptyChain[_][_]
def parseAge(s: String):
Validated[NonEmptyChain[UnreadableAge], Int]
def parseName(s: String):
Validated[NonEmptyChain[UnreadableName], String]
def parseRate(s: String):
Validated[NonEmptyChain[UnreadableRate], Double]
implicitly[Semigroup[NonEmptyChain]]
SEMIGROUPSEMIGROUP COLLECTIONSCOLLECTIONS
Name Provider Empty? Accumulation
performance
List Scala ✅ O(n^2)
NonEmptyList ❌ O(n^2)
Chain ✅ constant
NonEmptyChain ❌ constant
POSTFIX SYNTAXPOSTFIX SYNTAX
import cats.implicits._
def luckyNumber: Validated[NonEmptyChain[String], Int] =
if (Random.nextBoolean)
42.validNec
else
"You are unlucky.".invalidNec
Functional Error Handling with Cats
ALGEBRASALGEBRAS
trait ParserAlg[F[_]] {
def parseAge(s: String): F[Int]
def parseName(s: String): F[String]
def parseRate(s: String): F[Double]
}
TYPECLASS METHODSTYPECLASS METHODS
class BadParser[F[_]](implicit F: ApplicativeError[F, Throwable]) {
def parseAge(s: String): F[Int] =
// raised, not thrown
F.raiseError(new Exception("what a world"))
}
NESTEDNESTED FF ANDAND GG
trait FancyParserAlg[F[_], G[_]] {
def parseAge(s: String): F[G[Int]]
def parseName(s: String): F[G[String]]
def parseRate(s: String): F[G[Double]]
}
FF WITH TRANSFORMERWITH TRANSFORMER GTGT
trait FancyParserAlg[F[_]] {
def parseAge(s: String): GT[F, Int]
def parseName(s: String): GT[F, String]
def parseRate(s: String): GT[F, Double]
}
FURTHER READINGFURTHER READING
Contains Chain and Validated, from the Cats
microsite
Cats Data Types
JOBS.DISNEYCAREERS.COMJOBS.DISNEYCAREERS.COM
@@mark canlas nycmark canlas nyc

More Related Content

What's hot (20)

PDF
MongoDBăšäœçœźæƒ…ć ± ïœžćœ°ç†ç©șé–“ă‚€ăƒłăƒ‡ăƒƒă‚Żă‚čたçŽč介
Koji Iwazaki
 
PPT
Memory Optimization
Wei Lin
 
PDF
Python Functions Tutorial | Working With Functions In Python | Python Trainin...
Edureka!
 
PDF
Domain Modeling in a Functional World
Debasish Ghosh
 
PPT
cpp input & output system basics
gourav kottawar
 
PDF
Functional Domain Modeling - The ZIO 2 Way
Debasish Ghosh
 
PDF
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Jorge VĂĄsquez
 
PDF
Cassandra Introduction & Features
DataStax Academy
 
PPTX
Ngrx: Redux in angular
Saadnoor Salehin
 
PPTX
Running Free with the Monads
kenbot
 
PDF
Simplify CDC Pipeline with Spark Streaming SQL and Delta Lake
Databricks
 
PDF
Redis + Apache Spark = Swiss Army Knife Meets Kitchen Sink
Databricks
 
PDF
Zio in real world
Wiem Zine Elabidine
 
PPSX
Java annotations
FAROOK Samath
 
PDF
Refactoring Functional Type Classes
John De Goes
 
PDF
Exploring ZIO Prelude: The game changer for typeclasses in Scala
Jorge VĂĄsquez
 
PPTX
Java: Regular Expression
Masudul Haque
 
PDF
Workshop 21: React Router
Visual Engineering
 
PDF
PostgreSQL Extensions: A deeper look
Jignesh Shah
 
MongoDBăšäœçœźæƒ…ć ± ïœžćœ°ç†ç©șé–“ă‚€ăƒłăƒ‡ăƒƒă‚Żă‚čたçŽč介
Koji Iwazaki
 
Memory Optimization
Wei Lin
 
Python Functions Tutorial | Working With Functions In Python | Python Trainin...
Edureka!
 
Domain Modeling in a Functional World
Debasish Ghosh
 
cpp input & output system basics
gourav kottawar
 
Functional Domain Modeling - The ZIO 2 Way
Debasish Ghosh
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Jorge VĂĄsquez
 
Cassandra Introduction & Features
DataStax Academy
 
Ngrx: Redux in angular
Saadnoor Salehin
 
Running Free with the Monads
kenbot
 
Simplify CDC Pipeline with Spark Streaming SQL and Delta Lake
Databricks
 
Redis + Apache Spark = Swiss Army Knife Meets Kitchen Sink
Databricks
 
Zio in real world
Wiem Zine Elabidine
 
Java annotations
FAROOK Samath
 
Refactoring Functional Type Classes
John De Goes
 
Exploring ZIO Prelude: The game changer for typeclasses in Scala
Jorge VĂĄsquez
 
Java: Regular Expression
Masudul Haque
 
Workshop 21: React Router
Visual Engineering
 
PostgreSQL Extensions: A deeper look
Jignesh Shah
 

Similar to Functional Error Handling with Cats (20)

PDF
Pragmatic Real-World Scala
parag978978
 
PDF
Pragmatic Real-World Scala (short version)
Jonas Bonér
 
ODP
Beginning Scala Svcc 2009
David Pollak
 
KEY
Achieving Parsing Sanity In Erlang
Sean Cribbs
 
PDF
More expressive types for spark with frameless
Miguel Pérez Pasalodos
 
PDF
C# Cheat Sheet
GlowTouch
 
PPTX
Introduction to R
Sander Kieft
 
PPTX
Graph Database Query Languages
Jay Coskey
 
PDF
Useful javascript
Lei Kang
 
PPTX
Plc (1)
James Croft
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
ZIP
Round PEG, Round Hole - Parsing Functionally
Sean Cribbs
 
PDF
An overview of Python 2.7
decoupled
 
PDF
A tour of Python
Aleksandar Veselinovic
 
PPTX
Python 101++: Let's Get Down to Business!
Paige Bailey
 
PDF
An introduction to property-based testing
Vincent Pradeilles
 
PDF
ScalaBlitz
Aleksandar Prokopec
 
Pragmatic Real-World Scala
parag978978
 
Pragmatic Real-World Scala (short version)
Jonas Bonér
 
Beginning Scala Svcc 2009
David Pollak
 
Achieving Parsing Sanity In Erlang
Sean Cribbs
 
More expressive types for spark with frameless
Miguel Pérez Pasalodos
 
C# Cheat Sheet
GlowTouch
 
Introduction to R
Sander Kieft
 
Graph Database Query Languages
Jay Coskey
 
Useful javascript
Lei Kang
 
Plc (1)
James Croft
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono
 
Round PEG, Round Hole - Parsing Functionally
Sean Cribbs
 
An overview of Python 2.7
decoupled
 
A tour of Python
Aleksandar Veselinovic
 
Python 101++: Let's Get Down to Business!
Paige Bailey
 
An introduction to property-based testing
Vincent Pradeilles
 
ScalaBlitz
Aleksandar Prokopec
 
Ad

Recently uploaded (20)

PDF
Thread In Android-Mastering Concurrency for Responsive Apps.pdf
Nabin Dhakal
 
PDF
Alexander Marshalov - How to use AI Assistants with your Monitoring system Q2...
VictoriaMetrics
 
PDF
iTop VPN With Crack Lifetime Activation Key-CODE
utfefguu
 
PDF
Generic or Specific? Making sensible software design decisions
Bert Jan Schrijver
 
PPTX
Hardware(Central Processing Unit ) CU and ALU
RizwanaKalsoom2
 
PPTX
Home Care Tools: Benefits, features and more
Third Rock Techkno
 
PDF
Build It, Buy It, or Already Got It? Make Smarter Martech Decisions
bbedford2
 
PDF
Automate Cybersecurity Tasks with Python
VICTOR MAESTRE RAMIREZ
 
PDF
Unlock Efficiency with Insurance Policy Administration Systems
Insurance Tech Services
 
PDF
Linux Certificate of Completion - LabEx Certificate
VICTOR MAESTRE RAMIREZ
 
PDF
IDM Crack with Internet Download Manager 6.42 Build 43 with Patch Latest 2025
bashirkhan333g
 
PDF
vMix Pro 28.0.0.42 Download vMix Registration key Bundle
kulindacore
 
PDF
4K Video Downloader Plus Pro Crack for MacOS New Download 2025
bashirkhan333g
 
PPTX
Agentic Automation: Build & Deploy Your First UiPath Agent
klpathrudu
 
PPTX
AEM User Group: India Chapter Kickoff Meeting
jennaf3
 
PDF
Online Queue Management System for Public Service Offices in Nepal [Focused i...
Rishab Acharya
 
PDF
The 5 Reasons for IT Maintenance - Arna Softech
Arna Softech
 
PDF
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
PDF
Digger Solo: Semantic search and maps for your local files
seanpedersen96
 
PPTX
Tally software_Introduction_Presentation
AditiBansal54083
 
Thread In Android-Mastering Concurrency for Responsive Apps.pdf
Nabin Dhakal
 
Alexander Marshalov - How to use AI Assistants with your Monitoring system Q2...
VictoriaMetrics
 
iTop VPN With Crack Lifetime Activation Key-CODE
utfefguu
 
Generic or Specific? Making sensible software design decisions
Bert Jan Schrijver
 
Hardware(Central Processing Unit ) CU and ALU
RizwanaKalsoom2
 
Home Care Tools: Benefits, features and more
Third Rock Techkno
 
Build It, Buy It, or Already Got It? Make Smarter Martech Decisions
bbedford2
 
Automate Cybersecurity Tasks with Python
VICTOR MAESTRE RAMIREZ
 
Unlock Efficiency with Insurance Policy Administration Systems
Insurance Tech Services
 
Linux Certificate of Completion - LabEx Certificate
VICTOR MAESTRE RAMIREZ
 
IDM Crack with Internet Download Manager 6.42 Build 43 with Patch Latest 2025
bashirkhan333g
 
vMix Pro 28.0.0.42 Download vMix Registration key Bundle
kulindacore
 
4K Video Downloader Plus Pro Crack for MacOS New Download 2025
bashirkhan333g
 
Agentic Automation: Build & Deploy Your First UiPath Agent
klpathrudu
 
AEM User Group: India Chapter Kickoff Meeting
jennaf3
 
Online Queue Management System for Public Service Offices in Nepal [Focused i...
Rishab Acharya
 
The 5 Reasons for IT Maintenance - Arna Softech
Arna Softech
 
Odoo CRM vs Zoho CRM: Honest Comparison 2025
Odiware Technologies Private Limited
 
Digger Solo: Semantic search and maps for your local files
seanpedersen96
 
Tally software_Introduction_Presentation
AditiBansal54083
 
Ad

Functional Error Handling with Cats