6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2024

Day 19

[Elixir] パイプラインの流れでそのまま変数を定義したい

Last updated at Posted at 2024-12-18

本記事は、Elixir Advent Calendar 2024 シリーズ7の19日目です。


変数束縛とパイプライン

Elixirのパイプラインは書いていて楽しいものですが、たまーに変数をおいておかないといけないこともありますよね?


data1 = 
  big_data
  |> something_process()

data2 = 
  big_data
  |> another_process()

Enum.zip(data1, data2)
|> hoge_process

...

このように変数の列とデータ処理の列がずれます。
これはこれで変数が強調されるので見やすいという意見は当然あり得ます。

が、なんとなく揃えたいなーというのが本記事の動機です。

完成系

下記のように追加した構文bindが動くことを目指します。

[1, 2, 3]
|> Enum.map(& &1 * 2)
|> bind(:a)

IO.inspect(a)
#=> [2, 4, 6]

説明

最終的には下記2つの要件を満たす必要があります。

  1. 変数の用意: 「変数を用意する」という関数の呼び出し
  2. 式を追加: 元の計算式に加えて「変数を用意する」という式を追加

変数の用意

結論を書くと、Macro.var/2を使えば可能です。
引数1は変数名、引数2はcontextと記載されてますが、変数をどのモジュールや関数で定義するか、という情報だと理解すればOK。

livebookやiexだと実行はできますが、効果は発揮できません。
戻り値はElixirにおける内部のコード表現であるASTになっています。

iex> Macro.var(:foo, __MODULE__)
#=> {:foo, [], nil}

式を追加

元の式を書き換える機能といえば、Elixirマクロの出番です。
前項で出てきた変数の内部表現を使ってElixirの式を改変して返します。

実装

下記が最終系です。

livemd
# Bind Macro for Qiita

## Section

```elixir
defmodule Util do
  defmacro bind(ast, var) do
    # 変数の内部表現
    var = Macro.var(var, __CALLER__.module)

    # デバッグ用
    ast
    |> Macro.to_string()
    |> IO.puts()
    |> dbg()

    # 作った変数 = 元のASTという形にして返す
    quote do
      unquote(var) = unquote(ast)
    end
  end
end
```

```elixir
require Util
import Util

[1, 2, 3]
|> Enum.map(& &1 * 2)
|> bind(:a)
```

解説

  • CALLERはマクロの呼び出し元の情報を取得します(livebookだと__CALLER__.moduleはnilです)
  • quoteで元の式を返します
  • unquoteでAST表現されているコードをElixirコードにします
  • (メタプロ基本はElixirSchoolを読むのがおすすめ)

パイプラインから続けて変数束縛できるようにしました。

6
0
0

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
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?