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

PDF
non-strict functions, bottom and scala by-name parameters
PPTX
Taking your side effects aside
PDF
Sequence and Traverse - Part 1
PDF
Functional Programming 101 with Scala and ZIO @FunctionalWorld
PDF
ZIO-Direct - Functional Scala 2022
PDF
Why The Free Monad isn't Free
PDF
Sequence and Traverse - Part 2
PPTX
Capabilities for Resources and Effects
non-strict functions, bottom and scala by-name parameters
Taking your side effects aside
Sequence and Traverse - Part 1
Functional Programming 101 with Scala and ZIO @FunctionalWorld
ZIO-Direct - Functional Scala 2022
Why The Free Monad isn't Free
Sequence and Traverse - Part 2
Capabilities for Resources and Effects

What's hot (20)

PDF
Ad hoc Polymorphism using Type Classes and Cats
PDF
ZIO Queue
PDF
Exploring ZIO Prelude: The game changer for typeclasses in Scala
PDF
Monoids - Part 1 - with examples using Scalaz and Cats
PDF
Zio in real world
PDF
The Functional Programming Triad of Map, Filter and Fold
PDF
Error Management: Future vs ZIO
PDF
Applicative Functor
PDF
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
PDF
Preparing for Scala 3
PDF
Java Collections Tutorials
PPTX
DS Module 1.pptx
PPTX
String Builder & String Buffer (Java Programming)
PPT
Ch 2 lattice & boolean algebra
PPTX
Function in Mathematics
PPSX
Collections - Maps
PPTX
Dictionary in python
PDF
Python Unit 3 - Control Flow and Functions
PPTX
Purely Functional Data Structures in Scala
PPT
Propositional Logic and Pridicate logic
Ad hoc Polymorphism using Type Classes and Cats
ZIO Queue
Exploring ZIO Prelude: The game changer for typeclasses in Scala
Monoids - Part 1 - with examples using Scalaz and Cats
Zio in real world
The Functional Programming Triad of Map, Filter and Fold
Error Management: Future vs ZIO
Applicative Functor
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Preparing for Scala 3
Java Collections Tutorials
DS Module 1.pptx
String Builder & String Buffer (Java Programming)
Ch 2 lattice & boolean algebra
Function in Mathematics
Collections - Maps
Dictionary in python
Python Unit 3 - Control Flow and Functions
Purely Functional Data Structures in Scala
Propositional Logic and Pridicate logic
Ad

Similar to Functional Error Handling with Cats (20)

PDF
Pragmatic Real-World Scala (short version)
PDF
Pragmatic Real-World Scala
ODP
Beginning Scala Svcc 2009
KEY
Achieving Parsing Sanity In Erlang
PDF
More expressive types for spark with frameless
PDF
C# Cheat Sheet
PPTX
Introduction to R
PPTX
Graph Database Query Languages
PDF
Useful javascript
PPTX
Plc (1)
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
ZIP
Round PEG, Round Hole - Parsing Functionally
PDF
An overview of Python 2.7
PDF
A tour of Python
PPTX
Python 101++: Let's Get Down to Business!
PDF
An introduction to property-based testing
PDF
Pragmatic Real-World Scala (short version)
Pragmatic Real-World Scala
Beginning Scala Svcc 2009
Achieving Parsing Sanity In Erlang
More expressive types for spark with frameless
C# Cheat Sheet
Introduction to R
Graph Database Query Languages
Useful javascript
Plc (1)
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Round PEG, Round Hole - Parsing Functionally
An overview of Python 2.7
A tour of Python
Python 101++: Let's Get Down to Business!
An introduction to property-based testing
Ad

Recently uploaded (20)

PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PDF
System and Network Administration Chapter 2
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PPTX
Essential Infomation Tech presentation.pptx
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PPTX
ai tools demonstartion for schools and inter college
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
How Creative Agencies Leverage Project Management Software.pdf
PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PPTX
history of c programming in notes for students .pptx
wealthsignaloriginal-com-DS-text-... (1).pdf
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
System and Network Administration Chapter 2
Softaken Excel to vCard Converter Software.pdf
Wondershare Filmora 15 Crack With Activation Key [2025
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Essential Infomation Tech presentation.pptx
Design an Analysis of Algorithms II-SECS-1021-03
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
CHAPTER 2 - PM Management and IT Context
Upgrade and Innovation Strategies for SAP ERP Customers
ai tools demonstartion for schools and inter college
Which alternative to Crystal Reports is best for small or large businesses.pdf
How to Choose the Right IT Partner for Your Business in Malaysia
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
How Creative Agencies Leverage Project Management Software.pdf
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
history of c programming in notes for students .pptx

Functional Error Handling with Cats