Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Error Handling Without Exceptions
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Ben Darfler
May 06, 2015
Programming
140
0
Share
Error Handling Without Exceptions
A quick jaunt through functional error handling patterns using Ruby
Ben Darfler
May 06, 2015
More Decks by Ben Darfler
See All by Ben Darfler
Self-healing Software
bdarfler
0
150
Microservice Architectures
bdarfler
0
100
dotfiles
bdarfler
0
120
Remote Work
bdarfler
0
210
Low Latency Systems: How do they do it?
bdarfler
0
87
Scala
bdarfler
0
150
HTTP/2
bdarfler
0
150
Rails Asset Pipeline and You
bdarfler
0
90
How to Win at the Internet
bdarfler
1
51
Other Decks in Programming
See All in Programming
書き換えて学ぶTemporal #fukts
pirosikick
2
370
KMP × Kotlin 2.3 - How Android Got Slower While iOS Builds Improved by 47%
rio432
0
170
[RubyKaigi 2026] Require Hooks
palkan
1
320
実践ハーネスエンジニアリング:ステアリングループを実例から読み解く / Practical Harness Engineering: Understanding Steering Loops Through Real-World Examples
nrslib
5
5.3k
継続的な負荷検証を目指して
pyama86
3
1.1k
PHPでローカル環境用のSSL/TLS証明書を発行することはできるのか? #phpconkagawa
akase244
0
360
Symfony AI in Action - SymfonyLive Berlin 2026
chr_hertel
1
140
Spec-Driven Development with AI Agents (Workshop, May 2026)
antonarhipov
3
350
なぜあなたのコードには「コシ」がないのか?〜AI時代に問う、最後まで美味しい設計と戦略〜 #phpconkagawa / phpconkagawa2026
shogogg
0
160
サークル参加から学ぶ、小さな事業の回し方
yuzneri
0
180
AI時代だからこそ「Bloc」を採用する価値があるのかもしれない
takuroabe
0
170
2026-04-15 Spring IO - I Can See Clearly Now
jonatan_ivanov
1
200
Featured
See All Featured
Accessibility Awareness
sabderemane
1
110
Typedesign – Prime Four
hannesfritz
42
3k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
440
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
260
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
210
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
330
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.9k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Odyssey Design
rkendrick25
PRO
2
610
Building a Scalable Design System with Sketch
lauravandoore
463
34k
RailsConf 2023
tenderlove
30
1.4k
Transcript
Error Handling Without Exceptions
A Guiding Example ['1','2','3'].map{ |x| Integer(x) }.reduce(:+)
A Guiding Example ['1','2','3'].map{ |x| Integer(x) }.reduce(:+) => 6
A Guiding Example ['1','2','3'].map{ |x| Integer(x) }.reduce(:+) => 6 ['1','2','3','zero'].map{
|x| Integer(x) }.reduce(:+)
A Guiding Example ['1','2','3'].map{ |x| Integer(x) }.reduce(:+) => 6 ['1','2','3','zero'].map{
|x| Integer(x) }.reduce(:+) ArgumentError: invalid value for Integer(): "zero"
“ Errors aren’t exceptions because they’re not exceptional - they’re
real possible outcomes with their own information - treat them as such - Some Guy I Know
Can We Do Better?
Can We Do Better? def safe_to_i(value) Integer(value) rescue nil end
Can We Do Better? def safe_to_i(value) Integer(value) rescue nil end
['1','2','3','zero'].map{ |x| safe_to_i(x) }
Can We Do Better? def safe_to_i(value) Integer(value) rescue nil end
['1','2','3','zero'].map{ |x| safe_to_i(x) } => [1, 2, 3, nil]
Can We Do Better? def safe_to_i(value) Integer(value) rescue nil end
['1','2','3','zero'].map{ |x| safe_to_i(x) } => [1, 2, 3, nil] ['1','2','3','zero'].map{ |x| safe_to_i(x) }.reduce(:+)
Can We Do Better? def safe_to_i(value) Integer(value) rescue nil end
['1','2','3','zero'].map{ |x| safe_to_i(x) } => [1, 2, 3, nil] ['1','2','3','zero'].map{ |x| safe_to_i(x) }.reduce(:+) TypeError: nil can't be coerced into Fixnum
Take Three
Take Three def safer_to_i(value) [Integer(value)] rescue [] end
Take Three def safer_to_i(value) [Integer(value)] rescue [] end ['1','2','3','zero'].map{ |x|
safer_to_i(x) }
Take Three def safer_to_i(value) [Integer(value)] rescue [] end ['1','2','3','zero'].map{ |x|
safer_to_i(x) } => [[1], [2], [3], []]
Take Three def safer_to_i(value) [Integer(value)] rescue [] end ['1','2','3','zero'].map{ |x|
safer_to_i(x) } => [[1], [2], [3], []] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.flatten
Take Three def safer_to_i(value) [Integer(value)] rescue [] end ['1','2','3','zero'].map{ |x|
safer_to_i(x) } => [[1], [2], [3], []] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.flatten => [1, 2, 3]
Take Three def safer_to_i(value) [Integer(value)] rescue [] end ['1','2','3','zero'].map{ |x|
safer_to_i(x) } => [[1], [2], [3], []] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.flatten => [1, 2, 3] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.flatten.reduce(:+)
Take Three def safer_to_i(value) [Integer(value)] rescue [] end ['1','2','3','zero'].map{ |x|
safer_to_i(x) } => [[1], [2], [3], []] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.flatten => [1, 2, 3] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.flatten.reduce(:+) => 6
But I Wanted To Fail!
But I Wanted To Fail! class Array def sequence self.reduce([[]]){
|a,b| a.flat_map{ |aa| b.map{ |bb| aa << bb }}} end end
But I Wanted To Fail! class Array def sequence self.reduce([[]]){
|a,b| a.flat_map{ |aa| b.map{ |bb| aa << bb }}} end end ['1','2','3'].map{ |x| safer_to_i(x) } => [[1], [2], [3]]
But I Wanted To Fail! class Array def sequence self.reduce([[]]){
|a,b| a.flat_map{ |aa| b.map{ |bb| aa << bb }}} end end ['1','2','3'].map{ |x| safer_to_i(x) } => [[1], [2], [3]] ['1','2','3'].map{ |x| safer_to_i(x) }.sequence
But I Wanted To Fail! class Array def sequence self.reduce([[]]){
|a,b| a.flat_map{ |aa| b.map{ |bb| aa << bb }}} end end ['1','2','3'].map{ |x| safer_to_i(x) } => [[1], [2], [3]] ['1','2','3'].map{ |x| safer_to_i(x) }.sequence => [[1, 2, 3]]
But I Wanted To Fail! class Array def sequence self.reduce([[]]){
|a,b| a.flat_map{ |aa| b.map{ |bb| aa << bb }}} end end ['1','2','3','zero'].map{ |x| safer_to_i(x) } => [[1], [2], [3], []]
But I Wanted To Fail! class Array def sequence self.reduce([[]]){
|a,b| a.flat_map{ |aa| b.map{ |bb| aa << bb }}} end end ['1','2','3','zero'].map{ |x| safer_to_i(x) } => [[1], [2], [3], []] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.sequence
But I Wanted To Fail! class Array def sequence self.reduce([[]]){
|a,b| a.flat_map{ |aa| b.map{ |bb| aa << bb }}} end end ['1','2','3','zero'].map{ |x| safer_to_i(x) } => [[1], [2], [3], []] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.sequence => []
But I Wanted To Fail! ['1','2','3'].map{ |x| safer_to_i(x) }.sequence =>
[[1, 2, 3]]
But I Wanted To Fail! ['1','2','3'].map{ |x| safer_to_i(x) }.sequence =>
[[1, 2, 3]] ['1','2','3'].map{ |x| safer_to_i(x) }.sequence.map{ |x| x.reduce(:+) }
But I Wanted To Fail! ['1','2','3'].map{ |x| safer_to_i(x) }.sequence =>
[[1, 2, 3]] ['1','2','3'].map{ |x| safer_to_i(x) }.sequence.map{ |x| x.reduce(:+) } => [6]
But I Wanted To Fail! ['1','2','3'].map{ |x| safer_to_i(x) }.sequence =>
[[1, 2, 3]] ['1','2','3'].map{ |x| safer_to_i(x) }.sequence.map{ |x| x.reduce(:+) } => [6] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.sequence
But I Wanted To Fail! ['1','2','3'].map{ |x| safer_to_i(x) }.sequence =>
[[1, 2, 3]] ['1','2','3'].map{ |x| safer_to_i(x) }.sequence.map{ |x| x.reduce(:+) } => [6] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.sequence => []
But I Wanted To Fail! ['1','2','3'].map{ |x| safer_to_i(x) }.sequence =>
[[1, 2, 3]] ['1','2','3'].map{ |x| safer_to_i(x) }.sequence.map{ |x| x.reduce(:+) } => [6] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.sequence => [] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.sequence .map{ |x| x.reduce(:+) }
But I Wanted To Fail! ['1','2','3'].map{ |x| safer_to_i(x) }.sequence =>
[[1, 2, 3]] ['1','2','3'].map{ |x| safer_to_i(x) }.sequence.map{ |x| x.reduce(:+) } => [6] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.sequence => [] ['1','2','3','zero'].map{ |x| safer_to_i(x) }.sequence .map{ |x| x.reduce(:+) } => []
Where To Next?
Where To Next? def saferToInt(str: String): Option[Int] = try {
Some(str.toInt) } catch { case e: Exception => None } class ListOps[A](list: List[Option[A]]) { def sequence[A](a: List[Option[A]]): Option[List[A]] = a.foldRight[Option[List[A]]](Some(Nil))((a,b)=>a.flatMap(aa=>b.map(bb=>aa::bb))) def sequence: Option[List[A]] = sequence(list) } implicit def listToListOps[A](list: List[Option[A]]) = new ListOps(list)
Where To Next? List("1","2","3").map(saferToInt) res12: List[Option[Int]] = List(Some(1), Some(2), Some(3))
List("1","2","3").map(saferToInt).sequence res13: Option[List[Int]] = Some(List(1, 2, 3)) List("1","2","3").map(saferToInt).sequence.map(_.reduce(_+_)) res14: Option[Int] = Some(6)
Where To Next? List("1","2","3","zero").map(saferToInt) res16: List[Option[Int]] = List(Some(1), Some(2), Some(3),
None) List("1","2","3","zero").map(saferToInt).sequence res17: Option[List[Int]] = None List("1","2","3","zero").map(saferToInt).sequence.map(_.reduce(_+_)) res18: Option[Int] = None
Where To Next? This all works because List and Option
are Monads