长期缺铁性贫血会导致什么后果| 梦见出国了是什么意思| 重庆以前叫什么| 三妻四妾是什么生肖| 肌酐偏低是什么意思| nub是什么意思| 上课什么坐姿可以瘦腿| 掌中宝是什么肉| 12.8是什么星座| 碳14呼气试验阳性是什么意思| 女性吃金蝉有什么好处| 蔡英文是什么党派| 梦见新房子是什么意思| 4月26是什么星座| 梅核气有什么症状| 做梦梦到水是什么征兆| 缺维生素c会得什么病| 三个六代表什么意思| 得令是什么意思| 晚上睡觉阴部外面为什么会痒| 胡萝卜什么颜色| 为什么剪指甲不会疼| 清补凉是什么| 兔和什么相冲| 高考600多分能上什么大学| gms是什么意思| 歼是什么意思| 诱导是什么意思| 线粒体是什么| 一什么铃铛| 发呆是什么意思| 一凉就咳嗽是什么原因| 什么蜂蜜好| 冠字五行属什么| 半月板变性是什么意思| 总流口水是什么原因| 真菌孢子是什么| 三七植物长什么样子| 肚子疼应该吃什么药| 贡生相当于现在的什么| 半夜口干舌燥是什么原因| 甘油三酯查什么项目| 疖子是什么原因引起的| hpv是什么病严重吗| 话说多了声音嘶哑是什么原因| 配伍是什么意思| 什么的搏斗| 犯罪心理学属于什么专业| 荷花是什么季节开的| 头晕是什么症状| 吃饭时头晕是什么原因| rfc是什么意思| 鸡为什么吃自己下的蛋| 腿上有白点是什么原因| 木姜子是什么| 96年的属什么| 黄芪泡水喝有什么作用| 脑膜炎是什么原因引起的| 水浒传是什么朝代| 阿飞是什么意思| 锲而不舍是什么生肖| 男同是什么| 淋球菌培养是检查什么| 长史相当于现在什么官| 突然抽搐是什么原因| 老打饱嗝是什么原因| surprise是什么意思| 三七花泡水喝有什么功效| 头晕呕吐是什么原因| 补锌吃什么药| 冰雹是什么季节下的| 支气管病变什么意思| 常吃阿司匹林有什么副作用| 放屁多吃什么药好| 1994年属狗的是什么命| 女上位是什么意思| 等效球镜是什么意思| 女人消瘦应该检查什么| 丹毒用什么药膏| 大姨妈有血块是什么原因| 绞肠痧是什么病| 脸上长水泡似的痘痘是什么原因| 什么方法减肥最快| 三八妇女节送老婆什么礼物好| 经期喝茶有什么影响| 大红袍属于什么茶| app有什么用途| 身体老是出汗是什么原因| 海鲜配啤酒有什么反应| 静脉注射是什么意思| 林心如什么学历| 过敏性咳嗽吃什么药好| 米线是什么材料做的| 女人生气容易得什么病| 贲门松弛吃什么药| 什么水果清热解毒去火| 985是什么| 宫颈非典型鳞状细胞是什么意思| dha是什么意思| 混剪是什么意思| 窦性心动过速什么意思| 苯三酚注射有什么用| 胎盘成熟度0级什么意思| 西米露是什么材料做的| 治鸡眼用什么药最好| 口腔溃疡反反复复是什么原因| 九月十四号是什么星座| 冬至穿什么衣服| 破釜沉舟是什么生肖| 走后门什么意思| 7.6什么星座| 淡奶油能做什么| 夏至要吃什么| 恋爱观是什么| 乙肝是什么病严重吗| 召力念什么| 就坡下驴什么意思| 增加性功能吃什么药| 淋巴结肿大是什么原因| 湿疹是什么样子的| 03年是什么年| 孤单的我还是没有改变是什么歌| 什么是西米| 地铁不能带什么东西| 1979年是什么命| 松花蛋不能和什么一起吃| 血糖高的可以吃什么水果| 哈士蟆是什么东西| mw是什么单位| 流清鼻涕是什么原因| 什么是预防医学| 惊弓之鸟是什么故事| 缜密是什么意思| g什么意思| 空调外机不出水是什么原因| 侃侃而谈什么意思| 园字五行属什么| 夫妻都是b型血孩子是什么血型| 先天性聋哑病属于什么遗传病| 耽美是什么| 阴人是什么意思| 心衰是什么病| 裸官是什么意思| 威海的海是什么海| 丁毒豆泡酒能治什么病| 无赖不还钱最怕什么| 微量元素6项是查什么| 艮什么意思| 土霉素治什么病| 海水为什么是蓝色的| 态生两靥之愁中靥指什么| 日本为什么要偷袭珍珠港| 长期喝蜂蜜水有什么好处| 连长是什么军衔| 在家里可以做什么赚钱| 为什么会来月经| 龙虾吃什么食物| 梗米是什么米| 耳垂上有痣代表什么| 8月31号是什么星座| 什么情况下吃速效救心丸| 甲肝抗体阳性代表什么| 大便粘便池是什么原因| 桑叶有什么功效和作用| 梦见好多水是什么预兆| 舌苔是什么| 缸垫呲了什么症状| 射手座和什么座最配| 失信是什么意思| 耕田是什么意思| 车字旁有什么字| 尿浑浊是什么原因| ipl是什么意思| 卵泡长得慢是什么原因造成的| 下巴长痘痘是什么原因| 景气是什么意思| 七月是什么生肖| 征候是什么意思| 2月22日什么星座| 刘姥姥进大观园什么意思| 心梗做什么检查| 小孩子消化不好吃什么调理| 中老年补钙吃什么钙片好| 科目一考试需要带什么| 什么古迹| 11月24日是什么星座| plcc是什么意思| 长颈鹿的脖子为什么那么长| 松垮是什么意思| 6月11日是什么星座| 脾功能亢进是什么意思| 嘴苦口臭是什么原因造成的| 云想衣裳花想容是什么意思| 尾戒代表什么| 蛋白粉适合什么人群吃| 梦到老房子是什么意思| 260是什么意思| 什么的生活| 乙肝有抗体是显示什么结果| 白切鸡用什么鸡| ideal是什么意思| 肠痈是什么意思| 五三年属什么生肖| 什么症状是怀孕了| 本自具足是什么意思| 猪胰子是什么东西| 眼皮为什么会跳| 木加一笔变成什么字| 旮旯是什么意思| 体悟是什么意思| 王久是什么字| 注意力不集中是什么原因| 柳丁是什么| 肉桂属于什么茶| 葡萄什么时候传入中国| 两个方一个土是什么字| 低压高是什么原因引起的| 子癫是什么病| 发扬什么精神| 子宫后壁低回声是什么意思| 善变是什么意思| 早上起床喉咙有痰是什么原因| 掌心痣代表什么意思| 酸奶有什么营养价值| 麦麸是什么| 6.21什么星座| 恐龙蛋是什么水果| 县长什么级别干部| 2月份生日是什么星座| 和田玉籽料是什么意思| 产成品是什么意思| 什么筷子不发霉又健康| 风寒感冒用什么药| 阿根廷讲什么语言| 疑心病是什么意思| 月经结束一周后又出血是什么原因| 相机hdr功能是什么意思| 虎和什么属相不合| 熬笔是什么意思| 什么药止汗效果最好| 泌乳素高是什么原因| 幻视是什么意思| 血糖高有什么症状| 胸径是什么意思| 申时左眼跳是什么预兆| 凌晨3点是什么时辰| 喝什么解辣| 螺蛳粉为什么那么臭| 吃什么水果能壮阳| 勾魂是什么意思| 熬夜到什么程度会猝死| 大连机场叫什么| 伤口消毒用什么| 绞股蓝和什么搭配喝减肥| 超管是什么| 田七和三七有什么区别| 上火了吃什么药好| 宫外孕是什么导致的| 迁坟需要准备什么东西| 猫能吃什么| 单绒双羊是什么意思| 肠手术后吃什么恢复快| 尿白细胞加减什么意思| 复方氯化钠注射作用是什么| 百度

深圳巴士集团举办廉洁从业诗文朗诵比赛(图)

百度   杨雄年强调,在思想上要以十九大精神为指引,深入领会贯彻全面从严治党精神,切实增强全面从严治党的政治自觉、思想自觉和行动自觉;在工作上要结合中央一号文件、农业部一号文件精神,统筹部署2018年工作,不仅要突出抓重点,还要突出抓落实,在新征程中要有新作为。

One of Lisp's great features is its condition system. It serves a similar purpose to the exception handling systems in Java, Python, and C++ but is more flexible. In fact, its flexibility extends beyond error handling--conditions are more general than exceptions in that a condition can represent any occurrence during a program's execution that may be of interest to code at different levels on the call stack. For example, in the section "Other Uses for Conditions," you'll see that conditions can be used to emit warnings without disrupting execution of the code that emits the warning while allowing code higher on the call stack to control whether the warning message is printed. For the time being, however, I'll focus on error handling.

The condition system is more flexible than exception systems because instead of providing a two-part division between the code that signals an error1 and the code that handles it,2 the condition system splits the responsibilities into three parts--signaling a condition, handling it, and restarting. In this chapter, I'll describe how you could use conditions in part of a hypothetical application for analyzing log files. You'll see how you could use the condition system to allow a low-level function to detect a problem while parsing a log file and signal an error, to allow mid-level code to provide several possible ways of recovering from such an error, and to allow code at the highest level of the application to define a policy for choosing which recovery strategy to use.

To start, I'll introduce some terminology: errors, as I'll use the term, are the consequences of Murphy's law. If something can go wrong, it will: a file that your program needs to read will be missing, a disk that you need to write to will be full, the server you're talking to will crash, or the network will go down. If any of these things happen, it may stop a piece of code from doing what you want. But there's no bug; there's no place in the code that you can fix to make the nonexistent file exist or the disk not be full. However, if the rest of the program is depending on the actions that were going to be taken, then you'd better deal with the error somehow or you will have introduced a bug. So, errors aren't caused by bugs, but neglecting to handle an error is almost certainly a bug.

So, what does it mean to handle an error? In a well-written program, each function is a black box hiding its inner workings. Programs are then built out of layers of functions: high-level functions are built on top of the lower-level functions, and so on. This hierarchy of functionality manifests itself at runtime in the form of the call stack: if high calls medium, which calls low, when the flow of control is in low, it's also still in medium and high, that is, they're still on the call stack.

Because each function is a black box, function boundaries are an excellent place to deal with errors. Each function--low, for example--has a job to do. Its direct caller--medium in this case--is counting on it to do its job. However, an error that prevents it from doing its job puts all its callers at risk: medium called low because it needs the work done that low does; if that work doesn't get done, medium is in trouble. But this means that medium's caller, high, is also in trouble--and so on up the call stack to the very top of the program. On the other hand, because each function is a black box, if any of the functions in the call stack can somehow do their job despite underlying errors, then none of the functions above it needs to know there was a problem--all those functions care about is that the function they called somehow did the work expected of it.

In most languages, errors are handled by returning from a failing function and giving the caller the choice of either recovering or failing itself. Some languages use the normal function return mechanism, while languages with exceptions return control by throwing or raising an exception. Exceptions are a vast improvement over using normal function returns, but both schemes suffer from a common flaw: while searching for a function that can recover, the stack unwinds, which means code that might recover has to do so without the context of what the lower-level code was trying to do when the error actually occurred.

Consider the hypothetical call chain of high, medium, low. If low fails and medium can't recover, the ball is in high's court. For high to handle the error, it must either do its job without any help from medium or somehow change things so calling medium will work and call it again. The first option is theoretically clean but implies a lot of extra code--a whole extra implementation of whatever it was medium was supposed to do. And the further the stack unwinds, the more work that needs to be redone. The second option--patching things up and retrying--is tricky; for high to be able to change the state of the world so a second call into medium won't end up causing an error in low, it'd need an unseemly knowledge of the inner workings of both medium and low, contrary to the notion that each function is a black box.

The Lisp Way

Common Lisp's error handling system gives you a way out of this conundrum by letting you separate the code that actually recovers from an error from the code that decides how to recover. Thus, you can put recovery code in low-level functions without committing to actually using any particular recovery strategy, leaving that decision to code in high-level functions.

To get a sense of how this works, let's suppose you're writing an application that reads some sort of textual log file, such as a Web server's log. Somewhere in your application you'll have a function to parse the individual log entries. Let's assume you'll write a function, parse-log-entry, that will be passed a string containing the text of a single log entry and that is supposed to return a log-entry object representing the entry. This function will be called from a function, parse-log-file, that reads a complete log file and returns a list of objects representing all the entries in the file.

To keep things simple, the parse-log-entry function will not be required to parse incorrectly formatted entries. It will, however, be able to detect when its input is malformed. But what should it do when it detects bad input? In C you'd return a special value to indicate there was a problem. In Java or Python you'd throw or raise an exception. In Common Lisp, you signal a condition.

Conditions

A condition is an object whose class indicates the general nature of the condition and whose instance data carries information about the details of the particular circumstances that lead to the condition being signaled.3 In this hypothetical log analysis program, you might define a condition class, malformed-log-entry-error, that parse-log-entry will signal if it's given data it can't parse.

Condition classes are defined with the DEFINE-CONDITION macro, which works essentially the same as DEFCLASS except that the default superclass of classes defined with DEFINE-CONDITION is CONDITION rather than STANDARD-OBJECT. Slots are specified in the same way, and condition classes can singly and multiply inherit from other classes that descend from CONDITION. But for historical reasons, condition classes aren't required to be instances of STANDARD-OBJECT, so some of the functions you use with DEFCLASSed classes aren't required to work with conditions. In particular, a condition's slots can't be accessed using SLOT-VALUE; you must specify either a :reader option or an :accessor option for any slot whose value you intend to use. Likewise, new condition objects are created with MAKE-CONDITION rather than MAKE-INSTANCE. MAKE-CONDITION initializes the slots of the new condition based on the :initargs it's passed, but there's no way to further customize a condition's initialization, equivalent to INITIALIZE-INSTANCE.4

When using the condition system for error handling, you should define your conditions as subclasses of ERROR, a subclass of CONDITION. Thus, you might define malformed-log-entry-error, with a slot to hold the argument that was passed to parse-log-entry, like this:

(define-condition malformed-log-entry-error (error)
  ((text :initarg :text :reader text)))

Condition Handlers

In parse-log-entry you'll signal a malformed-log-entry-error if you can't parse the log entry. You signal errors with the function ERROR, which calls the lower-level function SIGNAL and drops into the debugger if the condition isn't handled. You can call ERROR two ways: you can pass it an already instantiated condition object, or you can pass it the name of the condition class and any initargs needed to construct a new condition, and it will instantiate the condition for you. The former is occasionally useful for resignaling an existing condition object, but the latter is more concise. Thus, you could write parse-log-entry like this, eliding the details of actually parsing a log entry:

(defun parse-log-entry (text)
  (if (well-formed-log-entry-p text)
    (make-instance 'log-entry ...)
    (error 'malformed-log-entry-error :text text)))

What happens when the error is signaled depends on the code above parse-log-entry on the call stack. To avoid landing in the debugger, you must establish a condition handler in one of the functions leading to the call to parse-log-entry. When a condition is signaled, the signaling machinery looks through a list of active condition handlers, looking for a handler that can handle the condition being signaled based on the condition's class. Each condition handler consists of a type specifier indicating what types of conditions it can handle and a function that takes a single argument, the condition. At any given moment there can be many active condition handlers established at various levels of the call stack. When a condition is signaled, the signaling machinery finds the most recently established handler whose type specifier is compatible with the condition being signaled and calls its function, passing it the condition object.

The handler function can then choose whether to handle the condition. The function can decline to handle the condition by simply returning normally, in which case control returns to the SIGNAL function, which will search for the next most recently established handler with a compatible type specifier. To handle the condition, the function must transfer control out of SIGNAL via a nonlocal exit. In the next section, you'll see how a handler can choose where to transfer control. However, many condition handlers simply want to unwind the stack to the place where they were established and then run some code. The macro HANDLER-CASE establishes this kind of condition handler. The basic form of a HANDLER-CASE is as follows:

(handler-case expression
  error-clause*)

where each error-clause is of the following form:

(condition-type ([var]) code)

If the expression returns normally, then its value is returned by the HANDLER-CASE. The body of a HANDLER-CASE must be a single expression; you can use PROGN to combine several expressions into a single form. If, however, the expression signals a condition that's an instance of any of the condition-types specified in any error-clause, then the code in the appropriate error clause is executed and its value returned by the HANDLER-CASE. The var, if included, is the name of the variable that will hold the condition object when the handler code is executed. If the code doesn't need to access the condition object, you can omit the variable name.

For instance, one way to handle the malformed-log-entry-error signaled by parse-log-entry in its caller, parse-log-file, would be to skip the malformed entry. In the following function, the HANDLER-CASE expression will either return the value returned by parse-log-entry or return NIL if a malformed-log-entry-error is signaled. (The it in the LOOP clause collect it is another LOOP keyword, which refers to the value of the most recently evaluated conditional test, in this case the value of entry.)

(defun parse-log-file (file)
  (with-open-file (in file :direction :input)
    (loop for text = (read-line in nil nil) while text
       for entry = (handler-case (parse-log-entry text)
                     (malformed-log-entry-error () nil))
       when entry collect it)))

When parse-log-entry returns normally, its value will be assigned to entry and collected by the LOOP. But if parse-log-entry signals a malformed-log-entry-error, then the error clause will return NIL, which won't be collected.

JAVA-STYLE EXCEPTON HANDLING

This version of parse-log-file has one serious deficiency: it's doing too much. As its name suggests, the job of parse-log-file is to parse the file and produce a list of log-entry objects; if it can't, it's not its place to decide what to do instead. What if you want to use parse-log-file in an application that wants to tell the user that the log file is corrupted or one that wants to recover from malformed entries by fixing them up and re-parsing them? Or maybe an application is fine with skipping them but only until a certain number of corrupted entries have been seen.

You could try to fix this problem by moving the HANDLER-CASE to a higher-level function. However, then you'd have no way to implement the current policy of skipping individual entries--when the error was signaled, the stack would be unwound all the way to the higher-level function, abandoning the parsing of the log file altogether. What you want is a way to provide the current recovery strategy without requiring that it always be used.

Restarts

The condition system lets you do this by splitting the error handling code into two parts. You place code that actually recovers from errors into restarts, and condition handlers can then handle a condition by invoking an appropriate restart. You can place restart code in mid- or low-level functions, such as parse-log-file or parse-log-entry, while moving the condition handlers into the upper levels of the application.

To change parse-log-file so it establishes a restart instead of a condition handler, you can change the HANDLER-CASE to a RESTART-CASE. The form of RESTART-CASE is quite similar to a HANDLER-CASE except the names of restarts are just names, not necessarily the names of condition types. In general, a restart name should describe the action the restart takes. In parse-log-file, you can call the restart skip-log-entry since that's what it does. The new version will look like this:

(defun parse-log-file (file)
  (with-open-file (in file :direction :input)
    (loop for text = (read-line in nil nil) while text
       for entry = (restart-case (parse-log-entry text)
                     (skip-log-entry () nil))
       when entry collect it)))

If you invoke this version of parse-log-file on a log file containing corrupted entries, it won't handle the error directly; you'll end up in the debugger. However, there among the various restarts presented by the debugger will be one called skip-log-entry, which, if you choose it, will cause parse-log-file to continue on its way as before. To avoid ending up in the debugger, you can establish a condition handler that invokes the skip-log-entry restart automatically.

The advantage of establishing a restart rather than having parse-log-file handle the error directly is it makes parse-log-file usable in more situations. The higher-level code that invokes parse-log-file doesn't have to invoke the skip-log-entry restart. It can choose to handle the error at a higher level. Or, as I'll show in the next section, you can add restarts to parse-log-entry to provide other recovery strategies, and then condition handlers can choose which strategy they want to use.

But before I can talk about that, you need to see how to set up a condition handler that will invoke the skip-log-entry restart. You can set up the handler anywhere in the chain of calls leading to parse-log-file. This may be quite high up in your application, not necessarily in parse-log-file's direct caller. For instance, suppose the main entry point to your application is a function, log-analyzer, that finds a bunch of logs and analyzes them with the function analyze-log, which eventually leads to a call to parse-log-file. Without any error handling, it might look like this:

(defun log-analyzer ()
  (dolist (log (find-all-logs))
    (analyze-log log)))

The job of analyze-log is to call, directly or indirectly, parse-log-file and then do something with the list of log entries returned. An extremely simple version might look like this:

(defun analyze-log (log)
  (dolist (entry (parse-log-file log))
    (analyze-entry entry)))

where the function analyze-entry is presumably responsible for extracting whatever information you care about from each log entry and stashing it away somewhere.

Thus, the path from the top-level function, log-analyzer, to parse-log-entry, which actually signals an error, is as follows:

Assuming you always want to skip malformed log entries, you could change this function to establish a condition handler that invokes the skip-log-entry restart for you. However, you can't use HANDLER-CASE to establish the condition handler because then the stack would be unwound to the function where the HANDLER-CASE appears. Instead, you need to use the lower-level macro HANDLER-BIND. The basic form of HANDLER-BIND is as follows:

(handler-bind (binding*) form*)

where each binding is a list of a condition type and a handler function of one argument. After the handler bindings, the body of the HANDLER-BIND can contain any number of forms. Unlike the handler code in HANDLER-CASE, the handler code must be a function object, and it must accept a single argument. A more important difference between HANDLER-BIND and HANDLER-CASE is that the handler function bound by HANDLER-BIND will be run without unwinding the stack--the flow of control will still be in the call to parse-log-entry when this function is called. The call to INVOKE-RESTART will find and invoke the most recently bound restart with the given name. So you can add a handler to log-analyzer that will invoke the skip-log-entry restart established in parse-log-file like this:5

(defun log-analyzer ()
  (handler-bind ((malformed-log-entry-error
                  #'(lambda (c)
                      (invoke-restart 'skip-log-entry))))
    (dolist (log (find-all-logs))
      (analyze-log log))))

In this HANDLER-BIND, the handler function is an anonymous function that invokes the restart skip-log-entry. You could also define a named function that does the same thing and bind it instead. In fact, a common practice when defining a restart is to define a function, with the same name and taking a single argument, the condition, that invokes the eponymous restart. Such functions are called restart functions. You could define a restart function for skip-log-entry like this:

(defun skip-log-entry (c)
  (invoke-restart 'skip-log-entry))

Then you could change the definition of log-analyzer to this:

(defun log-analyzer ()
  (handler-bind ((malformed-log-entry-error #'skip-log-entry))
    (dolist (log (find-all-logs))
      (analyze-log log))))

As written, the skip-log-entry restart function assumes that a skip-log-entry restart has been established. If a malformed-log-entry-error is ever signaled by code called from log-analyzer without a skip-log-entry having been established, the call to INVOKE-RESTART will signal a CONTROL-ERROR when it fails to find the skip-log-entry restart. If you want to allow for the possibility that a malformed-log-entry-error might be signaled from code that doesn't have a skip-log-entry restart established, you could change the skip-log-entry function to this:

(defun skip-log-entry (c)
  (let ((restart (find-restart 'skip-log-entry)))
    (when restart (invoke-restart restart))))

FIND-RESTART looks for a restart with a given name and returns an object representing the restart if the restart is found and NIL if not. You can invoke the restart by passing the restart object to INVOKE-RESTART. Thus, when skip-log-entry is bound with HANDLER-BIND, it will handle the condition by invoking the skip-log-entry restart if one is available and otherwise will return normally, giving other condition handlers, bound higher on the stack, a chance to handle the condition.

Providing Multiple Restarts

Since restarts must be explicitly invoked to have any effect, you can define multiple restarts, each providing a different recovery strategy. As I mentioned earlier, not all log-parsing applications will necessarily want to skip malformed entries. Some applications might want parse-log-file to include a special kind of object representing malformed entries in the list of log-entry objects; other applications may have some way to repair a malformed entry and may want a way to pass the fixed entry back to parse-log-entry.

To allow more complex recovery protocols, restarts can take arbitrary arguments, which are passed in the call to INVOKE-RESTART. You can provide support for both the recovery strategies I just mentioned by adding two restarts to parse-log-entry, each of which takes a single argument. One simply returns the value it's passed as the return value of parse-log-entry, while the other tries to parse its argument in the place of the original log entry.

(defun parse-log-entry (text)
  (if (well-formed-log-entry-p text)
    (make-instance 'log-entry ...)
    (restart-case (error 'malformed-log-entry-error :text text)
      (use-value (value) value)
      (reparse-entry (fixed-text) (parse-log-entry fixed-text)))))

The name USE-VALUE is a standard name for this kind of restart. Common Lisp defines a restart function for USE-VALUE similar to the skip-log-entry function you just defined. So, if you wanted to change the policy on malformed entries to one that created an instance of malformed-log-entry, you could change log-analyzer to this (assuming the existence of a malformed-log-entry class with a :text initarg):

(defun log-analyzer ()
  (handler-bind ((malformed-log-entry-error
                  #'(lambda (c)
                      (use-value
                       (make-instance 'malformed-log-entry :text (text c))))))
    (dolist (log (find-all-logs))
      (analyze-log log))))

You could also have put these new restarts into parse-log-file instead of parse-log-entry. However, you generally want to put restarts in the lowest-level code possible. It wouldn't, though, be appropriate to move the skip-log-entry restart into parse-log-entry since that would cause parse-log-entry to sometimes return normally with NIL, the very thing you started out trying to avoid. And it'd be an equally bad idea to remove the skip-log-entry restart on the theory that the condition handler could get the same effect by invoking the use-value restart with NIL as the argument; that would require the condition handler to have intimate knowledge of how the parse-log-file works. As it stands, the skip-log-entry is a properly abstracted part of the log-parsing API.

Other Uses for Conditions

While conditions are mainly used for error handling, they can be used for other purposes--you can use conditions, condition handlers, and restarts to build a variety of protocols between low- and high-level code. The key to understanding the potential of conditions is to understand that merely signaling a condition has no effect on the flow of control.

The primitive signaling function SIGNAL implements the mechanism of searching for an applicable condition handler and invoking its handler function. The reason a handler can decline to handle a condition by returning normally is because the call to the handler function is just a regular function call--when the handler returns, control passes back to SIGNAL, which then looks for another, less recently bound handler that can handle the condition. If SIGNAL runs out of condition handlers before the condition is handled, it also returns normally.

The ERROR function you've been using calls SIGNAL. If the error is handled by a condition handler that transfers control via HANDLER-CASE or by invoking a restart, then the call to SIGNAL never returns. But if SIGNAL returns, ERROR invokes the debugger by calling the function stored in *DEBUGGER-HOOK*. Thus, a call to ERROR can never return normally; the condition must be handled either by a condition handler or in the debugger.

Another condition signaling function, WARN, provides an example of a different kind of protocol built on the condition system. Like ERROR, WARN calls SIGNAL to signal a condition. But if SIGNAL returns, WARN doesn't invoke the debugger--it prints the condition to *ERROR-OUTPUT* and returns NIL, allowing its caller to proceed. WARN also establishes a restart, MUFFLE-WARNING, around the call to SIGNAL that can be used by a condition handler to make WARN return without printing anything. The restart function MUFFLE-WARNING finds and invokes its eponymous restart, signaling a CONTROL-ERROR if no such restart is available. Of course, a condition signaled with WARN could also be handled in some other way--a condition handler could "promote" a warning to an error by handling it as if it were an error.

For instance, in the log-parsing application, if there were ways a log entry could be slightly malformed but still parsable, you could write parse-log-entry to go ahead and parse the slightly defective entries but to signal a condition with WARN when it did. Then the larger application could choose to let the warning print, to muffle the warning, or to treat the warning like an error, recovering the same way it would from a malformed-log-entry-error.

A third error-signaling function, CERROR, provides yet another protocol. Like ERROR, CERROR will drop you into the debugger if the condition it signals isn't handled. But like WARN, it establishes a restart before it signals the condition. The restart, CONTINUE, causes CERROR to return normally--if the restart is invoked by a condition handler, it will keep you out of the debugger altogether. Otherwise, you can use the restart once you're in the debugger to resume the computation immediately after the call to CERROR. The function CONTINUE finds and invokes the CONTINUE restart if it's available and returns NIL otherwise.

You can also build your own protocols on SIGNAL--whenever low-level code needs to communicate information back up the call stack to higher-level code, the condition mechanism is a reasonable mechanism to use. But for most purposes, one of the standard error or warning protocols should suffice.

You'll use the condition system in future practical chapters, both for regular error handling and, in Chapter 25, to help in handling a tricky corner case of parsing ID3 files. Unfortunately, it's the fate of error handling to always get short shrift in programming texts--proper error handling, or lack thereof, is often the biggest difference between illustrative code and hardened, production-quality code. The trick to writing the latter has more to do with adopting a particularly rigorous way of thinking about software than with the details of any particular programming language constructs. That said, if your goal is to write that kind of software, you'll find the Common Lisp condition system is an excellent tool for writing robust code and one that fits quite nicely into Common Lisp's incremental development style.

Writing Robust Software

In the next chapter I'll give a quick overview of some of the 25 special operators you haven't had a chance to use yet, at least not directly.


1Throws or raises an exception in Java/Python terms

2Catches the exception in Java/Python terms

3In this respect, a condition is a lot like an exception in Java or Python except not all conditions represent an error or exceptional situation.

4In some Common Lisp implementations, conditions are defined as subclasses of STANDARD-OBJECT, in which case SLOT-VALUE, MAKE-INSTANCE, and INITIALIZE-INSTANCE will work, but it's not portable to rely on it.

5The compiler may complain if the parameter is never used. You can silence that warning by adding a declaration (declare (ignore c)) as the first expression in the LAMBDA body.

吃维生素c和维生素e有什么好处 天荒地老什么意思 亨特综合症是什么病 骨骼惊奇什么意思 人民检察院是国家的什么机关
女生的名字叫什么好听 血小板低吃什么食物补得快 什么不生四字成语 抖机灵是什么意思 右冠优势型是什么意思
ii是什么意思 舌头上有红点点是什么原因 怀孕第一个月最怕什么 侵犯什么意思 1069是什么意思
三无产品是指什么 乌龙茶适合什么季节喝 梦见离家出走是什么意思 嗑药是什么意思 低密度脂蛋白胆固醇偏高是什么意思
失眠有什么办法解决hcv8jop2ns0r.cn 廊坊有什么好玩的地方hcv8jop9ns7r.cn spi是什么hcv7jop7ns0r.cn 为什么要做试管婴儿hcv9jop7ns1r.cn 算力是什么hcv8jop0ns9r.cn
银杯子喝水有什么好处与坏处hcv7jop9ns5r.cn 什么是白色家电hcv8jop4ns8r.cn 肾阴虚吃什么药最好hcv8jop8ns5r.cn 一直打嗝什么原因hcv8jop3ns9r.cn 大腿抽筋是什么原因引起的hcv8jop1ns4r.cn
舌头有麻木感什么原因hcv9jop8ns0r.cn 泄身是什么意思dayuxmw.com 肾阴虚什么症状hcv8jop7ns7r.cn 刷酸是什么hcv9jop7ns9r.cn 奶茶三兄弟是什么hcv8jop6ns1r.cn
pid是什么意思hcv8jop2ns9r.cn 猫咪喜欢什么颜色hcv7jop9ns6r.cn 桦树茸有什么功效hcv8jop7ns5r.cn 一毛不拔是什么动物hcv9jop4ns4r.cn 捞面条配什么菜好吃hcv8jop7ns1r.cn
百度