在Raku中的模块中像Prelude模块一样使用Haskell


11

我正在编写一个包含某些部分的图形包,并且有一些运算符和数据类型分散在整个进程中。但是我不希望用户每次都添加相应的模块,因为这很混乱,例如,我将在这样的不同路径中有一个Point类,一个Monoid角色和一个Style

unit module Package::Data::Monoid;
# $?FILE = lib/Package/Data/Monoid.pm6

role Monoid {...}
unit module Package::Data::Point;
# $?FILE = lib/Package/Data/Point.pm6

class Point {...}
unit module Package::Data::Style;
# $?FILE = lib/Package/Data/Style.pm6

class Style {...}

我希望有一个haskell类似的前奏,lib/Package/Prelude.pm6 可以写出这样的脚本

use Package::Prelude;

# I can use Point right away, Style etc...

而不是做

use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;

# I can too use point right away, but for users not knowing the
# inner workings it's too overwhelming

我尝试了很多事情:

  • 这个版本没有给我正确的效果,我必须输入指向的整个路径,即Package::Data::Point...
unit module Package::Prelude;
# $?FILE = lib/Package/Prelude.pm6
use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;
  • 这个版本给我带来了Point便利,但是我遇到了运算符等问题,我也想自动从上述示例包中的导出例程中添加所有内容。
# $?FILE = lib/Package/Prelude.pm6
use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;

sub EXPORT {
  hash <Point> => Point
     , <Style> => Style
     , <mappend> => &mappend
     ...
}

你们是否知道一种更好且快速的方式来获得类似前奏的文件?


您可以使用unit class Package::Data::Point。您不必使用module
布拉德·吉尔伯特

Answers:


12

使用EXPORT是正确的方向。要了解的关键事项是:

  • 导入是词汇
  • 我们可以使用自省来获取和访问当前词法范围内的符号

所以配方是:

  • use 内部的所有模块 EXPORT
  • 然后提取所有导入的符号,并从中返回它们作为结果 EXPORT

例如,我创建一个模块Foo::Point,包括一个运算符和一个类:

unit module Foo::Point;

class Point is export {
    has ($.x, $.y);
}

multi infix:<+>(Point $a, Point $b) is export {
    Point.new(x => $a.x + $b.x, y => $a.y + $b.y)
}

并且,仅为了演示它可以与多个模块一起使用,还有一个Foo::Monad

unit module Foo::Monad;

class Monad is export {
    method explain() { say "Just think of a burrito..." }
}

目的是使这项工作:

use Foo::Prelude;
say Point.new(x => 2, y => 4) + Point.new(x => 3, y => 5);
Monad.explain;

可以通过编写Foo::Prelude包含以下内容的来实现:

sub EXPORT() {
    {
        use Foo::Point;
        use Foo::Monad;
        return ::.pairs.grep(*.key ne '$_').Map;
    }
}

这里有一些奇怪的地方可以解释:

  1. 一个sub具有隐含的声明$_$/$!。当导出模块时,导出这些将导致编译时符号冲突错误use。一个块只有一个隐式$_。因此,我们使用嵌套的裸块使生活更加轻松。
  2. grep是为了确保我们不导出隐式声明的$_符号(由于嵌套块,这是我们唯一需要关心的符号)。
  3. ::是引用当前范围的一种方式(词源:::是包分隔符)。::.pairs从而获得Pair当前范围内每个符号的对象。

在将来的Raku语言版本中可能会出现一种推测的重新导出机制,该机制将消除对这种样板的需求。


最后,这正是我想要的行为,非常感谢!
margolari
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.