D 言語でコンパイル時にマイナンバーの妥当性をチェックするテンプレート

# 発端
<blockquote class="twitter-tweet" lang="ja"><p lang="ja" dir="ltr">マイナンバーのチェックデジットを計算する&#10;<a href="https://t.co/hd5vkkS6sP">https://t.co/hd5vkkS6sP</a>&#10;D言語への翻訳で Advent calendar の記事になるかもな</p>&mdash; 大堀龍一 (Ryuichi OHORI) (@__DaLong) <a href="https://twitter.com/__DaLong/status/672765875968016384">2015, 12月 4</a></blockquote>

- http://qiita.com/qube81/items/fa6ef94d3c8615b0ce64

やってみよう。

# 実装

どやさ。

```d:mynumber.d
import std.algorithm.iteration : map;
import std.functional : unaryFun;
import std.numeric : dotProduct;
import std.range : dropOne, iota, repeat, zip;
enum isValidMyNumber(ulong x) =
    x < 10UL^^12 &&
    x.repeat
    .zip(0.iota(12).map!"10UL^^a")
    .map!"a[0]/a[1]%10"
    .unaryFun!(n12r => n12r.front ==
        n12r.dropOne
        .dotProduct(1.iota(12).map!"a<7?a+1:a-5")
        .unaryFun!"a%11"
        .unaryFun!"a<2?0:11-a");


// ulong
static assert(!  isValidMyNumber! 123456789010);
static assert(!  isValidMyNumber! 123456789011);
static assert(!  isValidMyNumber! 123456789012);
static assert(!  isValidMyNumber! 123456789013);
static assert(!  isValidMyNumber! 123456789014);
static assert(!  isValidMyNumber! 123456789015);
static assert(!  isValidMyNumber! 123456789016);
static assert(!  isValidMyNumber! 123456789017);
static assert(   isValidMyNumber! 123456789018);
static assert(!  isValidMyNumber! 123456789019);
static assert(   isValidMyNumber!  23456789013);
static assert(!  isValidMyNumber!9123456789010);
```

UFCS チェインのために `std.functional.unaryFun` を挟んでいますが、ナイーブにラムダ記法を使って `(a => expr)(value)` と即時呼び出しすると括弧が多くなるし、流れが前後するので読みにくくなるわけです。

えぇ、引数に文字列や文字からなる input range なんかを渡したいという需要もありますよね。

```d:mynumber.d
import std.range.primitives : ElementType, isInputRange;
import std.traits : isSomeChar, isSomeString;
template isValidMyNumber(alias x)
    if (isSomeString!(typeof(x)) ||
        isInputRange!(typeof(x)) &&
        isSomeChar!(ElementType!(typeof(x))))  // てきとー
{
    import std.algorithm.searching : all;
    import std.ascii : isDigit;
    import std.range : walkLength;
    static if (x.walkLength == 12 && x.all!isDigit)
    {
        import std.conv : to;
        alias isValidMyNumber = isValidMyNumber!(to!ulong(to!dstring(x)));
    }
    else
    {
        enum isValidMyNumber = false;
    }
}


// string
static assert(!  isValidMyNumber! "123456789010");
static assert(!  isValidMyNumber! "123456789011");
static assert(!  isValidMyNumber! "123456789012");
static assert(!  isValidMyNumber! "123456789013");
static assert(!  isValidMyNumber! "123456789014");
static assert(!  isValidMyNumber! "123456789015");
static assert(!  isValidMyNumber! "123456789016");
static assert(!  isValidMyNumber! "123456789017");
static assert(   isValidMyNumber! "123456789018");
static assert(!  isValidMyNumber! "123456789019");
static assert(   isValidMyNumber! "023456789013");
static assert(!  isValidMyNumber!  "23456789013");
static assert(!  isValidMyNumber!"9123456789010");
static assert(!  isValidMyNumber! "o23AS67890I3");

// InputRange
import std.range : chain;
static assert(!  isValidMyNumber!( "123456789010".chain("")));
static assert(!  isValidMyNumber!( "123456789011".chain("")));
static assert(!  isValidMyNumber!( "123456789012".chain("")));
static assert(!  isValidMyNumber!( "123456789013".chain("")));
static assert(!  isValidMyNumber!( "123456789014".chain("")));
static assert(!  isValidMyNumber!( "123456789015".chain("")));
static assert(!  isValidMyNumber!( "123456789016".chain("")));
static assert(!  isValidMyNumber!( "123456789017".chain("")));
static assert(   isValidMyNumber!( "123456789018".chain("")));
static assert(!  isValidMyNumber!( "123456789019".chain("")));
static assert(   isValidMyNumber!( "023456789013".chain("")));
static assert(!  isValidMyNumber!(  "23456789013".chain("")));
static assert(!  isValidMyNumber!("9123456789010".chain("")));
static assert(!  isValidMyNumber!( "o23AS67890I3".chain("")));
static assert(!  isValidMyNumber!( "l23AS67890I3".chain("")));
```

文字列やそれっぽいのを `ulong` に変換して最初の実装に丸投げするだけのやっつけテンプレートです。`static assert` 部で文字列から input range（非文字列扱い）をでっち上げるために `std.range.chain` を使ったのは秘密です。

# 感想

D 言語のマルチパラダイム性をアピールするチャンスだと思いました。