echo 2305843008139952128 === 230584308139952128; // true?false?
echo 10000000000000000; // 0が何個?
わかりにくいですね。
ということでどうにかしようというRFCが提出されました。
以下はNumeric Literal Separatorの日本語訳です。
Numeric Literal Separator
Introduction
人間の目は、一続きの長い数値を素早く解析するために最適化されてはいません。
従って、視覚的な区切り文字がなければ、コードの読み取りとデバッグに時間がかかり、読み取りミスを招く可能性があります。
1000000000; // 1億?10億?100億?
107925284.88; // オーダーはいくつ?
区切り文字のない数値リテラルはさらに、財務情報がセントなのかドルなのかなど、追加情報を伝えることができません。
$discount = 13500; // 13500ドル?13500セント?
.
Proposal
数値リテラルでアンダースコアをサポートし、数値を視覚的にグループ化することによって、コードの読みやすさを向上させます。
$threshold = 1_000_000_000; // 10億!
$testValue = 107_925_284.88; // 1億オーダー
$discount = 135_00; // $135
アンダースコア区切り文字は、PHPがサポートしているあらゆる数値リテラルで使用可能です。
6.674_083e-11; // 指数表記
299_792_458; // 整数
0xCAFE_F00D; // 16進数
0b0101_1111; // 2進数
0137_041; // 8進数
Restrictions
唯一の制限は、アンダースコアは数値に挟まれていなければならないということです。
つまり、以下のような書式は有効ではありません。
_100; // 定数として有効な書式
100_; // 末尾は×
1__1; // 連続は×
1_.0; 1._0; // 小数点前後は×
0x_123; // xの次なので×
0b_101; // bの次なので×
1_e2; 1e_2; // eの前後なので×
Unaffected PHP Functionality
数値リテラルの間にアンダースコアを追加しても、その値は変わりません。
字句解析の時点でアンダースコアは取り除かれるため、ランタイムには影響しません。
var_dump(1_000_000); // int(1000000)
Backward Incompatible Changes
後方互換性を壊す変更はありません。
Discussion
Use cases
使用を推奨する例
数値区切り文字は、数値の視覚的認知を可能にします。
すなわち、数値が現れたときに桁数を数える必要もなく、正確に間違いなく桁数が一目でわかるようになります。
これによって、4桁を超える数字を正しく読み取るためにかかる時間が大幅に短縮されます。
大きな数値リテラルは、ビジネスロジック定数、単体テスト値、データの変換などによく使用されます。
たとえばComposerがファイルを削除する際の再実行遅延は以下です。
usleep(350000); // without separator
usleep(350_000); // with separator
Active DirectoryのタイムスタンプからUNIXタイムスタンプへの変換。
$time = (int) ($adTime / 10000000 - 11644473600); // without separator
$time = (int) ($adTime / 10_000_000 - 11_644_473_600); // with separator
物理定数。
const ASTRONOMICAL_UNIT = 149597870700; // without separator
const ASTRONOMICAL_UNIT = 149_597_870_700; // with separator
2進数リテラル、16進数リテラルのバイト区切り。
0b01010100011010000110010101101111; // without separator
0b01010100_01101000_01100101_01101111; // with separator
0x42726F776E; // without separator
0x42_72_6F_77_6E; // with separator
Use cases to avoid
使用を避けるべき例
電話番号、クレジットカード番号、社会保障番号などのデータを格納するためにセパレータの使用が魅力的に映るかもしれません。
これらの値は数値のようも見えるからです。
しかし、それはほぼ常に悪い考え方です。
経験則として、これらの値に数値演算をする意味はありません。
これらの値を格納するための最良の方法は、整数ではありません。
// 使ってはいけない
$phoneNumber = 345_6789;
$creditCard = 231_6547_9081_2543;
$socialSecurity = 111_11_1111;
Will it be harder to search for numbers?
数値の検索が難しくなるのでは?
懸念となるのは、同じ値を複数の方法で記述することができるため、数値の検索が困難になることです。
しかし、これは既に現在起こっていることです。
同じ値を2進数、8進数、10進数、16進数、指数表記などで書くことが可能です。
実際には、フォーマットが一貫しているかぎり問題は起こりません。
逆に数値を見つけやすくなることもあります。
上で出ていた例を挙げると、検索時に13_500
と135_00
を区別することが可能になります。
また16進数リテラルをバイト区切りにすることで、_6F_
のように特定のバイトを含む数値だけを見つけることが可能になります。
Should it be the role of an IDE to group digits?
数値のグループ化はIDEの役目?
IDEは大きな数値を自動的に3桁区切りにフォーマットすることは可能ですが、区切りは必ずしも必要というわけではありません。
数値を同じ方法でグループ化することが常に望ましいわけではありません。
たとえば、財務数量がセント単位であるかドル単位であるかによって、異なる方法で表すことができます。
$total = 100_500_00; // $100,500.00
$total = 10_050_000; // $10,050,000
2進数リテラル・16進数リテラルは、nibbles/bytes/wordsなどの使用用途によって、様々な桁数でグループ化することができます。
IDEは、プログラマによるこれらの意図を読み取って自動整形することはできないでしょう。
Why resurrect this proposal?
何故このRFCを復活させた?
かつてのRFCは3年以上前(2016年1月)に投票があり、過半数の賛成があったものの2/3には足りず採用されませんでした。
当時の議論を見返したところ、このRFCは良いユースケースが提示されておらず、また投票期間も一週間と短かったため、多くの賛同は得られませんでした。
そのとき以来、数値リテラルのアンダースコア区切りは、多くの言語(Python、JavaScript、TypeScript等)で実装されており、以前より有用なユースケースをこの機能に対して適用できます。
Should I vote for this feature?
この機能に投票する必要はありますか?
Andrea Fauldsが要点をまとめました。
この機能はいくつかの利点があります。
それほど複雑な機能ではありません。
新しい構文やトークンはありません。既存の数値トークンの形式を変更するだけです。
既存の全ての数値リテラルに適用され、よく適合します。
他言語で確立された慣習に従います。
見た目は明らかに定数や識別子ではなく数値であることが一目でわかり、混乱の可能性は高くありません。
濫用には制限があります(先頭、末尾、連続はできません)
使用法は直感的です。
Comparison to other languages
他言語での実装例。
・Ada 単独、数値間
・C# 連続、数値間
・C++ 単独、数値間、シングルクオート
・Java 連続可、数値間
・JavaScript 単独、数値間
・Julia 単独、数値間
・Kotlin 連続可、数値間
・Perl 単独、数値間
・Python 単独、数値間
・Ruby 単独、数値間
・Rust 連続可、どこでも可
・Swift 連続可、数値間
Vote
2019/05/30投票開始、2019/06/13投票終了、可決には2/3+1の賛成が必要です。
2019/06/10現在は賛成30反対11で、おそらく受理されます。
感想
本文にもあるように、かつて却下されたRFCのリバイバルです。
当時は保守的傾向が強かったため却下されましたが、今回はユースケースをしっかり記載してきたこと、最近は(主にNikitaの働きにより)革新要素も取り込みやすくなりつつあること、そのNikitaが賛成していること、そしてJavaScriptなどでも導入されそうなことなどにより、PHPでも受け入れられそうです。
日本語だと数値は4桁区切りなので、3桁区切りの数値が入ってきても、結局桁数把握に時間がかかってしまいますね。
思考回路が英語の人であればシームレスに把握できるのでしょうが、私は完全に日本語脳なので、そこまで多大な恩恵を得ることはできないと思われます。
もっとも、一切区切りがない状態に比べたらはるかに良いでしょうけど。