-
Notifications
You must be signed in to change notification settings - Fork 5
/
errors-and-exceptions.html
725 lines (587 loc) · 40.8 KB
/
errors-and-exceptions.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="keywords" content="异常处理,运行时,
Erlang, exception, error, exit, throw, try, catch, crash, runtime, process" />
<meta name="description" content="A list of compile-time errors and warnings, runtime errors in Erlang. Also errors, exits and throws then how to handle them with try ... catch and other constructs." />
<meta name="google-site-verification" content="mi1UCmFD_2pMLt2jsYHzi_0b6Go9xja8TGllOSoQPVU" />
<link rel="stylesheet" type="text/css" href="static/css/screen.css" media="screen" />
<link rel="stylesheet" type="text/css" href="static/css/sh/shCore.css" media="screen" />
<link rel="stylesheet" type="text/css" href="static/css/sh/shThemeLYSE2.css" media="screen" />
<link rel="stylesheet" type="text/css" href="static/css/print.css" media="print" />
<link href="rss" type="application/rss+xml" rel="alternate" title="LYSE news" />
<link rel="icon" type="image/png" href="favicon.ico" />
<link rel="apple-touch-icon" href="static/img/touch-icon-iphone.png" />
<link rel="apple-touch-icon" sizes="72x72" href="static/img/touch-icon-ipad.png" />
<link rel="apple-touch-icon" sizes="114x114" href="static/img/touch-icon-iphone4.png" />
<title>Errors and Exceptions | Learn You Some Erlang for Great Good!</title>
</head>
<body>
<div id="wrapper">
<div id="header">
<h1>Learn you some Erlang</h1>
<span>for great good!</span>
</div> <!-- header -->
<div id="menu">
<ul>
<li><a href="content.html" title="Home">Home</a></li>
<li><a href="faq.html" title="Frequently Asked Questions">FAQ</a></li>
<li><a href="rss" title="Latest News">RSS</a></li>
<li><a href="static/erlang/learn-you-some-erlang.zip" title="Source Code">Code</a></li>
</ul>
</div><!-- menu -->
<div id="content">
<div class="noscript"><noscript>Hey there, it appears your Javascript is disabled. That's fine, the site works without it. However, you might prefer reading it with syntax highlighting, which requires Javascript!</noscript></div>
<h2>错误和异常</h2>
<h3><a class="section" name="not-so-fast">不要那么着急!</a></h3>
<img class="left" src="static/img/cyclist.png" width="268" height="252" alt="A green man with a huge head and tiny body on a bicycle" title="I don't know why I drew that" />
<p>
没有什么好的地方可以放置这章这种内容。
到目前为止,你已经经历了很多执行异常的场景,但是你还不知道该怎么去处理它们。
事实上,在这个章节内,我们不会看到所有的异常处理的技术。
这是因为Erlang存在两种主要的范式:函数和并发。
关于的函数部分,我们从这本书的开头就已经在介绍了:
透明参考,递归,高阶函数等。
关于并发的部分,也是Erlang非常的出名的部分:Actor,成百上千的进程,监控树等。</p>
<p>
因为我认为应当在学习并发部分之前,先了解函数部分,所以在这章中,我们只包括函数部分的异常处理。
如果我们想非常好的处理异常,我们必须理解它们。
</p>
<br>
<br>
<br>
<div class="note">
<p><strong>注意:</strong>
尽管,Erlang在函数编程方便拥有一些异常处理的机制,
但是绝大部分时间,Erlang的想法是让我们就让它崩溃吧。
我以前曾经提到过这一点,请看<a class="chapter" href="introduction.html#what-is-erlang">介绍</a>这一章。
在Erlang的并发特性上,这种机制更为明显。
</p>
</div>
<h3><a class="section" name="a-compilation-of-errors">编译期间的错误</a></h3>
<p>
Erlang中有非常多种类的错误:编译错误,逻辑错误,运行时错误和普通错误。
现在我将重点介绍编译错误,在后面的章节中将介绍其它类型的错误。</p>
<p>
编译期间的错误,经常是语法性质的错误:
编译器会检查你的函数名,语法符号(大括号,小括号,句号,逗号),
包括函数的参数量等。
下面是一些常见的编译期间错误和可能解决方法:
</p>
<dl>
<dt>module.beam: Module name 'madule' does not match file name 'module'</dt>
<dd>The module name you've entered in the <code>-module</code> attribute doesn't match the filename.</dd>
<dt>./module.erl:2: Warning: function some_function/0 is unused</dt>
<dd>You have not exported a function, or the place where it's used has the wrong name or arity. It's also possible that you've written a function that is no longer needed. Check your code!</dd>
<dt>./module.erl:2: function some_function/1 undefined</dt>
<dd>The function does not exist. You've written the wrong name or arity either in the <code>-export</code> attribute or when declaring the function. This error is also output when the given function could not be compiled, usually because of a syntax error like forgetting to end a function with a period.</dd>
<dt>./module.erl:5: syntax error before: 'SomeCharacterOrWord'</dt>
<dd>This happens for a variety of reason, namely unclosed parentheses, tuples or wrong expression termination (like closing the last branch of a <code>case</code> with a comma). Other reasons might include the use of a reserved atom in your code or unicode characters getting weirdly converted between different encodings (I've seen it happen!)</dd>
<dt>./module.erl:5: syntax error before: </dt>
<dd>All right, that one is certainly not as descriptive! This usually comes up when your line termination is not correct. This is a specific case of the previous error, so just keep an eye out.</dd>
<dt>./module.erl:5: Warning: this expression will fail with a 'badarith' exception</dt>
<dd>Erlang is all about dynamic typing, but remember that the types are strong. In this case, the compiler is smart enough to find that one of your arithmetic expressions will fail (say, <code>llama + 5</code>). It won't find type errors much more complex than that, though.</dd>
<dt>./module.erl:5: Warning: variable 'Var' is unused</dt>
<dd>You declared a variable and never use it afterwards. This might be a bug with your code, so double-check what you have written. Otherwise, you might want to switch the variable name to <code>_</code> or just prefix it with an underscore (something like <var>_Var</var>) if you feel the name helps make the code readable.</dd>
<dt>./module.erl:5: Warning: a term is constructed, but never used</dt>
<dd>In one of your functions, you're doing something such as building a list, declaring a tuple or an anonymous function without ever binding it to a variable or returning it. This warning tells you you're doing something useless or that you have made some mistake.</dd>
<dt>./module.erl:5: head mismatch</dt>
<dd>It's possible your function has more than one head, and each of them has a different arity. Don't forget that different arity means different functions, and you can't interleave function declarations that way. This error is also raised when you insert a function definition between the head clauses of another function.</dd>
<dt>./module.erl:5: Warning: this clause cannot match because a previous clause at line 4 always matches</dt>
<dd>A function defined in the module has a specific clause defined after a catch-all one. As such, the compiler can warn you that you'll never even need to go to the other branch.</dd>
<dt>./module.erl:9: variable 'A' unsafe in 'case' (line 5)</dt>
<dd>You're using a variable declared within one of the branches of a <code>case ... of</code> outside of it. This is considered unsafe. If you want to use such variables, you'd be better of doing <code>MyVar = case ... of</code>...</dd>
</dl>
<p>
这里应该包含了编译时期绝大部分的错误。有些时候,一些错误会产生大量的其它函数的错误信息,
因此非常难找到。因此有时候解决编译错误的方法是,不要被编译器爆出的错误所误导,因为很多时候编译器告诉你的错误根本就不是错误。
如果你看到其它种类错误,但是不在上面的列表中的时候,请给我发邮件,我会尽快的将它包含到这张错误列表中。</p>
<h3><a class="section" name="no-u">不,是你的逻辑出错了!</a></h3>
<img class="right" src="static/img/exam.png" width="232" height="224" alt="An exam with the grade 'F'" title="This is the worst essay on underwater breakdancing I have ever read!" />
<p>
逻辑错误是非常难发现和调试的错误。
这类错误绝大部分都是出自程序员自己:
一般来讲,都是'if'或者'case'的选择分支中没有考虑全面,将应当使用除法的地方和使用乘法的地方混淆了,等等。
这类错误,不会让你的程序崩溃,只是会返回你没见过的坏数据或者让你的程序做出非你预想的结果。
</p>
<p>
很多时候你希望是自己发现这些问题,
不过Erlang有很多工具可以帮助自己发现这些问题,其中包括测试框架,
TypEr和Dialyzer(将在章节<a class="chapter" href="types-or-lack-thereof.html#for-type-junkies">类型</a>中出现),
还有一个好用的
<a class="docs" href="http://www.erlang.org/doc/apps/debugger/debugger_chapter.html"
title="official documentation">调试器</a>和
<a class="docs" href="http://erldocs.com/17.3/runtime_tools/dbg.html"
title="link to non-official documentation">追踪单元</a>等等。
测试你的代码也许是最好的防卫方式。不幸的是,在每个程序员的职业生涯中,所遇到的错误,足够写一打书了,
所以我避免在这上面花费很多时间。非常容易发现哪些让你程序崩溃的错误,因为它们就摆在你面前。
注意这是非常原始的“让它崩溃吧”的思路。</p>
<h3><a class="section" name="run-time-errors">运行期错误</a></h3>
<p> 运行期错误在某种意义上会造成相当大的破坏,同样是会让你的代码崩溃。
当然Erlang拥有发现这些错误的方法,发现者这些错误是非常有价值的。
因此,我写了一些常见的运行期错误的代码,其中包涵了这些错误是如何产生的解释。
</p>
<dl>
<dt>function_clause</dt>
<dd>
<pre class="brush:eshell">
1> lists:sort([3,2,1]).
[1,2,3]
2> lists:sort(fffffff).
** exception error: no function clause matching lists:sort(fffffff)
</pre>
</dd>
<dd> 当所有函数的哨位都失败了,或者没有任何一个函数模式匹配,我们就会看到这个错误。</dd>
<dt>case_clause</dt>
<dd>
<pre class="brush:eshell">
3> case "Unexpected Value" of
3> expected_value -> ok;
3> other_expected_value -> 'also ok'
3> end.
** exception error: no case clause matching "Unexpected Value"
</pre>
</dd>
<dd> 这个看起来更像某人忘记<code>case</code>中的一些特殊情况,或者发送了错误的数据,
或者需要一个捕获所有的分支!</dd>
<dt>if_clause</dt>
<dd>
<pre class="brush:eshell">
4> if 2 > 4 -> ok;
4> 0 > 1 -> ok
4> end.
** exception error: no true branch found when evaluating an if expression
</pre>
</dd>
<dd>这个和<code>case_clause</code>错误很像:
因为它没有办法找到一个可以判定是<code>true</code>的分支。
所以你需要重新审视所有的分支或者添加一个默认的<code>true</code>分支,这样才能保证你的代码没问题。</dd>
<dt>badmatch</dt>
<dd>
<pre class="brush:eshell">
5> [X,Y] = {4,5}.
** exception error: no match of right hand side value {4,5}
</pre>
</dd>
<dd>
错误匹配这个错误,是在任何发生匹配失败的时候就会产生的。
这个绝大部分情况都是,你尝试一个不可能的模式匹配(就像上面那样),活着尝试绑定变量两次,
或者像<code>=</code>操作符两边不想等(这经常造成重新绑定变量失败!)。
请注意,出现这样的错误是因为,很多开发人员认为<var>_MyVar</var>和<code>_</code>具有相同的性质。
以下划线开头的变量是普通变量,除了当你不使用的时候,编译器不会向你抱怨说该变量没使用。它是不能被绑定多于一次的。
</dd>
<dt>badarg</dt>
<dd>
<pre class="brush:eshell">
6> erlang:binary_to_list("heh, already a list").
** exception error: bad argument
in function binary_to_list/1
called as binary_to_list("heh, already a list")
</pre>
</dd>
<dd>
这个和<code>function_clause</code>错误非常像,因为都是用不正确的参数调用了函数。其中主要的不同是
这个错误经常是因为开发人员在函数哨位外的函数内进行参数的验证。我将在后面的章节中,告诉你怎么主动throw这个错误信息。
</dd>
<dt>undef</dt>
<dd>
<pre class="brush:eshell">
7> lists:random([1,2,3]).
** exception error: undefined function lists:random/1
</pre>
</dd>
<dd>
这个错误主要是当调用一个函数时,该函数不存在。
情确保函数和函数的变量数量已经从模块中正确的导出(如果你是从模块外部调用)并且
再检查下你的模块和函数名拼写是否正确。
还有一种情况下,我们会看到这个错误,就是模块并不在Erlang的搜索路径下。
默认情况下,Erlang的搜索路径是当前文件夹。当然你可以通过<code><a class="docs" href="http://erldocs.com/17.3/kernel/code.html#add_patha/1"
title="I'll kill my brain finding new titles for the unofficial doc site">code:add_patha/1</a></code>
或 <code><a class="docs" href="http://erldocs.com/17.3/kernel/code.html#add_pathz/1" title="It's becoming hard to have new ideas">code:add_pathz/1</a></code>。
如果依然无法使用,请确保你已经编译了这个模块!
</dd>
<dt>badarith</dt>
<dd>
<pre class="brush:eshell">
8> 5 + llama.
** exception error: bad argument in an arithmetic expression
in operator +/2
called as 5 + llama
</pre>
</dd>
<dd>
这个一般都是当你进行了错误的数学运算的时候,才会看到,像除以0或者将数字和原子混在了一起。
</dd>
<dt>badfun</dt>
<dd>
<pre class="brush:eshell">
9> hhfuns:add(one,two).
** exception error: bad function one
in function hhfuns:add/2
</pre>
</dd>
<dd>
这个错误经常发生的原因是你将变量当作函数,但是这个变量的值并不是一个函数。
就像例子中那样我使用了<a class="chapter local" href="higher-order-functions.html">前一章</a>
中的函数<code>hhfuns</code>并且用两个原子当作函数。然后这是不会工作的,然后我们就看到了<code>badfun</code>错误。
<dt>badarity</dt>
<dd>
<pre class="brush:eshell">
10> F = fun(_) -> ok end.
#Fun<erl_eval.6.13229925>
11> F(a,b).
** exception error: interpreted function with arity 1 called with two arguments
</pre>
</dd>
<dd>
<code>badarity</code>是<code>badfun</code>错误的一个特殊情况:
当你使用高阶函数的时候,但是你传给它们过多的(或过少)的参数。
</dd>
<dt>system_limit</dt>
<dd>
这里有很多原因会引起<code>system_limit</code>这个错误:
太多的进程(我们已经看到过了),原子太长了,函数的参数量太多了,
原子的数量太多了,连接太多的节点等。如果想得到完整的详细信息列表,
请阅读<a class="docs" href="http://www.erlang.org/doc/efficiency_guide/advanced.html#id2265856">Erlang Efficiency Guide</a>
有关系统限制的章节。注意了,这里面有些错误是完全可以引起整个VM崩溃的。</dd>
</dl>
<h3><a class="section" name="raising-exceptions">Raising Exceptions</a></h3>
<img class="right" src="static/img/stop.png" width="148" height="179" alt="A stop sign" title="... Hammer time!" />
<p>In trying to monitor the execution of code and protect against logical errors, it's often a good idea to provoke run-time crashes so problems will be spotted early.</p>
<p>There are three kinds of exceptions in Erlang: <em>errors</em>, <em>throws</em> and <em>exits</em>. They all have different uses (kind of):</p>
<h4>Errors</h4>
<p>Calling <code>erlang:error(Reason)</code> will end the execution in the current process and include a stack trace of the last functions called with their arguments when you catch it. These are the kind of exceptions that provoke the run-time errors above.</p>
<p>Errors are the means for a function to stop its execution when you can't expect the calling code to handle what just happened. If you get an <code>if_clause</code> error, what can you do? Change the code and recompile, that's what you can do (other than just displaying a pretty error message). An example of when not to use errors could be our tree module from the <a class="chapter local" href="recursion.html#more-than-lists" title="More than lists">recursion chapter</a>. That module might not always be able to find a specific key in a tree when doing a lookup. In this case, it makes sense to expect the user to deal with unknown results: they could use a default value, check to insert a new one, delete the tree, etc. This is when it's appropriate to return a tuple of the form <code>{ok, Value}</code> or an atom like <code>undefined</code> rather than raising errors.</p>
<p>Now, errors aren't limited to the examples above. You can define your own kind of errors too:</p>
<pre class="brush:eshell">
1> erlang:error(badarith).
** exception error: bad argument in an arithmetic expression
2> erlang:error(custom_error).
** exception error: custom_error
</pre>
<p>Here, <code>custom_error</code> is not recognized by the Erlang shell and it has no custom translation such as "bad argument in ...", but it's usable in the same way and can be handled by the programmer in an identical manner (we'll see how to do that soon).</p>
<h4>Exits</h4>
<p>There are two kinds of exits: 'internal' exits and 'external' exits. Internal exits are triggered by calling the function <code>exit/1</code> and make the current process stop its execution. External exits are called with <code>exit/2</code> and have to do with multiple processes in the concurrent aspect of Erlang; as such, we'll mainly focus on internal exits and will visit the external kind later on.</p>
<p>Internal exits are pretty similar to errors. In fact, historically speaking, they were the same and only <code>exit/1</code> existed. They've got roughly the same use cases. So how to choose one? Well the choice is not obvious. To understand when to use one or the other, there's no choice but to start looking at the concepts of actors and processes from far away.</p>
<p>In the introduction, I've compared processes as people communicating by mail. There's not a lot to add to the analogy, so I'll go to diagrams and bubbles.</p>
<img src="static/img/a-b-msg.png" width="221" height="83" alt="A process 'A' represented by a circle, sending a message (represented by an arrow) to a process 'B' (another circle)" title="whoa!" />
<p>Processes here can send each other messages. A process can also listen for messages, wait for them. You can also choose what messages to listen to, discard some, ignore others, give up listening after a certain time etc.</p>
<img src="static/img/a-b-c-hello.png" width="226" height="143" alt="A process 'A' sending 'hello' to a process 'B', which in turns messages C with 'A says hello!'" title="B is the third wheel here" />
<p>These basic concepts let the implementors of Erlang use a special kind of message to communicate exceptions between processes. They act a bit like a process' last breath; they're sent right before a process dies and the code it contains stops executing. Other processes that were listening for that specific kind of message can then know about the event and do whatever they please with it. This includes logging, restarting the process that died, etc.</p>
<img src="static/img/a-b-dead.png" width="266" height="86" alt="A dead process (a bursting bubble) sending 'I'm dead' to a process 'B'" title="" />
<p>With this concept explained, the difference in using <code>erlang:error/1</code> and <code>exit/1</code> is easier to understand. While both can be used in an extremely similar manner, the real difference is in the intent. You can then decide whether what you've got is 'simply' an error or a condition worthy of killing the current process. This point is made stronger by the fact that <code>erlang:error/1</code> returns a stack trace and <code>exit/1</code> doesn't. If you were to have a pretty large stack trace or lots of arguments to the current function, copying the exit message to every listening process would mean copying the data. In some cases, this could become unpractical.</p>
<h4>Throws</h4>
<p>A throw is a class of exceptions used for cases that the programmer can be expected to handle. In comparison with exits and errors, they don't really carry any 'crash that process!' intent behind them, but rather control flow. As you use throws while expecting the programmer to handle them, it's usually a good idea to document their use within a module using them.</p>
<p>The syntax to throw an exception is:</p>
<pre class="brush:eshell">
1> throw(permission_denied).
** exception throw: permission_denied
</pre>
<p>Where you can replace <code>permission_denied</code> by anything you want (including <code>'everything is fine'</code>, but that is not helpful and you will lose friends).</p>
<p>Throws can also be used for non-local returns when in deep recursion. An example of that is the <code><a class="docs" href="http://erldocs.com/17.3/ssl/ssl.html" title="you don't actually need to click this to understand the chapter">ssl</a></code> module which uses <code>throw/1</code> as a way to push <code>{error, Reason}</code> tuples back to a top-level function. This function then simply returns that tuple to the user. This lets the implementer only write for the successful cases and have one function deal with the exceptions on top of it all.</p>
<p>Another example could be the array module, where there is a lookup function that can return a user-supplied default value if it can't find the element needed. When the element can't be found, the value <code>default</code> is thrown as an exception, and the top-level function handles that and substitutes it with the user-supplied default value. This keeps the programmer of the module from needing to pass the default value as a parameter of every function of the lookup algorithm, again focusing only on the successful cases.</p>
<img class="right" src="static/img/catch.png" width="107" height="248" alt="A fish that was caught" title="catch the pun" />
<p>As a rule of thumb, try to limit the use of your throws for non-local returns to a single module in order to make it easier to debug your code. It will also let you change the innards of your module without requiring changes in its interface.</p>
<h3><a class="section" name="dealing-with-exceptions">Dealing with Exceptions</a></h3>
<p>I've already mentioned quite a few times that throws, errors and exits can be handled. The way to do this is by using a <code>try ... catch</code> expression.</p>
<p>A <code>try ... catch</code> is a way to evaluate an expression while letting you handle the successful case as well as the errors encountered. The general syntax for such an expression is:</p>
<pre class="brush:erl">
try Expression of
SuccessfulPattern1 [Guards] ->
Expression1;
SuccessfulPattern2 [Guards] ->
Expression2
catch
TypeOfError:ExceptionPattern1 ->
Expression3;
TypeOfError:ExceptionPattern2 ->
Expression4
end.
</pre>
<p>The <var>Expression</var> in between <code>try</code> and <code>of</code> is said to be <em>protected</em>. This means that any kind of exception happening within that call will be caught. The patterns and expressions in between the <code>try ... of</code> and <code>catch</code> behave in exactly the same manner as a <code>case ... of</code>. Finally, the <code>catch</code> part: here, you can replace <var>TypeOfError</var> by either <code>error</code>, <code>throw</code> or <code>exit</code>, for each respective type we've seen in this chapter. If no type is provided, a <code>throw</code> is assumed. So let's put this in practice.</p>
<p>First of all, let's start a module named <code><a class="source" href="static/erlang/exceptions.erl">exceptions</a></code>. We're going for simple here:</p>
<pre class="brush:erl">
-module(exceptions).
-compile(export_all).
throws(F) ->
try F() of
_ -> ok
catch
Throw -> {throw, caught, Throw}
end.
</pre>
<p>We can compile it and try it with different kinds of exceptions:</p>
<pre class="brush:eshell">
1> c(exceptions).
{ok,exceptions}
2> exceptions:throws(fun() -> throw(thrown) end).
{throw,caught,thrown}
3> exceptions:throws(fun() -> erlang:error(pang) end).
** exception error: pang
</pre>
<p>As you can see, this <code>try ... catch</code> is only receiving throws. As stated earlier, this is because when no type is mentioned, a throw is assumed. Then we have functions with catch clauses of each type:</p>
<pre class="brush:erl">
errors(F) ->
try F() of
_ -> ok
catch
error:Error -> {error, caught, Error}
end.
exits(F) ->
try F() of
_ -> ok
catch
exit:Exit -> {exit, caught, Exit}
end.
</pre>
<p>And to try them:</p>
<pre class="brush:eshell">
4> c(exceptions).
{ok,exceptions}
5> exceptions:errors(fun() -> erlang:error("Die!") end).
{error,caught,"Die!"}
6> exceptions:exits(fun() -> exit(goodbye) end).
{exit,caught,goodbye}
</pre>
<p>The next example on the menu shows how to combine all the types of exceptions in a single <code>try ... catch</code>. We'll first declare a function to generate all the exceptions we need:</p>
<pre class="brush:erl">
sword(1) -> throw(slice);
sword(2) -> erlang:error(cut_arm);
sword(3) -> exit(cut_leg);
sword(4) -> throw(punch);
sword(5) -> exit(cross_bridge).
black_knight(Attack) when is_function(Attack, 0) ->
try Attack() of
_ -> "None shall pass."
catch
throw:slice -> "It is but a scratch.";
error:cut_arm -> "I've had worse.";
exit:cut_leg -> "Come on you pansy!";
_:_ -> "Just a flesh wound."
end.
</pre>
<p>Here <code>is_function/2</code> is a BIF which makes sure the variable <var>Attack</var> is a function of arity 0. Then we add this one for good measure:</p>
<pre class="brush:erl">
talk() -> "blah blah".
</pre>
<p><cite>And now for something completely different</cite>:</p>
<pre class="brush:eshell">
7> c(exceptions).
{ok,exceptions}
8> exceptions:talk().
"blah blah"
9> exceptions:black_knight(fun exceptions:talk/0).
"None shall pass."
10> exceptions:black_knight(fun() -> exceptions:sword(1) end).
"It is but a scratch."
11> exceptions:black_knight(fun() -> exceptions:sword(2) end).
"I've had worse."
12> exceptions:black_knight(fun() -> exceptions:sword(3) end).
"Come on you pansy!"
13> exceptions:black_knight(fun() -> exceptions:sword(4) end).
"Just a flesh wound."
14> exceptions:black_knight(fun() -> exceptions:sword(5) end).
"Just a flesh wound."
</pre>
<img class="right" src="static/img/black-knight.png" width="221" height="262" alt="Monty Python's black knight" title="Just a flesh wound." />
<p>The expression on line 9 demonstrates normal behavior for the black knight, when function execution happens normally. Each line that follows that one demonstrates pattern matching on exceptions according to their class (throw, error, exit) and the reason associated with them (<code>slice</code>, <code>cut_arm</code>, <code>cut_leg</code>).</p>
<p>One thing shown here on expressions 13 and 14 is a catch-all clause for exceptions. The <code>_:_</code> pattern is what you need to use to make sure to catch any exception of any type. In practice, you should be careful when using the catch-all patterns: try to protect your code from what you can handle, but not any more than that. Erlang has other facilities in place to take care of the rest.</p>
<p>There's also an additional clause that can be added after a <code>try ... catch</code> that will always be executed. This is equivalent to the 'finally' block in many other languages:</p>
<pre class="brush:erl">
try Expr of
Pattern -> Expr1
catch
Type:Exception -> Expr2
after % this always gets executed
Expr3
end
</pre>
<p>No matter if there are errors or not, the expressions inside the <code>after</code> part are guaranteed to run. However, you can not get any return value out of the <code>after</code> construct. Therefore, <code>after</code> is mostly used to run code with side effects. The canonical use of this is when you want to make sure a file you were reading gets closed whether exceptions are raised or not.</p>
<p>We now know how to handle the 3 classes of exceptions in Erlang with catch blocks. However, I've hidden information from you: it's actually possible to have more than one expression between the <code>try</code> and the <code>of</code>!</p>
<pre class="brush:erl">
whoa() ->
try
talk(),
_Knight = "None shall Pass!",
_Doubles = [N*2 || N <- lists:seq(1,100)],
throw(up),
_WillReturnThis = tequila
of
tequila -> "hey this worked!"
catch
Exception:Reason -> {caught, Exception, Reason}
end.
</pre>
<p>By calling <code>exceptions:whoa()</code>, we'll get the obvious <code>{caught, throw, up}</code>, because of <code>throw(up)</code>. So yeah, it's possible to have more than one expression between <code>try</code> and <code>of</code>...</p>
<p>What I just highlighted in <code>exceptions:whoa/0</code> and that you might have not noticed is that when we use many expressions in that manner, we might not always care about what the return value is. The <code>of</code> part thus becomes a bit useless. Well good news, you can just give it up:</p>
<pre class="brush:erl">
im_impressed() ->
try
talk(),
_Knight = "None shall Pass!",
_Doubles = [N*2 || N <- lists:seq(1,100)],
throw(up),
_WillReturnThis = tequila
catch
Exception:Reason -> {caught, Exception, Reason}
end.
</pre>
<p>And now it's a bit leaner!</p>
<div class="note">
<p><strong>Note:</strong> It is important to know that the protected part of an exception can't be tail recursive. The VM must always keep a reference there in case there's an exception popping up.</p>
<p>Because the <code>try ... catch</code> construct without the <code>of</code> part has nothing but a protected part, calling a recursive function from there might be dangerous for programs supposed to run for a long time (which is Erlang's niche). After enough iterations, you'll go out of memory or your program will get slower without really knowing why. By putting your recursive calls between the <code>of</code> and <code>catch</code>, you are not in a protected part and you will benefit from Last Call Optimisation.</p>
<p>Some people use <code>try ... of ... catch</code> rather than <code>try ... catch</code> by default to avoid unexpected errors of that kind, except for obviously non-recursive code with results that won't be used by anything. You're most likely able to make your own decision on what to do!</p>
</div>
<h3><a class="section" name="theres-more">Wait, there's more!</a></h3>
<p>As if it wasn't enough to be on par with most languages already, Erlang's got yet another error handling structure. That structure is defined as the keyword <code>catch</code> and basically captures all types of exceptions on top of the good results. It's a bit of a weird one because it displays a different representation of exceptions:</p>
<pre class="brush:eshell">
1> catch throw(whoa).
whoa
2> catch exit(die).
{'EXIT',die}
3> catch 1/0.
{'EXIT',{badarith,[{erlang,'/',[1,0]},
{erl_eval,do_apply,5},
{erl_eval,expr,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}
4> catch 2+2.
4
</pre>
<p>What we can see from this is that throws remain the same, but that exits and errors are both represented as <code>{'EXIT', Reason}</code>. That's due to errors being bolted to the language after exits (they kept a similar representation for backwards compatibility).</p>
<p>The way to read this stack trace is as follows:</p>
<pre class="brush:eshell">
5> catch doesnt:exist(a,4).
{'EXIT',{undef,[{doesnt,exist,[a,4]},
{erl_eval,do_apply,5},
{erl_eval,expr,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}
</pre>
<ul>
<li>The type of error is <code>undef</code>, which means the function you called is not defined (see the list at the beginning of this chapter)</li>
<li>The list right after the type of error is a stack trace</li>
<li>The tuple on top of the stack trace represents the last function to be called (<code>{Module, Function, Arguments}</code>). That's your undefined function.</li>
<li>The tuples after that are the functions called before the error. This time they're of the form <code>{Module, Function, Arity}</code>.</li>
<li>That's all there is to it, really.</li>
</ul>
<p>You can also manually get a stack trace by calling <code>erlang:get_stacktrace/0</code> in the process that crashed.</p>
<p>You'll often see <code>catch</code> written in the following manner (we're still in <a class="source" href="static/erlang/exceptions.erl">exceptions.erl</a>):</p>
<pre class="brush:erl">
catcher(X,Y) ->
case catch X/Y of
{'EXIT', {badarith,_}} -> "uh oh";
N -> N
end.
</pre>
<p>And as expected:</p>
<pre class="brush:eshell">
6> c(exceptions).
{ok,exceptions}
7> exceptions:catcher(3,3).
1.0
8> exceptions:catcher(6,3).
2.0
9> exceptions:catcher(6,0).
"uh oh"
</pre>
<p>This sounds compact and easy to catch exceptions, but there are a few problems with <code>catch</code>. The first of it is operator precedence:</p>
<pre class="brush:eshell">
10> X = catch 4+2.
* 1: syntax error before: 'catch'
10> X = (catch 4+2).
6
</pre>
<p>That's not exactly intuitive given that most expressions do not need to be wrapped in parentheses this way. Another problem with <code>catch</code> is that you can't see the difference between what looks like the underlying representation of an exception and a real exception:</p>
<pre class="brush:eshell">
11> catch erlang:boat().
{'EXIT',{undef,[{erlang,boat,[]},
{erl_eval,do_apply,5},
{erl_eval,expr,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}
12> catch exit({undef, [{erlang,boat,[]}, {erl_eval,do_apply,5}, {erl_eval,expr,5}, {shell,exprs,6}, {shell,eval_exprs,6}, {shell,eval_loop,3}]}).
{'EXIT',{undef,[{erlang,boat,[]},
{erl_eval,do_apply,5},
{erl_eval,expr,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}
</pre>
<p>And you can't know the difference between an error and an actual exit. You could also have used <code>throw/1</code> to generate the above exception. In fact, a <code>throw/1</code> in a <code>catch</code> might also be problematic in another scenario:</p>
<pre class="brush:erl">
one_or_two(1) -> return;
one_or_two(2) -> throw(return).
</pre>
<p>And now the killer problem:</p>
<pre class="brush:eshell">
13> c(exceptions).
{ok,exceptions}
14> catch exceptions:one_or_two(1).
return
15> catch exceptions:one_or_two(2).
return
</pre>
<p>Because we're behind a <code>catch</code>, we can never know if the function threw an exception or if it returned an actual value! This might not really happen a whole lot in practice, but it's still a wart big enough to have warranted the addition of the <code>try ... catch</code> construct in the R10B release.</p>
<h3><a class="section" name="try-a-try-in-a-tree">Try a try in a tree</a></h3>
<p>To put exceptions in practice, we'll do a little exercise requiring us to dig for our <code><a class="source" href="static/erlang/tree.erl">tree</a></code> module. We're going to add a function that lets us do a lookup in the tree to find out whether a value is already present in there or not. Because the tree is ordered by its keys and in this case we do not care about the keys, we'll need to traverse the whole thing until we find the value.</p>
<p>The traversal of the tree will be roughly similar to what we did in <code>tree:lookup/2</code>, except this time we will always search down both the left branch and the right branch. To write the function, you'll just need to remember that a tree node is either <code>{node, {Key, Value, NodeLeft, NodeRight}}</code> or <code>{node, 'nil'}</code> when empty. With this in hand, we can write a basic implementation without exceptions:</p>
<pre class="brush:erl">
%% looks for a given value 'Val' in the tree.
has_value(_, {node, 'nil'}) ->
false;
has_value(Val, {node, {_, Val, _, _}}) ->
true;
has_value(Val, {node, {_, _, Left, Right}}) ->
case has_value(Val, Left) of
true -> true;
false -> has_value(Val, Right)
end.
</pre>
<p>The problem with this implementation is that every node of the tree we branch at has to test for the result of the previous branch:</p>
<img class="center explanation" src="static/img/tree-case.png" width="495" height="176" alt="A diagram of the tree with an arrow following every node checked while traversing the tree, and then when returning the result" />
<p>This is a bit annoying. With the help of throws, we can make something that will require less comparisons:</p>
<pre class="brush:erl">
has_value(Val, Tree) ->
try has_value1(Val, Tree) of
false -> false
catch
true -> true
end.
has_value1(_, {node, 'nil'}) ->
false;
has_value1(Val, {node, {_, Val, _, _}}) ->
throw(true);
has_value1(Val, {node, {_, _, Left, Right}}) ->
has_value1(Val, Left),
has_value1(Val, Right).
</pre>
<p>The execution of the code above is similar to the previous version, except that we never need to check for the return value: we don't care about it at all. In this version, only a throw means the value was found. When this happens, the tree evaluation stops and it falls back to the <code>catch</code> on top. Otherwise, the execution keeps going until the last <samp>false</samp> is returned and that's what the user sees:</p>
<img class="center explanation" src="static/img/tree-throw.png" width="495" height="176" alt="A diagram of the tree with an arrow following every node checked while traversing the tree, and then skipping all the nodes on the way back up (thanks to a throw)" />
<p>Of course, the implementation above is longer than the previous one. However, it is possible to realize gains in speed and in clarity by using non-local returns with a throw, depending on the operations you're doing. The current example is a simple comparison and there's not much to see, but the practice still makes sense with more complex data structures and operations.</p>
<p>That being said, we're probably ready to solve real problems in sequential Erlang.</p>
<ul class="navigation">
<li><a href="higher-order-functions.html" title="Previous chapter">< Previous</a></li>
<li><a href="contents.html" title="Index">Index</a></li>
<li><a href="functionally-solving-problems.html" title="Next chapter">Next ></a></li>
</ul>
</div><!-- content -->
<div id="footer">
<a href="http://creativecommons.org/licenses/by-nc-nd/3.0/" title="Creative Commons License Details"><img src="static/img/cc.png" width="88" height="31" alt="Creative Commons Attribution Non-Commercial No Derivative License" /></a>
<p>Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution Non-Commercial No Derivative License</p>
</div> <!-- footer -->
</div> <!-- wrapper -->
<div id="grass" />
<script type="text/javascript" src="static/js/shCore.js"></script>
<script type="text/javascript" src="static/js/shBrushErlang2.js%3F11"></script>
<script type="text/javascript">
SyntaxHighlighter.defaults.gutter = false;
SyntaxHighlighter.all();
</script>
</body>
</html>