13
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

if文駆逐計画 〜30代疲れ目エンジニアの条件分岐作法〜

Last updated at Posted at 2024-10-10

もう長いことコードを書いていると、「ああ、これまたif文か」と思う瞬間、増えてこない?
それ、わかるよ。若かったあの頃はif文で全てが解決できると思ってたんだよね。あの頃はね。

if文は、ロックみたいなもんさ。無骨で力強く、ガシガシとパワーコードを弾いてる感じでパワーコードを書いてた。
だけど、年を重ねてくると、if文もだんだん胃もたれしてきて、「これ、もう少しいい感じに書けないかな?」って思うことが増えてくるんだよ。
今日はそんな30代の疲れ目エンジニアのための話。


三項演算子、君は僕の愛だ

まず最初に言わせてほしい。僕は三項演算子が好きだ。いや、正確には愛している?:が生み出すあの短さと簡潔さ、条件分岐が一行で完結するところが最高なんだ。これだけでコードがぐっとスマートになる。

例えば、こんなコード(SWRのサイトから引用):

import useSWR from 'swr'
 
function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)
 
  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

は三項演算子でこう書ける。

import useSWR from 'swr'

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)

  return error ? (
    <div>failed to load</div>
  ) : isLoading ? (
    <div>loading...</div>
  ) : (
    <div>hello {data.name}!</div>
  )
}

見て、このスッキリ感。if文ではreturnを3回も書かないといけないところを、たった1つのreturnで済ませられる。これが三項演算子の魅力さ。三項演算子はではなくだからね。
でもね、三項演算子にも限界があるんだ。


ネストの深みで見失うもの

三項演算子、条件が2つ、3つなら全然いいんだ。でも、条件が増えてくると、どうなる?
そう、ネストが深くなって、コードが追いかけにくくなるんだよね。

「ちょっと待てよ、何が起きてるんだ?」って脳みそインタプリタがバグる瞬間が来る。コードはシンプルなはずなのに、頭が追いつかない。これはもう、三項演算子の限界さ。


if文、君には感謝してるけど、もう卒業しよう

if文。プログラムを書き始めた頃は頼れる相棒だった。「if文とfor文があれば全ては解決できる」って思ってたよね。懐かしいあの頃だよ。
でも、条件が増えるたびに、if文がどんどん重くなっていく。

function UserAccess({ role }) {
  if (role === 'admin') {
    return <AdminDashboard />;
  } else if (role === 'editor') {
    return <EditorPanel />;
  } else if (role === 'viewer') {
    return <ViewerContent />;
  } else {
    return <GuestAccess />;
  }
}

これ、最初はわかりやすいよね。でも、条件が増えていくと、if文が積み重なっていく。「これ、if文が無限に続くんじゃないか?」って思った瞬間。それはもう卒業の時が来たんだ。


MatchWhen:if文駆逐計画、始動

そこで登場するのが、MatchWhen。if文から解放され、コードをもっとシンプルで読みやすく、整理されたものにできるんだ。

ReactのMatchWhenの実装

まず、MatchWhenを実装しよう。これで条件分岐をReactの中で扱いやすくするんだ。

const Match = ({ children }) =>
  React.Children.toArray(children).find(
    ({ props: { exp, otherwise } }) => exp || otherwise
  ) || <></>;

const When = ({ children, exp = false, otherwise = false }) =>
  exp === 0 ? <></> : (exp || otherwise) && children;

使用例:

例えば、ロールごとに異なるコンポーネントを表示するパターンを考えてみよう。

function UserAccess({ role }) {
  return (
    <Match>
      <When exp={role === 'admin'}>
        <AdminDashboard />
      </When>
      <When exp={role === 'editor'}>
        <EditorPanel />
      </When>
      <When exp={role === 'viewer'}>
        <ViewerContent />
      </When>
      <When otherwise>
        <GuestAccess />
      </When>
    </Match>
  );
}

このパターンなら、if文を並べる必要がなく、条件ごとにスッキリ整理できる。条件が増えてもネストが深くならないし、読みやすさが保たれる


SWRとMatchWhenの組み合わせ

例えば、SWRのデータ取得の状態に応じて異なる表示をする場合にも、MatchWhenはとても便利。

import useSWR from 'swr'

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)

  return (
    <Match>
      <When exp={error}>
        <div>failed to load</div>
      </When>
      <When exp={isLoading}>
        <div>loading...</div>
      </When>
      <When exp={data}>
        <div>hello {data.name}!</div>
      </When>
      <When otherwise>
        <div>no data available</div>
      </When>
    </Match>
  )
}

SWRの結果に応じて、必要なUIコンポーネントを簡単に切り替えることができる。複雑なif文や三項演算子を使うより、こちらの方が圧倒的にシンプル。


Reactだけじゃない:JavaScript(TypeScript)でのmatchwhen

ReactでMatchWhenを使ってif文を駆逐したら、次はもっと広くJavaScriptの世界でも同じことをしたくならない?
実は、matchwhenという関数を使えば、JavaScriptでもif文をもっとシンプルに整理できるんだ。


matchwhen の実装

TypeScript版:

type When<T> = [boolean, () => T];

const is = (exp: boolean) =>
  <T>(t: () => T = () => true as T) =>
    (f: () => T = () => false as T) =>
      [f, t][Number(exp)]();

const match = <T>(l: When<T>[]) => (ow: () => T = () => undefined as unknown as T): T => {
  const tl = l.flatMap(([exp, fn]) => is(exp)(() => [fn])(() => []));
  return is(tl.length === 0)(ow)(tl[0]);
};

const when = <T>([exp, fn]: When<T>) => exp && fn();

JavaScript版:

const is = (exp) => (t = () => true) => (f = () => false) => [f, t][Number(exp)]();

const match = (l) => (ow = () => undefined) => {
  const tl = l.flatMap(([exp, fn]) => is(exp)(() => [fn])(() => []));
  return is(tl.length === 0)(ow)(tl[0]);
};

const when = ([exp, fn]) => exp && fn();

この実装では、内部的に is 関数を使って、シンプルな条件分岐を表現している。if文や三項演算子をもっと整理された形で処理できるようにしているんだ。


シンプルに使う例

この matchwhen を使って、条件分岐をもっと読みやすく、スッキリさせることができる。

match の例:

例えば、スコアに応じて評価を返すような条件分岐があるとする。この場合、match を使うことで、if文や三項演算子を使わずに、条件に応じた結果を返せる。

const score = 85;

const result = match([
  [score >= 90, () => "A"],
  [score >= 70, () => "B"],
  [score >= 50, () => "C"],
  [score < 50, () => "F"]
])(() => "Invalid score");

console.log(result);  // "B"

match 関数は、条件と処理をペアにしてリスト化することで、コードの可読性を保ちながら柔軟に条件を追加できる。

when の例:

もっとシンプルな1つの条件を評価したいときは、when を使って結果を返せる。

const score = 95;
const result = when([score >= 90, () => "A"])
console.log(result);  // "A"

このように、when は1つの条件に対して利用可能


実は思いつきで作った制御構造

ここまで話しておいてなんだけど、実はこの MatchWhen、そして matchwhen、完全に思いつきで作ったものなんだ。まだこれを使って大規模なプロジェクトを組んだわけじゃない。でも、試してみたら案外いけるんじゃないかなって思ったんだよ。

もしこの記事を読んで「これ、試してみようかな?」って思ってくれたら、僕たちは一緒にif文駆逐計画の先駆者になれるかもしれない。そう思うと、少しワクワクしない?


より複雑なパターンマッチングへの道

ここまで紹介した matchwhen は、シンプルな条件分岐やパターンマッチングに適しているけど、もっと複雑なパターンやデータ構造に対してはどうするか?
その場合には、さらに強力なパターンマッチングライブラリとして ts-pattern がある。
これはTypeScript向けに設計されていて、型安全かつ強力なパターンマッチを実現できるライブラリだ。もし複雑な分岐に遭遇したら、ts-patternを使うことで、さらに柔軟に対応できるはず。


まとめ:if文はロック。でも、少し肩の力を抜いてみよう

if文はロックだよ。無骨で力強く、コードを書く楽しさを教えてくれた。でも、今はもっと楽に、気楽にコードを書いていきたい。

MatchWhen、そして matchwhen を使えば、if文を駆逐して、もっとシンプルなコードを書ける。もしif文に疲れてきたなら、この方法を試してみてほしい。次のステップに進んでいこう。

13
15
1

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
13
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?