Elixirで「過激」にメタプログラミング！マクロでコードに闘魂を注入

```elixir
defmodule FightingSpiritMacro do
  defmacro shout(messages) do
    quote do
      unquote(messages)
      |> Enum.each(& IO.puts("<b><font color=\"red\">$\\huge{#{&1}}$</font></b>"))
    end
  end
end

require FightingSpiritMacro
messages = [
  "元氣ですかーーーッ！！！",
  "元氣があればなんでもできる！",
  "",
  "闘魂とは己に打ち克つこと。",
  "そして闘いを通じて己の魂を磨いていく",
  "ことだと思います。"
]
FightingSpiritMacro.shout(messages)
```

<b><font color="red">$\huge{元氣ですかーーーッ！！！}$</font></b>
<b><font color="red">$\huge{元氣があればなんでもできる！}$</font></b>
<b><font color="red">$\huge{}$</font></b>
<b><font color="red">$\huge{闘魂とは己に打ち克つこと。}$</font></b>
<b><font color="red">$\huge{そして闘いを通じて己の魂を磨いていく}$</font></b>
<b><font color="red">$\huge{ことだと思います。}$</font></b>

![DALL·E 2024-11-04 11.24.56.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/62520343-490c-135e-65d2-f0f5c43e1cef.jpeg)



# はじめに

[Elixir](https://elixir-lang.org/) 1.17で「過激な」メタプログラミングに挑戦してみませんか？Elixirには、**マクロ**を使ってコードを動的に生成するという強力な機能が備わっています。このマクロ、使い方次第でコードが極めてエレガントに、そして強力に変化します。ただし、慎重に使わないと「過激すぎる」コードになりがちです。まさに「過激な」プロレスです。ストロングスタイルです！

ここでは、Elixirにおけるメタプログラミングの基礎と、マクロでのコード生成方法をシンプルに紹介します。まさに「猪木イズム」で挑むべきテーマです！

---

# メタプログラミングとは？

メタプログラミングとは、**コードを生成するためのコード**を書くことです。Elixirでは、この目的のために**マクロ**を利用します。マクロを用いると、コードの繰り返しや冗長性を一気に減らすことができ、柔軟なプログラムが実現します。しかし、その柔軟性ゆえに、プロジェクト全体の可読性が落ちる危険性も伴います。

# マクロの基本

まず、シンプルなマクロの基本から見ていきましょう。マクロを定義するには、`defmacro`を使います。また、マクロの定義には`quote`と`unquote`が鍵となります。

```elixir
defmodule FightingMacro do
  defmacro shout do
    quote do
      IO.puts "元氣ですかーーーッ！！！"
    end
  end
end
```

上記の例では、`shout`というマクロを定義しています。このマクロを呼び出すと、まさに「元氣ですかーーーッ！！！」と叫ぶコードが生成されます。

## 使用例

マクロは通常の関数と同じように使用できますが、呼び出すと「コード自体」が展開されます。

```elixir
require FightingMacro
FightingMacro.shout() # => 元氣ですかーーーッ！！！
```

# 過激なマクロの活用例

まだちっともよさが伝わらないと思いますので、活用例を示します。

マクロの力を過激に発揮させる例として、複数のデータ型に対応した「共通処理」を動的に生成する方法を見てみましょう。例えば、ログを出力するマクロを使うと、どの型のデータでも一貫したログを出力できます。

```elixir
defmodule LoggerMacro do
  defmacro log(value) do
    quote do
      IO.puts "闘魂注入: #{inspect(unquote(value))}"
    end
  end
end

require LoggerMacro
LoggerMacro.log("123da-") # => 闘魂注入: "123da-"
LoggerMacro.log(123) # => 闘魂注入: 123
LoggerMacro.log(:"strong style") # => 闘魂注入: :"strong style"
```

`LoggerMacro`を使わない場合のコードも示しておきます。`闘魂注入`をタイプする回数に差がでます。この数が多くなればなるほど、コーディング効率の面で差がでるというわけです。マクロを使うとたったの1回ですみます。

```elixir:LoggerMacroを使わない場合は3回闘魂タイプが必要
IO.puts "闘魂注入: #{inspect("123da-")}"
IO.puts "闘魂注入: #{inspect(123)}"
IO.puts "闘魂注入: #{inspect(:"strong style")}"
```

# マクロを使う際の注意点

マクロは強力なツールですが、「過激」に使いすぎるとコードが複雑になりがちです。以下のポイントに注意して活用しましょう：

- **シンプルに保つ**：メンテナンスが難しくなるため、過度なロジックをマクロに詰め込まない。
- **使いどころを見極める**：繰り返し処理や共通ロジックに適した部分のみで利用し、細かい処理には通常の関数を使う。
- **可読性を重視**：他の開発者が理解しやすい形でマクロを設計する。

上記は一般的なマクロを使う際の注意点をまとめています。

公式では以下のページにまとまっています。
[Meta-programming anti-patterns](https://hexdocs.pm/elixir/macro-anti-patterns.html)

https://hexdocs.pm/elixir/macro-anti-patterns.html

---

# どうやって実行するんだー！？　バカヤロー！って方へ

GitHubのアカウントをお持ちの方へお手軽な方法を示しておきます。
[PhoenixアプリケーションをGitHub Codespaces上で開発する方法](https://qiita.com/torifukukaiou/items/5dd716cb04db9b46bc92)で紹介した[GitHub Codespaces](https://github.co.jp/features/codespaces)を使うという方法です。

https://qiita.com/torifukukaiou/items/5dd716cb04db9b46bc92

記事中で紹介している[phx_devcontainer](https://github.com/TORIFUKUKaiou/phx_devcontainer)を使うと、ElixirがインストールされたUbuntuコンテナが立ち上がるので、Ubuntu上で直接開発をしているかのように操作ができます。`iex`コマンドでREPL(Read-Eval-Print Loop)が立ち上がるのでこの記事のコード例をぜひ試してください。

![スクリーンショット 2024-11-02 14.11.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/303ee996-0b65-30c9-9b75-08d297b96f7a.png)

そして気づきましたか。もっともらしく書いていますが、この記事で挙げた`LoggerMacro`は過激すぎます。単なる関数で間に合います。

```elixir
defmodule :闘魂ロガー do
  def log(value) do
    IO.puts "闘魂注入: #{inspect(value)}"
  end
end

:闘魂ロガー.log("123da-")
:闘魂ロガー.log(123)
:闘魂ロガー.log(:"strong style")
```

ムフフ。
**虚と実が、入り交じるのがプロレスです。**

---

# まとめ

Elixirのマクロを駆使することで、コードに「過激な」エレガンスと効率を注入できます。とはいえ、猪木イズムを忘れず、慎重かつ大胆に挑戦してください！この過激なツールを使いこなすことで、Elixirの可能性をさらに広げ、闘魂をこめたコードが生まれるでしょう。

<b><font color="red">$\huge{迷わず使えよ、使えばわかるさ！}$</font></b>