🐶

コードを読んでいて気持ちのよい瞬間

2023/08/13に公開

システム開発をする際、要件があり、それを満たすようにコードが記述される。
私たちが対峙するコードには2種類あると考えており、1つ目はゼロから新たに作るコード、2つ目は誰かが作った既存のコード。後者のケースではコードと紐づく要件が残されていないケースがたまによくあり、コードから要件を読み取ることになる。
そして、ぱっとみ不可思議に思えたロジックの意図が読み取れて、認識していなかった要件の存在に気づけた時、すごく気持ちがよい。これは業務でもOSSでもそう。

先日Rectorのコードを読んでいた。Rectorというのは静的解析の一種でPHPのコードを自動的にリファクタリングしてくれる。
型を評価するNodeTypeResolverというクラスにresolveTernaryType()というメソッドがある。

/**
 * @return \PHPStan\Type\MixedType|\PHPStan\Type\UnionType
 */
private function resolveTernaryType(Ternary $ternary)
{
    if ($ternary->if !== null) {
        $first = $this->getType($ternary->if);
        $second = $this->getType($ternary->else);
        if ($this->isUnionTypeable($first, $second)) {
            return new UnionType([$first, $second]);
        }
    }
    $condType = $this->getType($ternary->cond);
    if ($this->isNullableType($ternary->cond) && $condType instanceof UnionType) {
        $first = $condType->getTypes()[0];
        $second = $this->getType($ternary->else);
        if ($this->isUnionTypeable($first, $second)) {
            return new UnionType([$first, $second]);
        }
    }
    return new MixedType();
}

これは三項演算子を評価するメソッド。
三項演算子とは(expr1) ? (expr2) : (expr3)という形式で、expr1がtrueの場合はexpr2、falseの場合はexpr3を値とする構文。
上記コードの$ternary->condはexpr1、$ternary->ifはexpr2、$ternary->elseはexpr3に相当する。
で、はじめ不可思議に感じたのは$ternary->if !== nullの部分。$ternary->ifがnullのケースを想定している。初見で、なぜ?となった。
マニュアルを見てみるとexpr2を省略する記法(expr1 ?: expr3)があるとわかった。どうやらこれをエルビス演算子と呼ぶらしい。PHPマニュアルには書かれていないで公式の呼び方ではないっぽいが。
ちなみに似たような記法にnull合体演算子というものがあるけれど、こちらはPHP ParserにおいてTernaryではなくCoalesceで表される。

コードリーディングは筋トレに似てる。
地道で退屈な時もあるけど、たまに成果を感じる瞬間があり充実感がある。

Discussion