sub と言えば Perl、 Perl と言えば sub ですが、 Perl 6 の関数定義には sub 以外にも色々あり、それぞれに役割が異なっています。1つ1つ見ていきましょう。
sub
sub
は Perl 5 の sub と同じく、モジュールレベルの関数を定義します。
sub foobar { say 'ok' }
foobar; # => ok
my sub, our sub
単に sub
と書くと my sub
の省略だと見なされます。 my sub
で定義した関数はモジュールの外からは見えません。関数をモジュール外から呼び出せるようにするには明示的に our sub
で定義します。
module Foo {
sub bar { say 'ok' }
my sub my_bar { say 'ok' }
our sub our_bar { say 'ok' }
}
Foo::bar; # => error: Could not find symbol '&bar'
Foo::my_bar; # => error: Could not find symbol '&my_bar'
Foo::our_bar; # => ok
multi sub
multi sub
はマルチディスパッチに対応した関数を定義します。マルチディスパッチは C++ や Java でいうオーバーロードを動的に実現する機構で、引数の型や値によって関数を呼び分けることができます。
multi sub foo(Int $int) { say 'int' }
multi sub foo(Str $str) { say 'str' }
foo(42); # => int
foo('the answer'); # => str
proto sub
proto sub
は multi sub
で多重定義した同名の関数に共通する性質を定義します。たとえば関数本体が実行される前にコードを差し込むことができます。
multi sub foo(Int $int) { say 'int' }
multi sub foo(Str $str) { say 'str' }
proto sub foo(|) { print 'I haz '; {*} } # `{*}` は適切な multi sub の本体に置き換わる
foo(42); # => I haz int
foo('the answer'); # => I haz str
anon sub
anon sub {}
式は匿名関数オブジェクトを作ることができます。
my $f = anon sub foo { say 'foo' };
$f(); # => foo
sub {}
式で作れる無名関数オブジェクトと違い、匿名関数オブジェクトには名前がついています(ややこしいですね)。関数オブジェクトに名前をつけておくとエラーが起きたときバックトレースに関数名が表示されます。デバッグの助けになるでしょう。
(anon sub foo { die 'ouch!' })(); # => error: ouch! in sub foo at /path/to/script:1
# ^^^^^^^ 匿名:名前あり
(sub { die 'ouch!' })(); # => error: ouch! in sub at /path/to/script:1
# ^^^^ 無名:名前なし
なお sub {}
式で名前つき関数オブジェクトを作ると関数定義もされてしまうので注意が必要です。
(sub foo { say 'foo' })(); #=> foo
foo; # => foo
method
method
はその名のとおりメソッドを定義します。
class Klass {
method meth { say 'ok' }
}
Klass.meth; # => ok
Perl 6 にはクラスメソッドとインスタンスメソッドの違いはありません。クラスメソッドとして呼び出した場合は self
がクラスオブジェクトになります。クラスオブジェクトは未定義値扱いなので、 self.defined
の真偽を見ればクラスメソッドとして呼ばれたかどうかが分かります。
class Klass {
method meth {
if self.defined { say 'called as instance method' }
else { say 'called as class method' }
}
}
Klass.new.meth; # => called as instance method
Klass.meth; # => called as class method
self
が未定義値であるかどうかでマルチディスパッチすることもできます。
class Klass {
multi method meth(Mu:D:) { say 'called as instance method' }
multi method meth(Mu:U:) { say 'called as class method' }
}
Klass.new.meth; # => called as instance method
Klass.meth; # => called as class method
method ! (private method)
プライベートメソッドを定義するにはメソッド名の前に !
をつけます。プライベートメソッドを呼び出すには .
ではなく !
を使って self!meth()
のようにします。
class Klass {
method pub { say 'pub and ' ~ self!priv }
method !priv { 'priv' }
}
Klass.pub; # => pub and priv
Klass.priv; # => error: No such method 'priv' for invocant of type 'Klass'
submethod
submethod
は継承されないメソッドを定義します。必ずオーバーライドしなければならないメソッドを定義するときなどに使います。
class Base {
method meth { say 'meth and' ~ self.submeth }
method submeth { 'submeth' }
}
class DerivedWithSubmeth is Base {
submethod submeth { 'another submeth' }
}
class DerivedWithoutSubmeth is Base {}
Base.new.meth; # => meth and submeth
DerivedWithSubmeth.meth; # => meth and another submeth
DerivedWithoutSubmeth.meth; # => error: No such method 'submeth' for ...