【问题标题】:What is the difference between my class and our class in Raku(perl6)?我的班级和我们在 Raku(perl6) 中的班级有什么区别?
【发布时间】:2016-02-20 16:53:47
【问题描述】:

我已阅读规范,但我仍然对 my class[our] class 的区别感到困惑。有什么区别,何时使用?

【问题讨论】:

    标签: raku


    【解决方案1】:

    my 范围声明器暗示词法范围:在其声明之后,符号对当前花括号集中的代码可见。因此,我们倾向于将一对花括号内的区域称为“词汇范围”。例如:

    sub foo($p) {
        # say $var;       # Would be a compile time error, it's not declared yet
        my $var = 1;
        if $p {
            $var += 41;   # Inner scope, $var is visible
        }
        return $var;      # Same scope that it was declared in, $var is visible
    }
    # say $var;           # $var is no longer available, the scope ended
    

    由于变量的可见性与其在代码中的位置直接相关,词法作用域对于推理程序非常有帮助。这适用于:

    • 程序员(既是因为他们自己对程序的推理,也是因为当事物具有词法范围时可以检测和报告更多错误)
    • 编译器(词法作用域允许更轻松和更好的优化)
    • IDE 等工具(分析和推理具有词法范围的事物更容易处理)

    在将成为 Raku 的语言的设计过程的早期,子例程没有默认具有词法范围(并且像在 Perl 中一样具有 our 范围),但是人们意识到词法范围是更好的默认值。进行子例程调用总是尝试解析具有词法范围的符号,这意味着可以在编译时报告未声明的子例程。此外,词汇范围内的符号集在编译时是固定的,并且在像子例程这样的声明性构造的情况下,例程以只读方式绑定到该符号。这也允许诸如多次调度的编译时解析、编译时参数检查等。 Raku 语言的未来版本很可能会针对词法范围的程序元素指定越来越多的编译时检查。

    如果词法作用域这么好,为什么our(也称为包)作用域存在?简而言之,因为:

    1. 有时我们希望比给定的词汇范围更广泛地共享内容。我们可以将所有内容声明为词法,然后标记我们想要与is export 共享的内容,但是..
    2. 一旦我们到了使用大量不同库的地步,让所有东西都尝试将内容导出到消费者的单一词法范围内,可能会导致很多冲突

    包允许符号的命名空间。例如,如果我想在同一代码中同时为 HTTP 和 WebSockets 使用 Cro 客户端,我可以很高兴地同时使用它们,并将它们分别称为 Cro::HTTP::ClientCro::WebSocket::Client

    包由包声明符引入,例如classmodulegrammar和(带有警告)roleour 声明将在封闭的包结构中进行安装。

    这些包最终存在于一个名为GLOBAL 的顶级包中——这很合适,因为它们实际上是全局可见的。如果我们声明一个our-scoped 变量,那么它就是一个全局变量(尽管希望是一个命名空间变量),关于它已经写了足够多的内容,我们知道我们应该停下来思考一下,并想知道全局变量是否是最好的 API 决策(因为最终通过 GLOBAL 可见的所有内容都是 API 决定)。

    的事情有点模糊,然而,我们可以有词法包。这些是未安装在GLOBAL 中的软件包。我发现这些在进行 OO 编程时非常有用。例如,我可能有:

    # This class that ends up in GLOBAL...
    class Cro::HTTP::Client {
        # Lexically scoped classes, which are marked `my` and thus hidden
        # implementation details. This means I can refactor them however I
        # want, and never have to worry about downstream fallout!
        my class HTTP1Pipeline {
            # Implementation...
        }
        my class HTTP2Pipeline {
            # Implementation...
        }
    
        # Implementation...
    }
    

    词法包也可以嵌套并包含our-范围变量,但最终不会全局可见(除非我们以某种方式选择将它们泄露出去)。

    不同的 Raku 程序元素已被赋予一个默认范围:

    • 子例程默认为词法 (my) 范围
    • 方法默认为has 范围(仅通过方法调度可见)
    • 类型(类、角色、语法、子集)和模块声明默认为包 (our) 范围
    • 常量和枚举默认为包 (our) 范围

    实际上,最常被共享的东西默认为包范围,其余的则不是。 (变量确实迫使我们明确地选择一个范围,但是最常见的选择也是最短的类型。)

    就我个人而言,我很犹豫是否要让某个东西比语言默认值更明显,但我经常会让它们不那么明显(例如,my用于内部使用的常量,以及用于构造实现细节的类)。当我可以通过在全局可见包中公开 our-scoped 变量来做某事时,我仍然通常更喜欢将其设置为 my-scoped 并提供 sub(导出)或 method(可见凭借在包范围内的class) 来控制对它的访问,从而为自己在未来购买一些灵活性。我认为现在可以做出错误的选择,如果我给自己空间让他们在未来更正确,而不会给任何人带来不便。 :-)

    总结:

    • 使用my 作用域作为实现细节的所有内容
    • 还可以将 my 范围用于您计划使用 export 的内容,但请记住,将符号导出到消费者的单个词法范围中并有名称冲突的风险,因此在导出特别通用的名称时要慎重
    • our 用于要共享的内容,以及在需要使用命名空间以避免冲突时使用
    • 无论如何,我们最想共享的元素默认为 our 范围,因此明确写 our 应该让我们停下来思考

    【讨论】:

      【解决方案2】:

      myour 的区别主要在生成符号表时相关。例如:

      my $a;           # Create symbol <$a>  at top level
      package Foo {    # Create symbol <Foo> at top level
         my  $b;       # Create symbol <$b>  in Foo scope 
         our $c;       # Create symbol <$c>  in Foo scope
      }                #           and <Foo::<$c>> at top level
      

      在实践中,这意味着our 范围内的任何内容都可以通过为包标识符添加前缀($Foo::cFoo::&lt;$c&gt; 是同义词)轻松与外界共享,而my 范围内的任何内容都不容易可用——尽管您当然可以通过 getter subs 等方式提供对它的访问权限。

      大多数时候你会想使用my。大多数变量只属于它们当前的范围,没有人有任何业务达到顶峰。但our 在某些情况下可能很有用:

      • 不会毒害符号表的常量(这就是为什么实际上使用constant 暗示 our 范围)。因此,您可以使用 package Colors { constant red = 1; constant blue = 2; } 制作更多 C 风格的枚举/常量,然后将它们引用为 Colors::red
      • 应该可以访问但不需要导出的类或子类(或者不应该因为与内置函数或其他模块重叠的符号)。导出符号可能很棒,但有时使用包/模块命名空间来提醒你什么东西也很好。因此,这也是在运行时通过 subs 管理选项的好方法:CoolModule::set-preferences( ... )。 (尽管在这里也可以使用动态变量来产生很好的效果)。

      我相信其他人会在其他时候评论our 范围很有用,但这些是我自己的经验。

      【讨论】:

        【解决方案3】:

        与变量一样,my 以词法方式绑定名称,而our 还在周围的包中创建一个条目。

        module M {
            our class Foo {}
            class Bar {} # same as above, really
            my class Baz {}
        }
        
        say M::Foo; # ok
        say M::Bar; # still ok
        say M::Baz; # BOOM!
        

        对模块内部的类使用my。当然,您仍然可以通过将这些本地符号标记为 is export 来使这些本地符号可用于导入代码。

        【讨论】:

        • 公开和导出的角色 Rational 怎么样? github.com/rakudo/rakudo/blob/…
        • @teodozjan:它不需要公开或导出,因为它是“设置”的一部分。您编写的所有代码都被包含核心内置函数的外部词法范围隐式包围,例如Rational
        猜你喜欢
        • 1970-01-01
        • 2018-05-20
        • 1970-01-01
        • 2019-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-17
        • 1970-01-01
        相关资源
        最近更新 更多