tokuhirom's blog

cmake で変数を全部ダンプする

cmake で現在利用可能な変数の名前とその値をすべてダンプしたいということがあると思う。そんな時にはこうしたらいい。

get_cmake_property(_variableNames VARIABLES)
foreach (_variableName ${_variableNames})
    message(STATUS "${_variableName}=${${_variableName}}")
endforeach()

http://stackoverflow.com/questions/9298278/cmake-print-out-all-accessible-variables-in-a-script

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

valgrind でメモリリークの誤検出が起きる場合、suppression を指定する

valgrind は、問題がない場合でも誤検出することがママある。 そのような場合、suppressions ファイルを生成すればよい。

たとえば、dlopen() は内部でエラーメッセージ保存用のバッファを calloc で確保するが、このバッファは開放されないが、これはバグではないということになっている。See. https://sourceware.org/bugzilla/show_bug.cgi?id=14015

このような場合、以下のようにして、--gen-suppressions=all として、suppressions 設定をダンプさせることができる。

$ make -j 4 && valgrind --gen-suppressions=all --leak-check=full --show-leak-kinds=all ./a.out 
make: Nothing to be done for `all'.
==829== Memcheck, a memory error detector
==829== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==829== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==829== Command: ./a.out
==829==
==829==
==829== HEAP SUMMARY:
==829==     in use at exit: 32 bytes in 1 blocks
==829==   total heap usage: 437 allocs, 436 frees, 117,923 bytes allocated
==829==
==829== 32 bytes in 1 blocks are still reachable in loss record 1 of 1
==829==    at 0x4C2B974: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==829==    by 0x4E3668F: _dlerror_run (dlerror.c:141)
==829==    by 0x4E360C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
==829==    by 0x407599: pone_compile_node (main.c:771)
==829==    by 0x407851: main (main.c:845)
==829==
{
    <insert_a_suppression_name_here>
    Memcheck:Leak
    match-leak-kinds: reachable
    fun:calloc
    fun:_dlerror_run
    fun:dlopen@@GLIBC_2.2.5
    fun:pone_compile_node
    fun:main
}
==829== LEAK SUMMARY:
==829==    definitely lost: 0 bytes in 0 blocks
==829==    indirectly lost: 0 bytes in 0 blocks
==829==      possibly lost: 0 bytes in 0 blocks
==829==    still reachable: 32 bytes in 1 blocks
==829==         suppressed: 0 bytes in 0 blocks
==829==
==829== For counts of detected and suppressed errors, rerun with: -v
==829== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

この内容を t/myapp.supp などに保存する。そして、このファイルを指定して起動すると、以下のように、不要な警告をだまらせることができる。

$ make -j 4 && valgrind --suppressions=t/myapp.supp --leak-check=full --show-leak-kinds=all ./a.out
make: Nothing to be done for `all'.
==862== Memcheck, a memory error detector
==862== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==862== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==862== Command: ./a.out
==862==
==862==
==862== HEAP SUMMARY:
==862==     in use at exit: 32 bytes in 1 blocks
==862==   total heap usage: 437 allocs, 436 frees, 117,923 bytes allocated
==862==
==862== LEAK SUMMARY:
==862==    definitely lost: 0 bytes in 0 blocks
==862==    indirectly lost: 0 bytes in 0 blocks
==862==      possibly lost: 0 bytes in 0 blocks
==862==    still reachable: 0 bytes in 0 blocks
==862==         suppressed: 32 bytes in 1 blocks
==862==
==862== For counts of detected and suppressed errors, rerun with: -v
==862== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

C++ と valgrind

libstdc++ は内部でメモリプールを持っている上に、プロセス終了時に開放しないので、valgrind が勘違いする。

以下のような極めて単純なプログラムでも All heap blocks were freed にならない。

#include <iostream>
int main() {
    return 0;
}

この問題は以下のように公式の FAQ に掲載されている。

My program uses the C++ STL and string classes. Valgrind reports 'still reachable' memory leaks involving these classes at the exit of the program, but there should be none.
First of all: relax, it's probably not a bug, but a feature. Many implementations of the C++ standard libraries use their own memory pool allocators. Memory for quite a number of destructed objects is not immediately freed and given back to the OS, but kept in the pool(s) for later re-use. The fact that the pools are not freed at the exit of the program cause Valgrind to report this memory as still reachable. The behaviour not to free pools at the exit could be called a bug of the library though.

    Using GCC, you can force the STL to use malloc and to free memory as soon as possible by globally disabling memory caching. Beware! Doing so will probably slow down your program, sometimes drastically.

    With GCC 2.91, 2.95, 3.0 and 3.1, compile all source using the STL with -D__USE_MALLOC. Beware! This was removed from GCC starting with version 3.3.

    With GCC 3.2.2 and later, you should export the environment variable GLIBCPP_FORCE_NEW before running your program.

    With GCC 3.4 and later, that variable has changed name to GLIBCXX_FORCE_NEW.

There are other ways to disable memory pooling: using the malloc_alloc template with your objects (not portable, but should work for GCC) or even writing your own memory allocators. But all this goes beyond the scope of this FAQ. Start by reading http://gcc.gnu.org/onlinedocs/libstdc++/faq/index.html#4_4_leak if you absolutely want to do that. But beware: allocators belong to the more messy parts of the STL and people went to great lengths to make the STL portable across platforms. Chances are good that your solution will work on your platform, but not on others.

http://valgrind.org/docs/manual/faq.html#faq.reports

が、上記の方法は最近の gcc だとなぜかうまく動かすことができなかったので、諦めた。

valgrind --leak-check=yes --leak-resolution=high --show-reachable=yes ./a.out

として、qr/ERROR SUMMARY: 0 errors/ になることを確認してそれでよし、ということにした。

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

jcmd が "well-known file is not secure" という IO 例外を上げてくるときの対処

jcmd が "well-known file is not secure" などと意味不明なことを申した場合、この例外は 以下の位置から上がっている。


JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_checkPermissions
  (JNIEnv *env, jclass cls, jstring path)
{
    jboolean isCopy;
    const char* p = GetStringPlatformChars(env, path, &isCopy);
    if (p != NULL) {
        struct stat64 sb;
        uid_t uid, gid;
        int res;

        /*
         * Check that the path is owned by the effective uid/gid of this
         * process. Also check that group/other access is not allowed.
         */
        uid = geteuid();
        gid = getegid();

        res = stat64(p, &sb);
        if (res != 0) {
            /* save errno */
            res = errno;
        }

        /* release p here before we throw an I/O exception */
        if (isCopy) {
            JNU_ReleaseStringPlatformChars(env, path, p);
        }

        if (res == 0) {
            if ( (sb.st_uid != uid) || (sb.st_gid != gid) ||
                 ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) ) {
                JNU_ThrowIOException(env, "well-known file is not secure");
            }
        } else {
            char* msg = strdup(strerror(res));
            JNU_ThrowIOException(env, msg);
            if (msg != NULL) {
                free(msg);
            }
        }
    }
}

この例外はちょっと不親切で、

  • attach しようとしているユーザーの uid がプロセスファイルの uid と違う
  • 同じくグループIDが違う
  • ファイルモードが IRGRP, IWGRP, IROTH, IWOTH のうちいずれかが立っている

ときに発生するのだが、実際に上がるケースとしては、sudo jcmd などとしてユーザーのプロセスにアタッチしようとしたなどの理由により発生することが多くて、sudo -uwww-data jcmd ... などとすればすむことが多い。

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

Perl6 における イテレータの挙動のメモ

Perl6 のイテレータは、role Iterable を実装しているクラスから取得することができる。 Iterable には iterator メソッドが実装されていて、これを取り出せばよい。

my $iter = $obj.iterator();

この $iter は role Iterator でアレしてます。role Iterator には pull-one というメソッドがあります。pull-one はイテレータが最後に到達すると IterationEnd というシングルトンバリューを返すので、以下のように実装すれば生の iterator を直接さわって順番に舐めることができます。

use v6;
use nqp;

my $obj = [1,2,3];
my $iter = $obj.iterator;
while True {
    my $next = $iter.pull-one();
    if nqp::decont($next) =:= IterationEnd {
        last;
    }
    say $next;
}
say "done";

ただし、このようなローレベルな操作は普通必要ありません。

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

Perl6 のフィボナッチ数列生成についての解説

mattn ブログで紹介されている Perl6 のフィボナッチ数列が奇妙に見える人が多いようなので、まともな解説。

ref. http://mattn.kaoriya.net/software/lang/perl6/20151026144119.htm

フィボナッチ数列とは以下のような数列です。

1, 1, 2, 3, 5, 8, 13, 21, 34, 55

最初の2つの数字が 1, 1 でして、その後のものは直前2つの数字を足したものです。

よって、Perl5 で記述した場合、先頭10個のフィボナッチ数列を求めるには以下のようになります。

use v5.16.0;

sub fib {
    state %memo; # 一応 memoize ぐらいはしておく

    my $n = shift;
    $memo{$n} //= do {
        if ($n == 0 || $n == 1) {
            1
        } else {
            fib($n-1) + fib($n-2);
        }
    };
}

sub fibs {
    my $n = shift;
    map { fib($_) } 0..$n;
}

say join(', ', fibs(10));

ですが、Perl6 では無限数列を扱う機能が充実しているため、こういったものは非常に単純に記述できます。

Perl6 での書き方を解説しましょう。

まず、先頭の数列を用意します。

1, 1

次に、続きは一つ前の項目と2つ前の項目の和であることを示します。

1, 1, -> $a, $b { $a + $b } ...*

ここで、-> $a, $b { $a + $b } はラムダ式です。これは ES6 などのものと同じですね。 ...* というのは、数列が続くことを示し、* は無限大を表します。こうすることで無限に続く数列を表すことができるのです。

Perl6 では、2項のラムダは * + * のように省略できるのでこれは

1, 1, *+* ...*

と表記することができます。

ここから、先頭10個を取り出すには

my @a = (1, 1, *+* ...*);
say @a[0..9];

また、レンジの右辺は ^10 のようにすれば「10は含まない」つまり「0~9」のレンジを作ることができるので、以下のように表記できます。

my @a = (1, 1, *+* ...*);
say @a[^10];

これをまとめると以下のようになります。

say (1, 1, *+* ...*)[^10];

ま、そんな感じです。

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

Perl6 でリストから無限リストをつくる

問題

my @a = <yellow red green> のようなリストから 'yellow', 'red', 'green', 'yellow', 'red', 'green', ..* のような無限リストを作りたい。

答え

my @list1 = <a b c>; my @list = map {@list1[$_ % @list1.elems]}, (0..*); say @list[42..50] のようにすれば良い。

まとめ

perl6@irc.freenode.org で質問して答えを得たが、ちょっとむずかしすぎるなあという印象。

本当は <a b c> x * でやりたかった。

追記

my $list = (|<a b c> xx *); say $list[40..50]; で良いとのことになりました。

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

WebSocket with Perl6

Yay, I released WebSocket.pm on ecosystem. You can write a WebSocket server very easy!

Here is an example chat server in Perl6. https://github.com/tokuhirom/p6-WebSocket/blob/master/eg/chat.pl6

use v6;

use HTTP::Server::Tiny;
use WebSocket::P6SGI;

sub MAIN(Int :$port=80) {
    my $html = $=finish;
    $html ~~ s:g/'<<<PORT>>>'/$port/;

    my $supply = Supply.new;

    my $s = HTTP::Server::Tiny.new(port => $port);
    $s.run(-> %env {
        say "request: %env<PATH_INFO>";
        given %env<PATH_INFO> {
            when '/' {
                200, [], [$html]
            }
            when '/echo2' {
                my $s;

                ws-psgi(%env,
                    on-ready => -> $ws {
                        $s = $supply.tap(-> $got {
                            $ws.send-text("GOT: $got");
                        });
                        say 'ready';
                    },
                    on-text => -> $ws, $txt {
                        $supply.emit($txt);

                        say 'sent.';
                        if $txt eq 'quit' {
                            say 'close it!';
                            $ws.send-close();
                        }
                    },
                    on-binary => -> $ws, $binary {
                        $ws.send-binary($binary);
                    },
                    on-close => -> $ws {
                        say "closing socket";
                        $s.close if $s;
                    },
                );
            }
            default {
                404, [], ['not found']
            }
        }
    });
}

=finish
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>WS</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <header><h1>WS</h1></header>
        <section class="row">
            <form id="form">
                <input type="text" name="message" id="message">
                <input type="submit">
            </form>
            <pre id="log"></pre>
        </section>
        <footer>Powered by <a href="http://perl6.org/">Perl6</a></footer>
    </div>
    <script type="text/javascript">
        function log(msg) {
            $('#log').text($('#log').text() + msg + "\n");
        }

        $(function () {
            var ws = new WebSocket('ws://localhost:<<<PORT>>>/echo2');
            ws.onopen = function () {
                log('connected');
            };
            ws.onclose = function (ev) {
                log('closed');
            };
            ws.onmessage = function (ev) {
                log('received: ' + ev.data);
                $('#message').val('');
            };
            ws.onerror = function (ev) {
                console.log(ev);
                log('error: ' + ev.data);
            };
            $('#form').submit(function () {
                ws.send($('#message').val());
                return false;
            });
        });
    </script>
</body>
</html>
Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

«を入力する方法»

Perl6 ではよく $<pairs>».made などと入力するシーンがあるが、これの入力方法はわかりづらい。 この記号は Guillemet と呼ばれていて、まあ ASCII の範囲外なので普通のプログラミング言語ではオペレータとして採用されていない。

タイピングの方法は以下が詳しい。

https://en.wikipedia.org/wiki/Guillemet#Typing_.22.C2.AB.22_and_.22.C2.BB.22_on_computers

Mac で ASCII キーボードなら Opt+Shift+\ で入力できる。日本語キーボードなら Alt+Shift+} なようだ。

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

Shipped full featured Plack clone for Perl6 named `Crust`

Hi, i'm tokuhirom, one of the core developer of Plack.

During the past few weeks, I've been hack the Perl6. This is to celebrate the Christmas in the best mood.

I'm a minimalist, and I love a software built on clean and well tested parts. I mean tokuhirom loves CPAN. To write the practical web applications in Perl6, it has released some of the library.

  • HTTP::Server::Tiny - IO::Socket::Async based full-featured HTTP/1.1 PSGI ready web application server(Port of Starlet).
  • HTTP::Parser - Port of HTTP::Parser::XS(In pure Perl6, only ported HTTP request parser, for now).
  • Getopt::Tiny - Port of Getopt::Long
  • HTTP::MultiPartParser - multipart/form-data parser.

Finally, by using these software, we have succeeded in transplanting the Plack. It's named Crust.

https://github.com/tokuhirom/p6-Crust/

I, and documentation issue(you know, my English is not good)t contains following modules.

  • Crust::App::File
  • Crust::Handler::HTTP::Easy
  • Crust::Handler::HTTP::Server::Tiny
  • Crust::Headers
  • Crust::MIME
  • Crust::Middleware::AccessLog
  • Crust::Request
  • Crust::Request::Upload
  • Crust::Response

You can install it by panda.

panda install Crust.

And there is a crustup command. You can run your P6SGI application by following comamnd:

crustup -e '-> $env { 200, [], ["ok"] }'
crustup app.psgi

We need your contribution for this project. There are some missing parts.

Thanks,

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

Getopt::Tiny for Perl6

Hi, I shipped Getopt::Tiny for Perl6's ecosystem.

https://github.com/tokuhirom/p6-Getopt-Tiny/

You can install it by panda command:

panda install Getopt::Tiny

And then, you can use it!

get-options($args, <
    e=s
    I=s@
    p=i
    h|host=s
>);

wow! it's really readable. enjoy it~~

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

Raw::Socket was retired.

https://github.com/tokuhirom/p6-Raw-Socket/

I removed raw-socket from ecosystem. Because I had misunderstood the behavior of IO::Socket::Async.

IO::Socket::Async's behaviour is good enough to me. tap-ed callback function have each threads. these works concurrently

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

Ported Cookie::Baker to Perl6

I ported @kazeburo san's great Cookie::Baker to Perl6.

https://github.com/perl6/ecosystem/pull/60

This is my third module for Perl6 ecosystem

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

[mac][osx][java][mysql] JDBC で mysql につなごうとしたら "Communications link failure" のエラーが出るとき

gradle でテストを動かしていたら、途中で JDBC が "Communications link failure" と言って死ぬ場合がある。 これは com.mysql.jdbc.exceptions.jdbc4.CommunicationsException のメッセージだが、このメッセージ自体は、socket を connect することにしたということを示すのみで、それ以上の情報が無い。ただこのエラー自体は、OS レベルでエラー返して発生することがほとんどなので、strace -f とかして調べればよいですね。

ありがちなのは bind-address を 127.0.0.1 に設定しているのに他のサーバーからアクセスしたとからしい。


一方で、テストの実行途中でこのエラーが出る場合は、リソースが枯渇している可能性がある。 つまり、テストケースの中でリソースがリークしている可能性だ。

そういった場合、以下のようなスクリプトをささっと書いて使うのがよい。

これで、特定の名前のプロセスの開いているファイル数を調べて、しきい値よりも多くなったらその値を出す。

perl hoge.pl --limit=2000 java

とかで実行すればいい。

mac の場合、デフォルトだとプロセスあたりに開くことができるファイルの数が少なめに設定されているので、mac でだけ死ぬ場合はこれを疑うといい。


mysql connector/j の出すエラーが不親切なので mysql が悪いのかなとおもってログを見てみたりする場合もあるが、そもそも他の部分でファイルディスクリプタ開きすぎている場合などあるので、mysql だけを疑っていてもダメ(もちろん mysql まわりに問題がある場合もある)。

Created: 2015-09-28 08:28:46 +0000
Updated: 2015-09-28 08:28:46 +0000

[perl5] Shipped HTTP::MultiPartParser for Perl6

I ported @chansen's HTTP::MultiPartParser to Perl6.

https://github.com/tokuhirom/p6-HTTP-MultiPartParser

You can parse multipart/form-data content very easy!

Created: 2015-09-28 08:28:46 +0000
Updated: 2015-09-28 08:28:46 +0000

[Perl6] how do i debug if got an exception from internal methods

I got a Cannot use a Buf as a string, but you called the Stringy method on it excetption.

It comes from X::Buf::AsStr exception class.

my class X::Buf::AsStr is Exception {
    has $.method;
    method message() {
        "Cannot use a Buf as a string, but you called the $.method method on it";
    }
}

But this method doesn't tell me an information to debug.

In this case, you can use --ll-exception option for perl6-m command.

It allows to show full backtrace includes internal method call.

In this case, I called "hoge".encode('ascii') eq "hoge". eq operator calls .Stringy method automatically.

Created: 2015-09-28 08:28:46 +0000
Updated: 2015-09-28 08:28:46 +0000

I talked about perl6 in gotanda.pm

I talked about web application development on Perl6 in gotanda.pm.

Created: 2015-09-28 08:28:46 +0000
Updated: 2015-09-28 08:28:46 +0000

fork と kevent

kqueue は fork した後はイベントが無効となり利用できない。 BSD 系のOSではrforkというシステムコールが用意されておりこれを利用すれば引き続き同じリソースを利用可能だが、Mac OSX にはこのシステムコールがない。

Created: 2015-09-28 08:28:46 +0000
Updated: 2015-09-28 08:28:46 +0000

lldb で core ファイルを見たい

lldb binary -c core

で見れる。

Created: 2015-09-28 08:28:46 +0000
Updated: 2015-09-28 08:28:46 +0000

formatting moarvm's profiler result in CLI

moarvm outputs profiler result as HTML. It's too heavy to display for large programs. But it can output json too.

I wrote a command line tool to display inclusive/exclusive time.

here is a code: https://github.com/tokuhirom/p6-HTTP-Server-Tiny/blob/master/author/moar-profiler-cli.pl

sample output is here: 42329399 <anon> gen/moar/m-BOOTSTRAP.nqp 2734 42326077 CALL-ME lib/NativeCall.pm 219 41899557 accept /home/tokuhirom/.rakudobrew/moar-nom/install/share/perl6/site/lib/Raw/Socket/INET.pm6 136 41836327 p6_socket_accept native library -2 39888308 <anon> /home/tokuhirom/dev/p6-HTTP-Server-Tiny/lib/HTTP/Server/Tiny.pm6 62 2080758 handler /home/tokuhirom/dev/p6-HTTP-Server-Tiny/lib/HTTP/Server/Tiny.pm6 147 2070563 <anon> /home/tokuhirom/dev/p6-HTTP-Server-Tiny/lib/HTTP/Server/Tiny.pm6 154 1870526 parse-http-request /home/tokuhirom/dev/p6-HTTP-Server-Tiny/lib/HTTP/Server/Tiny.pm6 238 852769 infix:<ne> gen/moar/m-CORE.setting 7922 838721 infix:<eq> gen/moar/m-CORE.setting 7918 733063 sink gen/moar/m-CORE.setting 10186 729940 sink-all gen/moar/m-CORE.setting 2806 589297 <anon> /home/tokuhirom/dev/p6-HTTP-Server-Tiny/lib/HTTP/Server/Tiny.pm6 246 495481 match gen/moar/m-CORE.setting 8369 473728 list gen/moar/m-CORE.setting 7536 438270 <anon> lib/NativeCall.pm 220 417899 ASSIGN-POS gen/moar/m-CORE.setting 13204 414589 guess_library_name lib/NativeCall.pm 157 408443 library /home/tokuhirom/.rakudobrew/moar-nom/install/share/perl6/site/lib/Raw/Socket/INET.pm6 9 366499 <anon> /home/tokuhirom/.rakudobrew/moar-nom/install/share/perl6/site/lib/Raw/Socket/INET.pm6 12 365713 <anon> /home/tokuhirom/.rakudobrew/moar-nom/install/share/perl6/site/lib/Raw/Socket/INET.pm6 13 328924 infix:<eq> gen/moar/m-CORE.setting 1713 328914 <anon> /home/tokuhirom/dev/p6-HTTP-Server-Tiny/lib/HTTP/Server/Tiny.pm6 269 312313 Stringy gen/moar/m-CORE.setting 1264 299129 r gen/moar/m-CORE.setting 21928 294579 subst-mutate gen/moar/m-CORE.setting 5729 274877 Str gen/moar/m-CORE.setting 11385 265843 ASSIGN-POS-SLOWPATH gen/moar/m-CORE.setting 13225 260310 join gen/moar/m-CORE.setting 11938 250136 e gen/moar/m-CORE.setting 21905

Created: 2015-09-28 08:28:46 +0000
Updated: 2015-09-28 08:28:46 +0000

perl6 ではコールバックを受け取る関数では Sub を受け取るように明示する

perl6 では以下のようなコードでは 3 が帰る。

(sub { (-> { return 3 })(); return 4 })().say

-> { } という記法からすると sub { } と同義なように見えるが実はそうではない。これは perl5 の BLOCK と同じことだ。 つまり以下と同じ。

perl -E 'say sub { map { return 1 } 2; return 3 }->()'

なので、この中で return すると上に抜けてしまう。

> (-> { }).WHAT.say
(Block)
> (sub { }).WHAT.say
(Sub)

実際のところ、そもそも Block と Sub で型も違うので、一般にコールバック関数を受け取るようなメソッドを定義する際には以下のように Sub を受け取るように明示すべき。

method run(Sub $app) { ... }

明示しておかないと、処理がなぜか突然上のほうにすっぽぬけたりしてマジで意味不明になるので辛いです.

Created: 2015-09-28 08:28:46 +0000
Updated: 2015-09-28 08:28:46 +0000
Next page