5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OTP28 を利用する Elixir で config 内の正規表現 ~r の利用ができなくなった?

5
Last updated at Posted at 2025-12-13

結論を先に書くと

OTP28 を利用する Elixir で config 内の正規表現 ~r の利用ができなくなった?

は、一時的に利用できなかったが、 Elixir 1.19.3 で入った Enhancements Support /E modifier for regular expressions in config files を使うことで利用できるようになっています。

発見の経緯

Nerves の 旧 kiosk_example, 現 kiosk_demo へのコントリビュートの中で、 mix firmware がコケることに気づいたことが発端です。

エラーは以下、

** (Mix) Could not read configuration file. It has invalid configuration terms such as functions, references, and pids. Please make sure your configuration is made of numbers, atoms, strings, maps, tuples and lists. The following entries are wrong:

Application: :kiosk_example
Key: KioskExampleWeb.Endpoint
Value: [
  url: [host: "localhost"],
  adapter: Bandit.PhoenixAdapter,
  render_errors: [formats: [html: KioskExampleWeb.ErrorHTML, json: KioskExampleWeb.ErrorJSON], layout: false],
  pubsub_server: KioskExample.PubSub,
  live_view: [signing_salt: "TT/yCaKQ"],
  http: [ip: {127, 0, 0, 1}, port: 4000],
  check_origin: false,
  code_reloader: true,
  debug_errors: true,
  secret_key_base: "hb/peKNv21ttmBwvr+/YTwyJ4FJUJzsyteDrKT16A/4WFRm16GuOBd3rYa2KpGNk",
  watchers: [
    esbuild: {Esbuild, :install_and_run, [:kiosk_example, ["--sourcemap=inline", "--watch"]]},
    tailwind: {Tailwind, :install_and_run, [:kiosk_example, ["--watch"]]}], live_reload: [patterns: [~r/priv\/static\/(?!uploads\/).*(js|css|png|jpeg|jpg|gif|svg)$/, ~r/priv\/gettext\/.*(po)$/, ~r/lib\/kiosk_example_web\/(controllers|live|components)\/.*(ex|heex)$/]],
  server: true
]

調べてみると、以下の issue が見つかります。

この issue は途中で、josevalim さんが Fixed in main and will be fixed in the next RC! と記載していますが、その後やっぱり直ってなかったことがわかるスレッドになっています。

再現方法は、 whatyouhide さんの以下でシンプルに再現できます。

原因

josevalim さんが regexes can no longer be compiled into configuration files, I think that's a permanent change with Erlang/OTP 28 :( と記載されていますが、これについて掘ってみたいと思います。

まず、 OTP28 で正規表現にどのような変更が入ったのか?調べると以下が見つかります。

Erlang/OTP 28 uplifts the re module to use PCRE2, instead of the PCRE library.

ref. https://www.erlang.org/blog/highlights-otp-28/#pcre2

re というのは Erlang で正規表現(regular expression)を扱うモジュールです。この re モジュールが正規表現のマッチに利用するライブラリーが、 OTP28 で PCRE から PCRE2 に変わったとのことです。
※ PCRE: Perl Compatible Regular Expressions

そして、この変更によって sabiwara さん曰く

The problem is that regexes in OTP 28 are now using references under the hood.

とのことです。まずこれを確認してみます。

# OTP28
iex(1)> ~r/foo/ |> Map.from_struct
%{
  re_pattern: {:re_pattern, 0, 0, 0, #Reference<0.464910916.1989541891.141153>},
  opts: [],
  source: "foo"
}
# OTP27
iex(1)>  ~r/foo/ |> Map.from_struct
%{
  re_pattern: {:re_pattern, 0, 0, 0,
   <<69, 82, 67, 80, 77, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 255, 255, 255, 255,
     255, 255, 255, 255, 102, 0, 111, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131,
     0, 9, 29, 102, 29, 111, 29, 111, 120, 0, 9, 0>>},
  opts: [],
  source: "foo"
}

なるほど、OTP27 で binary() で記述されてる項が、 OTP28 では reference() に変わっています。

これがどうやら原因です。というのは config には静的なデータしか含めないはずだからです。起動中の VM に依存するような reference()pid() というのは config に書くことはできません。

これだと困るのが、例えば Phoenix の以下のような設定です。

# Watch static and templates for browser reloading.
config :kiosk_demo, KioskDemoWeb.Endpoint,
  live_reload: [
    patterns: [
      ~r"priv/static/(?!uploads/).*(js|css|png|jpeg|jpg|gif|svg)$",
      ~r"priv/gettext/.*(po)$",
      ~r"lib/kiosk_demo_web/(controllers|live|components)/.*(ex|heex)$"
    ]
  ]

この設定があるために mix firmware がコケていたのでした。
※「 Nerves の firmware だったら、 live_reload をそもそも使わないのでは?」はするどいツッコミで、phoenix の dev.exs をそのまま利用して MIX_ENV=devmix firmware をしていたために、この現象にぶち当たりました。

Elixir 1.19.3 ではどのように対応されたか?

そこで Elixir に導入されたのが以下です。

[Kernel] Support /E modifier for regular expressions in config files

ref. https://github.com/elixir-lang/elixir/blob/v1.19/CHANGELOG.md#v1193-2025-11-13

従来 ~r/foo/ と書いていた箇所を ~r/foo/E とすることで静的データとすることができるようです。

# OTP28
iex(1)> ~r/foo/E |> Map.from_struct
%{
  re_pattern: {:re_exported_pattern,
   <<114, 101, 45, 80, 67, 82, 69, 50, 164, 254, 191, 127, 1, 0>>, "foo",
   [:export],
   <<83, 50, 82, 80, 10, 0, 46, 0, 1, 8, 8, 0, 1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
     7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
     26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
     45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
     ...>>},
  ...
}

ただし、この表記の場合、実行時の overhead がかかると書いてあります。

the cost of a runtime overhead every time to re-import it every time it is executed

ref. https://hexdocs.pm/elixir/1.19.3/Regex.html#module-modifiers

実行時に都度 exported な ~r/foo/E から読み込んで ~r/foo/ として処理するからだと思います。


以上です〜

5
1
2

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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?