my enum Example17 <>

05 June 2005


Enum, enumerate 的缩写,可翻译为枚举。
在 Larry Wall's A12 中,Enum 用于简化类似下面这样的声明:

my subtype DayOfWeek of Int where 0..6;
my subtype DayOfWeek::Sunday    of DayOfWeek where 0;
my subtype DayOfWeek::Monday    of DayOfWeek where 1;
my subtype DayOfWeek::Tuesday   of DayOfWeek where 2;
my subtype DayOfWeek::Wednesday of DayOfWeek where 3;
my subtype DayOfWeek::Thursday  of DayOfWeek where 4;
my subtype DayOfWeek::Friday    of DayOfWeek where 5;
my subtype DayOfWeek::Saturday  of DayOfWeek where 6;
这是一种很常见的结构。一个类只允许几个已知的值,这就是枚举的大概意思。上面的代码可以简化为:
my enum DayOfWeek of int
        «Sunday Monday Tuesday Wednesday Thursday Friday Saturday»;
如果你想把 Sunday 为 7,且采用缩写的话,可以这么做:
my enum DayOfWeek <<:Mon(1) Tue Wed Thu Fri Sat Sun>>;

枚举这种结构是很常见的,比如罗马数字或十六进制等:

my enum roman [i => 1,   v => 5,
                x => 10,  l => 50,
                c => 100, d => 500,
                m => 1000];
my Scalar hex «:zero(0) one two three four five six seven eight nine
                   :ten eleven twelve thirteen fourteen fifteen»;

Usage

enum 可以表现为一个 role 或 property, 而 enum 的值可以表现为一个子类型,方法或者一个普通值(不带参数的 sub)。
我们以 enum day <Sun Mon Tue Wed Thu Fri Sat> 为例:
my $x does Tue; # Tue 做为 role
$x does day<Tue>; # 上一句其实等同于这一句
say $x.day; # 3, enum 定义了 day::Tue 为 3
$x ~~ day; $x ~~ Tue; $x ~~ day::Tue; # 都返回 ture
$x ~~ Wed; # 返回 false
前面的
role Example13 { # Perl6 } 里补充的 but 是 enum 最好的使用方法。
$x = "Today" but Tue; # 等同与 $x = "Today" but day::Tue; 或 $x = "Today" but day<Tue>;
用前面 but 的解释就是:
$x = "Today";
$x does day;
$x.day = &day::('Tue');
下面这些都将返回 true
$x.does(Tue)
$x.does(day)
$x.day == Tue
day($x) == Tue
Tue($x)
$x.Tue

我们有两个内建的 enum:

our bit enum *bool <false true>;
our bit enum *taint <untainted tainted>;

Others

关于冲突与覆盖,请参考 Perl6::Bible::A12Perl6::Bile::S12.

Google Sitemap

04 June 2005


Google 最近推出了新业务,Google Sitemap.
主要是给 webmaster 在自己的网站上建一个 XML 文档,然后 Google Sprider 来抓取这个文档查看网站更新了什么。
我不明白为什么不直接抓取 RSS/Atom, 这样就可以省去重新写一个文档了。

或许 RSS/Atom 的冗长资料太多了,Google Sitemap 所要求的 XML 标签很少。详细的要求请参阅 https://www.google.com/webmasters/sitemaps/docs/en/protocol.html
我在自己的 Eplanet 上简单 hack 了下。
因为我自己的要求很少(比如不用过滤 XML 的特殊标签),所以代码很简单(没有使用任何 XML:: 模块)。
代码很简单,看完所要求的格式就可以了。

# declare the vars
my $data; # the data to save in

# normal settings
my $file = $c->config->{build_root} . "/sitemaps.xml";
my $site_prefix = 'http://www.fayland.org/journal';
    
# got the data from database
my @topics = Eplanet::M::CDBI::Cms->retrieve_from_sql(qq{
    1=1 ORDER BY cms_id DESC LIMIT 0, 20
});
    
# the data format see https://www.google.com/webmasters/sitemaps/docs/en/protocol.html
$data = '<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.google.com/schemas/sitemap/0.84">'."\n";

# for every topic
foreach my $topic (@topics) {
    my $topic_name = $topic->get('cms_file');
    my $date = $topic->{'cms_mod_data'} || $topic->{'cms_cre_data'};
    # format the date
    $date = &format_date($date);
    
    # add to the data
    $data .= "<url>
    <loc>$site_prefix/${topic_name}.html</loc>
    <lastmod>$date</lastmod>
</url>\n";
}
    
# finish the data format
$data .= '</urlset>';

# save to the file
open(FH, ">$file");
flock(FH, 2);
print FH $data;
close(FH);

完毕后就提交 URL. Done!


my subtype Examples16 of Perl6 where /^kissu$/

04 June 2005


应该说类与类型是不一样的,类的英文为 class, 而类型的英文为 type.
虽然在 Perl6 中类和角色(role)通常表现得与类并无不同。可以说 Perl6 中的类型/Type 由类/class, 角色/role, 和子类型/subtype 组成。
通常我们通过子类(subclass)来继承类并添加一些新的属性或方法(增加能力),而我们将通过子类型来限制一些特性(可理解为去掉能力)。

最简单的比如我们要一个只能是偶数的子类型。我们就可以这么定义:

subtype EvenNum of Num where { $^n % 2 == 0 }
首先它继承了类型 Num 然后通过 where 来限定它为偶数($^n 为前面讲过的占位符参数)。
my EvenNum $n;
$n = 2;             # Okay
$n = -2;            # Okay
$n = 0;             # Okay
$n = 3;             # 出错
另一个例子:
subtype Str_not2b of Str where /^[isnt|arent|amnot|aint]$/;
                                                                            
my Str_not2b $hamlet;
$hamlet = 'isnt';   # Okay because 'isnt' ~~ /^[isnt|arent|amnot|aint]$/
$hamlet = 'amnt';   # Bzzzzzzzt!   'amnt' !~ /^[isnt|arent|amnot|aint]$/

detailed

  • 通常定义子类型的方法:
    my subtype Str_not2b of Str where /^[isnt|arent|amnot|aint]$/;
    # or
    my Str subtype Str_not2b where /^[isnt|arent|amnot|aint]$/;
  • 匿名的子类型定义为:
    Str where /^[isnt|arent|amnot|aint]$/;
    由此可以看出子类型定义的关键字不是 subtype 而是 where. where 在此的作用大致可以写为:
    { $_.does(Str) and $_ ~~ /^[isnt|arent|amnot|aint]$/; }
  • 子类型最主要用于限制参数类型。比如某子程序只接受奇数作为参数:
    sub check_even (Num where { $^n % 2 == 0 } $even) {...}
    如果觉得这样看起来很不顺,有很英语化的写法:
    sub check_even ($even of Num where { $^n % 2 == 0 }) {...}
  • 子类型最主要的作用为多种分派(multiple dispatch)的参数提供类型限制。
    multi sub mesg ($mesg of Str where /<profanity>/ is copy) { ... }
    
    multi sub mesg (($mesg of Num where { $^n % 2 == 0 }) { ... }
    
    multi sub mesg ($mesg of Str) { ... }
    默认带有限制子类型的 sub 比不带的优先级要高。也就是如果匹配 rule <profanity> 的话就调用该 sub, 都不匹配才调用不带限制的 sub

has $.Examples14 handles 'Perl6';

02 June 2005


Delegation

Delegation, 中文的意思可以为代表,授权或是委托。
Delegation 能让你在一个 class 或 role 里把另一个对象的方法当成自己的。

例子用于理解

class Dog {
    method wag { 2 }
    method run { 100 }
}
class Wolf {
    method wag { 4 }
}
class Snoopy {
    has $.tail handles 'wag';
}
my $pet = Snoopy.new;
# $pet.wag; 错误,不知道 $.tail 对应哪个类
$pet.tail = Dog.new; # 这里的 $pet 没有继承 Dog 类
$pet.wag; # 2
$pet.tail = Wolf.new; # 将 $.tail 对应到 Wolf
$pet.wag; # 4, $.tail 对应 Wolf, 所以调用 Wolf.wag;

其中最关键的一句就是 has $.tail handles 'wag'; 它使类 Snoopy 产生了一个方法 wag. 而调用 wag 就等同于调用 $.tail.wag 所以这话也就等同于在类里定义了如下代码:

method wag ([email protected] is context(Lazy)) { $.tail.wag([email protected]) }
关键字 handles 的中文意思可以为处理。所以代码 has $.tail handles 'wag' 的字面意思理解可以为“用 $.tail 来处理 wag 调用”。

方法多样化

  • 如果要多于一个方法的话,可以这么表示:
    has $.tail handles <wag run>;
  • 其实我们是用智能匹配来处理 handles 后面的参数。比如:
    has $:fur handles /^get_/;
    # 智能匹配正则表达式,所以以 get_ 开头的方法调用都委托 $:fur 对象来处理
    
    has $:fur handles Groomable;
    # handles 的方法就是 Groomable 里的所有方法
  • 如果你想定义一个方法名字而实际调用的是另一个方法,可以使用 Pair.
    has $:fur handles { :shakefur<shake> :scratch<get_fleas> };
    # 当你调用 shakefur 时实际上调用了 $:fur.shake
  • 可以更进一步,使用正则替换来改变方法名:
    has $:fur handles (s/^furget_/get_/);
    # 当你调用 furget_a 时实际调用的是 get_a

Want more?

See Synopsis12.

Class::Date 的两个常见用途

01 June 2005


题外话

昨天 chunzi 给我传了他改进的对身份证验证的模块,加进了地区信息和真正的生日验证等。Thanks, 非常棒。
他的代码我放到这了:IdentityCard.pm, 迟点我会对 Business::CN::IdentityCard 进行更新。(最近有三门考试 + 两门作业,有点忙)

不打算讲 Class::Date 的所有用途,详细的自行参考 perldoc Class::Date

用途一:验证日期正确与否

chunzi 在模块里使用了 Class::Date 来验证所输入身份证日期的正确性,写起来非常简单。类似的代码如下:
use Class::Date qw/:errors date/;

my $date = date '1981-02-29';
if ($date->error) { die $date->errstr; }
# 因为这伪造的日期是错误的,所以我们可以捕获得到 Range check on date or time failed
->error 有好几种类型,这里的值为 E_RANGE, 属于范围超出。

用途二:日期加减

Eplanet 有个功能为最近四天发表的后标记 New, 最近两天修改的后标记 Update.
这个功能很多地方都用得着。用 Class::Date 写这个判断非常简单。
use Class::Date qw/:errors date now/;

my $date = now; # now $date is '2005-06-01 15:00:52'
my $post_time = '2005-05-31 12:12:00';

if ($date - '4D' < $post_time) {
    print 'new';
}
非常简单。我们还有年月小时等都可以做这个加减。
如果不用 Class::Date 的话要先将 $post_time 转为 1970 年到现在的秒,然后通过现在的相减小于两天的秒。比起这个复杂多了。 CPAN 的意义在于大家分享自己的劳动成果。(如果某个问题很普遍,去 CPAN 上找一定可以找到解决方案。)

Attention

Class::Date 似乎在 Win2k 下不支持 1970 年前的日子。比如:
use Class::Date qw/:errors date/;
my $date = date '1969-01-01';
print $date->errstr; # Range check on date or time failed
提示范围超出。后来我试了下 Date::Simple 似乎可以。
use Date::Simple;
my $date  = Date::Simple->new('1949-02-28');

if ($date) { print 'OK' } else { print 'UOK'; }

Enjoy

Now, have a try. Thanks chunzi again. :)


module updated

01 June 2005


I got up at 8:10, it's a bit later since the class has begun.

Afternoon was too short, and I devoted my evening to updating Business::CN::IdentityCard. The original code is from chunzi, Thanks for his help.

Now it can give the birthday, province, district and gender of the IdentityCard owner. it's not a toy any more.

I uploaded it to CPAN ,I'll be grateful if you can help me to test this module.
Cause it needs a while to reach CPAN, you can install it by downloading from http://www.fayland.org/CPAN/

In developing this module, I find the Class::Date's date cann't deal with date before 1970 on Win2k. I just wonder if there is another way to validate a given date. Tips are welcomed.

I'd go sleep. Good night.

Update

Date::Simple is a good choice.

role Example13 { # Perl6 }

31 May 2005


Intro.

role 是类代码的复用。有点类似模块可以将子程序导出到某一程序或另一模块,一个 role 能将它的属性和方法导出到另一个类。注意,它与继承不同。
很多语言都有这个功能:比如 Ruby 有 mixins, Java 有 interfaces, 和某些版本的 Smalltalk 有 traits. Perl6 的 role 比它们略好一点。

easy example

class Dog {
    does bark;
    ...
}
class Wolf does bark {
    ...
}
role bark {
    method barking {
        say 'Wooo!';
    }
}
my $dog = Dog.new();
$dog.barking(); # Wooo!
my $wolf = Wolf.new();
$wolf.barking(); # Wooo!
这就是 role, 一种没有什么继承关系的代码复用。:)
如果 role 里定义了私有方法,注意此方法不是对 role 是私有的,而是对使用的类。
role 里可以调用 role 也可以继承 class.最简单的讲 role 和 class 就是关键字不同。

mixins

如果在一个类后面加 does role 的话,那是在编译时就加进去了。
而在一个对象后面加 does role 的话,那是在运行时才加进去的。
# 上面的例子我们不在 class 后面或里面加  does bark
if $pet ~~ Dog|Wolf { $pet does bark; $pet.barking(); }

方法冲突

假设我们有 class Dog, role Pet 和 role Sentry. role 中都定义了方法 shake.
  • 如果 class 中定义了 shake, 那么隐藏 role 中定义的 shake.
  • 如果是 role Pet 继承 class Dog 的话(role Pet is Dog),那么该 role 隐藏 class 里定义的 shake.
  • class 定义 shake 后,隐藏的 role 中的 shake 可以通过 ./Sentry::shake() 或 ./Pet::shake() 调用。
  • 第二种情况是 class 中没有定义 shake, 那么又分为两种。编译时(在 class 后面加的)会报错,而运行时(在 $object 后面加的)会用后一个自动覆盖。
    # 假设 Dog 里没有定义 shake
    my $animal is Dog does Pet does Sentry;
    # 这里后面的 Sentry 里的 shake 会隐藏 Pet 的 shake
所以一般而言,碰到冲突的解决方案有:
  1. 在 class 中定义 shake 来覆盖。或者该 shake 方法通过指定不同调用不同 role 的方法。
  2. 用 multi 通过类型来避免冲突

property 和 but

如果你需要一个 role, 而这个 role 只有一个与它名字一样的属性的话,Perl6 中提供了关键字 property 来简化操作。
比如下面的代码就可以简化为一行:
my role answer {
    has int $.answer is rw;
}
 # 用 property 简化
my int property answer;
but 与 does 不同的是 but 在一个副本上作变动。
$a = 0 but answer(42);
# 等同于
$tmp = 0;  # $tmp 为 0 的一个副本
$tmp does answer; # answer 是前面定义的一个属性 role
$tmp.answer = 42;
$a = $tmp;
# 或者我们可以怎么写:
(($anonymous = 0) does answer).answer = 42;
$a = $anonymous;
这么定义后 $a 本身还是为 0, 只是它有一个 answer 属性。由于是 is rw 的,所以可以这么设置:
$a.answer = 44;
but 最大的用途是后面跟一个 Enum 枚举类型。Enum 稍后再说,或请参考 Perl6::Bible::A12Perl6::Bible::S12.

$Perl6 = $class.bless(:Example<12>);

31 May 2005


Pause

I'm sorry. pugs 还有很多没有实现,所以我也不知道写下来的是否正确,所以就先等等了。
本章只是占个地方。可以略过不看。等 pugs 实现了 bless 等我会慢慢加进来。

构建和初始化

所有的类都从 Object 类继承了一个构建函数 new. 它能将传递进来的参数(只能是带名参数)分别初始化对应的属性。
Class Dog {
    has $.name;
    ...

my $pet = Dog.new(:name<Eric>); # 这里的 new 继承自 Object, 而传递进来的 :name<Eirc> 将初始化 $.name
你可以自己写 new 来覆盖 Object 的 new.
或者写你喜欢名字的构建器。与 Perl 5 中一样,一个构件器就是调用了 bless 的例程,不同的是类中调用的是方法。
因为 pugs 还没实现 bless, 所以我也试不出来。只能暂时缓缓。

submethod BUILD

class Dog {
    has $.name;
    has $.mum;
    has $:age;

    submethod BUILD ($.name) {
        $.name ~= ' Lam';
    }
}
my $pet = Dog.new( :name<Snoopy>, :mum<Kitty>, :age<2> );
say $pet.name; # Snoopy Lam
前面讲过 submethod 与 method 不同的是 submethod 是不会被继承的。
这里的 BUILD 方法覆盖了 Object 里的方法。函数 bless 会通过调用 BUILDALL 来自动调用合适的 BUILD

clone

class Dog {
    has $.name;
    has $.mum;
    has $:age;
    ...
}
my $pet = Dog.new( :name<Snoopy>, :mum<Kitty>, :age<2> );
...
my $pet2 = $pet.clone(:name<Lily>);
# 除了名字改为 Lily, 其 mum, age 都跟 $pet 一样,而且可以调用 $pet 能调用的东西

class Examples11 is Perl6 { ... }

30 May 2005


Declare

我开始打算讲 Perl 6 中新的面对对象技术。如果有些新的知识碰到了而前面没讲过,我会穿插着讲点那方面的知识。
最重要的声明是:我不保证我所写的东西是正确的。所以各位看官如果发现错误请给我发电子邮件(fayland at gmail.com)。

sit down, now comes class. :>

在 Perl 5 中对象,模块,类,包的概念是混在一起的,没有区分开来:
  • 一个对象只是一个 bless 的引用
  • 一个类 class 只是一个包 package
  • 一个方法 method 只是一个子程序 sub
  • 一个模块 module 只是一个与 package 名一样的文件

在 Perl 6 中他们将都各自拥有自己的关键字。模块有 module 类有 class 方法有 method 对象有 object.

暂时不介绍它们是什么,先讲几个概念:
  • 子程序。关键字 sub, 是带有参数列表的不可继承例程。
  • 方法。关键字 method, 是可继承的例程,它总是有一个对应的对象(称之为调用者)和属于一个特定的类。
  • 子方法。关键字 submethod, 是不可继承的方法,或者说是化装成方法的子程序。它也有调用者和所属的特定类。
  • 多重方法/Multimethods. 关键字 multi, 它的特点是可以有一个或多个调用者。
  • 规则。关键字 rule, 是一种用于模式匹配的带语法的方法。它的对应块有特殊的语法。(以后详细说)
  • 宏。关键字 macro, 是一种当它们被解析时(编译时)就运行的例程。宏可能返回另一个源代码字符串或解析树。
  • 角色。关键字 role, 主要用于代码复用。(以后详细说)

class

先讲类,类主要用于实例(对象)管理,其次才是软件复用(通过继承或委派来实现,我们一般用 role 来实现代码复用)。
  1. 类是用 class 声明的。它有两种声明方式:
    class Foo;        # 文件下面的部分都是类的定义
    ...
    
    class Bar {...}    # 块里才是类的定义部分
    我们通过 is 来继承类(可以多重继承),用 does 来复用 role. 简单的例子如下:
    class Dog is Mammal is Pet;  # 继承 Mammal 继承 Pet
    class Dog is Mammal does Pet { # Pet 是一个 role
    
    # 另一种方式:
    class Dog {
        is Mammal;
        does Pet;
        ...
    }
  2. 属性和私有属性。一个简单的例子:
    class Dog {
        has $.name is rw; 
        has $.mum; # 没有 is rw 的话只能在 class 里改这个数据
        has $:age; # 私有属性,可以后面加 = 1' 来设置默认值,上面的属性也一样
    }
    
    my $pet = Dog.new( :name<Snoopy>, :mum<Kitty>, :age<2> );
    # 另一种方式
    # my Dog $pet .= new( :name<Snoopy>, :mum<Kitty>, :age<2> );
    
    say $pet.name;  # Snoopy
    say $pet.mum;   # Kitty
    # say $pet.age; # WRONG, age 为私有变量
    
    $pet.name = 'Shakespeare'; # 设置属性 name 的值
    say $pet.name;             # Shakespeare
    
    # $pet.mum = 'Hella';      # WRONG, 默认 mum 是只读的。如果要在类外面更改 mum, 必须设置它为 is rw
    
    has $.name 类似于生成一个具有同样名字(name)的存取器。
    is rw 等后缀属性将复制为存取器的后缀属性。这里的 is rw 将使 name 存取器为左值方法(Lvaule method)。
    默认的存取器是只读的。简便设定所有属性为 is rw 的话可以在 class Dog 后加 is rw.
  3. 方法和私有方法。我们用 method 来定义方法(我们以前面的例子为基础):
    Class Dog {
        ...
        method grow (?$year = 1) {
            $:age += $year;
            say "$year year past, now $.name is $:age years old";
        }
    }
    my $pet = Dog.new( :name<Eric>, :age<2> );
    $pet.grow; # 1 year past, now Eric is 3 years old
    $pet.grow(2); # 2 year past, now Eric is 5 years old
    
    # 除了这种调用方法外,我么还可以这么调用(间接调用)
    grow $pet: 2;
    grow $pet:(3);
    method 其实可以算做 sub 的一种,所不同的是 method 是放在 class/role 里的。
    定义私有方法与定义私有属性类似,在方法前加 :. 如 mehod :MyPrivateMethod {...}
    甚至定义私有类也可以这样:class :MyPrivateClass {...}

    内部方法的调用:

    class Dog {
        has $.name;
        has $:age;
        method grow (?$year = 1) {
            $:age += $year;
            say "$year year past, now $.name is $:age years old";
            .:sigh; # 等同于 $_.:sigh
        }
        method :sigh {
            say "$.name is too old" if ($:age > 7);
        }
    }
  4. 类的智能匹配。如果你想确定某一变量是不是某一类的话,可以使用 ~~ 智能匹配。如前面的 $pet:
    if $pet ~~ Dog { say "$pet is a dog"; }

see u tomorrow

I'll add something later. have fun!

%Examples{'IO'} = open('Perl6');

30 May 2005


my shoulder

Yap, I'm just back from the Shanghai pm meeting. great meeting and lovely guys. :)

simple

因为 FILEHANDLE 将不再存在,而 Perl 6 中有一类型叫做 IO,所以我们一般用如下方法(IO 对象的方法)读取文件:
my $out = open(">$filename"); # $out 的类型就是 IO, 下同
my $in = open($filename);
my $append = open(">>$filename");
读进来后我们将不用 <> 来获取行,而用如下方法获取:
my $line1 = readline($in); # 要读取所有行的话将 $line1 改为 @line1。下同
my $line2 = $in.readline();
my $line3 = = $in; # 注意不是 ==
这是三种读取的方法,下面是简单的写入方法:
$out.print("... Its not over yet!\n"); # 追加的写入是一样的
关闭自然是:
$in.close;

for, while

一般推荐用 for 来写:
for (=$fh) -> $line { # 或 for =$fh -> $line { 省略了那括号
while 的写法如下:
my $line; # 必须声明 $line
while ($line = = $fh) {

相比而言,for 有额外的好处就是限制了 $line 参数的作用域到它所属的块。( while 声明 的 $line 在块结束后还是继续存在的。)

注意:下面的写法将使 $line 为整个文件的内容:
while =$*IN -> $line {

这是因为 = 在标量上下文里会做一 slurp, 将整个文件读取进来。

slurp

my $content = slurp $filename;
$content 将是整个文件的内容。

DATA

我们在 Perl 5 中文件里可以有一句柄为 __DATA__, 在 Perl 6 中我们可以有很多个 DATA。语法结构是下面这样子,但我不确定以后会不会更改(pugs 目前还没实现这一功能)。
=begin DATA LABEL1
LABEL1.1
LABEL1.2
LABEL1.3
=end DATA

=begin DATA LABEL2
LABEL2.1
LABEL2.2
=end DATA

# %=DATA<LABEL1>[0] 为 LABEL1.1
# 我们还可以缩写为 $=LABEL2[1] 为 LABEL2.2

prompts

在 Perl 5 中命令行参数获取我们一般用
print "输入您的用户名";
$user = <>;
Perl 6 中我们将用 prompts 命令行提示来获取:
$ARGS prompts("Search? "); # Perl 6
while (<$ARGS>) {
但这也不是定下来了,所以先不详细介绍,等定下来了我再补充说明。

句柄参数

我在 my Perl6 @Examples[3] 介绍过类型在子程序中的应用。
在 Perl 5 中一般用 \*FH 来传递句柄,这不是很方便而且 Perl 6 中也不再有 * 这种 typeglob 类型。新的传递方法将简单的使用类型 IO, 结合上面的写个简单完整例子:
my $fh = open('>temp') or die $!;
&append_2($fh, "line1");
&append_2($fh, "line2");
$fh.close;

sub append_2(IO $fh, $str) {
    $fh.print("$str in subroutine\n");
}

# file:temp
# line1 in subroutine
# line2 in subroutine

DIR

因为在 Apocalypse, Synopsis 里都没有提到操作 DIR 的事,所以默认跟 Perl 5 是一样的。
my IO $dir;
opendir($dir, '.');
my @files = readdir($dir);
close($dir);
我认为 OO 方式也是可以的。比如 $dir.opendir('.'); $dir.readdir(); $dir.close();

To be continued

wait for a while. :)