LoginSignup
7
7

More than 5 years have passed since last update.

Perl 6 の関数は sub だけじゃない

Last updated at Posted at 2012-12-04

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 submulti 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 ...
7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7