我什么时候想恢复Perl 6异常? [英] When would I want to resume a Perl 6 exception?

查看:53
本文介绍了我什么时候想恢复Perl 6异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

也许我真正的问题是此功能是否适合 Learning Perl 6 "?基于此Perl 6 CATCH块是否能够在词法范围内更改变量?,看来最简单的示例可能是除了一个简单的例子.

在这个问题中,我正在使用某种似乎很愚蠢或更好的方法来解决该特定问题,因为我是在使用该功能而不是解决问题.

在文档中已将警告用作特殊形式的异常(控制异常"),您可以在其中获取消息,可以根据需要捕获消息,但是也可以忽略它,并且消息将自行恢复(尽管我当时很想在哪里应该捕获Perl 6警告控制异常?).

除此之外,我还在考虑呼叫者可以处理被呼叫者范围之外的失败的事情.例如,重新连接到数据库,修复丢失的目录以及被调用方不负责的其他外部资源问题.

在阅读其他语言中的此类信息时,主要建议是不要使用它们,因为在现实世界"中,编程人员无论如何都不会实际解决问题.

下一步要恢复的C#异常处理程序的答案似乎表明这是不好的做法,而且代码很丑陋.我当然还没有找到在被调用方中隐藏一堆代码的方法.

尽管我不认为这是一个很好的方法,也没有推荐给初学者的方法,但我还是举了这个例子.程序启动时会查找PID文件.如果找到一个,则抛出异常.处理该异常会检查另一个实例是否仍在运行,这可能会引发不同类型的异常.并且,有一种可以处理文件IO问题.诀窍在于,如果另一个程序未运行(但将其PID文件留在后面), X :: MyProgram :: FoundSemaphore 可以恢复.

  class X :: MyProgram :: FoundSemaphore是异常{有$ .filename;具有$ .this-pid = $ * PID;具有$ .that-pid = $!filename.lines(1);方法要点{找到了一个现有的信号量文件(pid {.that-pid})"}}类X :: MyProgram :: StillRunning是异常{有$ .that-pid;有$ .os-error;方法要点{此程序已在运行(pid {self.that-pid})"}}X :: MyProgram :: IO :: OpenFile类为异常{有$ .filename;方法要点{此程序已在运行(pid {self.that-pid})"}}sub create-semaphore {状态$ filename ="$ * PROGRAM.pid";END {取消链接$ filename}死X :: MyProgram :: FoundSemaphore.new(:文件名($文件名))如果$ filename.IO.e;我的$ fh =尝试打开$ filename,:w;#公开抛出Ad :: Hoc,这可能会更有帮助死X :: MyProgram :: IO :: OpenFile.new(:文件名($文件名),:os-error($!),#角色X :: IO-ish),除非$ fh;$ fh.print:$ * PID;}开始 {尝试 {抓住 {当X :: MyProgram :: FoundSemaphore {我的$ proc =运行qqw/kill -0 {.that-pid}/;X :: MyProgram :: StillRunning.new(:that-pid(.that-pid)).抛出如果$ proc.so;#退出代码为0,因此为True取消链接.filename;.恢复;}默认{说抓到{.^ name}";出口 }}create-semaphore();}}sub MAIN(Int $ delay = 10){将"$ * PID睡眠$ delay秒";睡眠$ delay;} 

解决方案

可恢复的异常肯定不是我在Perl 6中发现的.我认为我没有在用户空间"代码中使用过它们完全没有.事实证明,可恢复异常是实现 emit 功能的正确方法,该功能在 supply react 块中使用.在 gather 中使用的 take 函数也使用可恢复的异常来实现,并且-您已经发现- warn 会使用它们.

我怀疑其中的最后一个- warn -是典型的Perl 6用户感兴趣的唯一情况.捕获警告并将其发送到其他地方-可能发送到日志文件或日志服务器-是一件相当合理的事情.就学习Perl 6而言,这可能是可恢复异常的明显有用示例.

我认为很重要的一点是,所有利用Perl 6本身中的可恢复异常的用例都被归类为控制异常".控制异常本质上是实现级别的常规异常:它们涉及控制的非本地转移.它们在语言级别上是不同的,因为如果您的 emit take warn next last 等停止工作,原因是带有 default CATCH 块吞没了控制异常!

但是,这也有点按我说的做,不如我做":虽然Perl 6乐于使用异常系统来实现非本地流控制,但它在某种程度上阻碍了它的发展.语言,而不是将其作为要做的事的榜样.并有充分的理由:通常,使用异常执行流控制的代码很难遵循,而对于可恢复异常则要加倍.另一个巨大的风险是,这样的异常可以被使用裸 try CATCH default 的代码吞没-使它成为一个在更大的代码库中要做的事情相当脆弱.

我认为可重复使用的异常的最佳用途最终将成为用户根本不考虑异常的事情的实现策略,就像 take的情况一样 emit (大多数情况下是 warn ).并且,与可恢复异常的现有示例一样,要恢复的事物将是专门设计用于在可恢复情况下抛出的异常类型,并且仅在需要这样做的情况下使用.在Perl 6提供定义自定义控件异常的方法之前,我还是不太愿意这样做. try / default 吞咽问题使它太脆弱了.

Perhaps my real question is "Is this a feature appropriate for Learning Perl 6"? Based on Should this Perl 6 CATCH block be able to change variables in the lexical scope?, it seems the simplest example might be beyond a simple example.

In that question I was working with something that appears to be silly or better down in another way for that particular problem because I was playing with the feature rather than solving a problem.

There's the documented use of warnings as special sort of exceptions ("control exceptions") where you get the message, can catch it if you like, but can also ignore it and it will resume on its own (although I was rather stupid about this in Where should I catch a Perl 6 warning control exception?).

Beyond that, I'm thinking about things where the caller can handle a failure outside of the scope of the callee. For instance, reconnecting to a database, fixing missing directories, and other external resource issues that the callee doesn't have responsibility for.

In reading about this sort of thing in other languages, the advice has mostly been to not use them because in "real world" programming people tend to not actually handle the problem anyway.

The answer to C# exception handler resume next seems to say it's poor practice and ugly code. I certainly haven't figured out a way to hide a bunch of code in the callee.

I hacked up this example, although I'm not convinced it's a good way to do it or something to recommend to beginners. The program looks for a PID file when it starts. If it finds one, it throws an exception. Handling that exception checks that the other instance is still running, which might throw a different type of exception. And, there's the one to handle the file IO problems. The trick is that the X::MyProgram::FoundSemaphore can resume if the other program isn't running (but left its PID file behind).

class X::MyProgram::FoundSemaphore is Exception {
    has $.filename;
    has $.this-pid = $*PID;
    has $.that-pid = $!filename.lines(1);

    method gist {
        "Found an existing semaphore file (pid {.that-pid})"
        }
    }

class X::MyProgram::StillRunning is Exception {
    has $.that-pid;
    has $.os-error;

    method gist {
        "This program is already running (pid {self.that-pid})"
        }
    }

class X::MyProgram::IO::OpenFile is Exception {
    has $.filename;

    method gist {
        "This program is already running (pid {self.that-pid})"
        }
    }

sub create-semaphore {
    state $filename = "$*PROGRAM.pid";
    END { unlink $filename }

    die X::MyProgram::FoundSemaphore.new(
        :filename($filename)
        ) if $filename.IO.e;

    my $fh = try open $filename, :w;

    # open throws Ad::Hoc, which could be more helpful
    die X::MyProgram::IO::OpenFile.new(
        :filename($filename),
        :os-error($!),  # role X::IO-ish
        ) unless $fh;

    $fh.print: $*PID;
    }

BEGIN {
    try {
        CATCH {
            when X::MyProgram::FoundSemaphore {
                my $proc = run qqw/kill -0 {.that-pid}/;
                X::MyProgram::StillRunning.new(
                    :that-pid(.that-pid) ).throw
                    if $proc.so; # exit code is 0, so, True
                unlink .filename;
                .resume;
                }
            default { say "Caught {.^name}"; exit }
            }

        create-semaphore();
        }
    }

sub MAIN ( Int $delay = 10 ) {
    put "$*PID sleeping for $delay seconds";
    sleep $delay;
    }

解决方案

Resumable exceptions certainly aren't something I've found myself reaching for in Perl 6. I don't think I've used them in "userspace" code at all yet. A resumable exception turned out to be the right way to implement the emit function, used in supply and react blocks. The take function used in gather is also implemented using a resumable exception, and - as you've already discovered - warn uses them.

I suspect the last of these - warn - is the only case that the typical Perl 6 user will be interested in. Capturing warnings and sending them elsewhere - perhaps to a log file or log server - is a fairly reasonable thing to need to do. So far as Learning Perl 6 goes, that is probably the obvious useful example of a resumable exception.

I think it's significant that all of the use-cases that take advantage of resumable exceptions in Perl 6 itself turn out to be things classified as "control exceptions". Control exception are essentially normal exceptions at an implementation level: they involve a non-local transfer of control. They are made distinct at the language level because it would be rather awkward to use Perl 6 if your emit, take, warn, next, last and so forth stopped working because of a CATCH block with a default swallowing the control exceptions!

However, it's also a bit of "do as I say, not as I do": while Perl 6 is happy to use the exception system to implement non-local flow control, it somewhat fences it off in a dusty corner of the language rather than holding it up as an example of something to do. And for good reason: usually, code that uses exceptions to do flow control is hard to follow, and that goes double for resumable exceptions. The other big risk is that such exceptions can be swallowed up by code that uses a bare try or a CATCH with a default - making it a rather fragile thing to do in a larger codebase.

I'd imagine the best uses of resumable exceptions will turn out to be as an implementation strategy for things that users won't think about in terms of exceptions at all - just as is the case with take and emit (and, most of the time, warn). And, as with the existing examples of resumable exceptions, the thing being resumed will be an exception type that was specifically designed to be thrown in resumable situations and only used in cases where that is a sensible thing to do. Until Perl 6 provides a way to define custom control exceptions, however, I'd be rather reluctant to do this; the try/default swallowing issue makes it just too fragile.

这篇关于我什么时候想恢复Perl 6异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆