如何配置让Elixir模块的__info__函数正常可用?
问题:调用Elixir模块__info__函数触发FunctionClauseError的解决方法
复现场景
IEx执行日志
iex(2)> Mirror.Imports.Product Mirror.Imports.Product iex(3)> Mirror.Imports.Product.parse_description("aaaa") [] iex(4)> Mirror.Imports.Product.__info__(:attribute) ** (FunctionClauseError) no function clause matching in Mirror.Imports.Product.__info__/1 The following arguments were given to Mirror.Imports.Product.__info__/1: # 1 :attribute (mirror 0.1.0) Mirror.Imports.Product.__info__/1 iex(4)> Mirror.Imports.Product.__info__(:function) ** (FunctionClauseError) no function clause matching in Mirror.Imports.Product.__info__/1 The following arguments were given to Mirror.Imports.Product.__info__/1: # 1 :function (mirror 0.1.0) Mirror.Imports.Product.__info__/1
对应的Product模块代码
defmodule Mirror.Imports.Product do use Ecto.Schema import Ecto.Changeset def parse_description(description) do {:ok, document} = Floki.parse_document(description) Floki.find(document, "img") |> Enum.map(fn {_, x,_} -> x end) |> Enum.map(fn [_, {"src", path}] -> path end) |> Enum.map(fn x -> Path.split(x) |> Enum.slice(3, 4) |> Path.join() end) end end
调用Mirror.Imports.Product.__info__(:attribute)和__info__(:function)时均触发FunctionClauseError,如何配置让__info__函数正常可用?
解决方法
原因分析
use Ecto.Schema会在模块中注入自定义的__info__/1函数,这个函数只处理和Ecto Schema相关的参数(比如:schema、:source),覆盖了Elixir默认的__info__/1实现——而默认实现原本负责处理:attribute、:function这类参数,所以调用时会出现函数子句不匹配的错误。
方案一:保留默认__info__实现
在模块中手动添加对默认__info__的调用,让未被Ecto处理的参数转发给默认实现:
defmodule Mirror.Imports.Product do use Ecto.Schema import Ecto.Changeset # 添加该行,保留默认__info__实现 def __info__(key), do: super(key) def parse_description(description) do {:ok, document} = Floki.parse_document(description) Floki.find(document, "img") |> Enum.map(fn {_, x,_} -> x end) |> Enum.map(fn [_, {"src", path}] -> path end) |> Enum.map(fn x -> Path.split(x) |> Enum.slice(3, 4) |> Path.join() end) end end
修改后再调用__info__(:function)或__info__(:attribute)就能正常返回结果。
方案二:使用Module模块的内置函数
不需要修改模块代码,直接用Elixir标准库Module模块的函数来获取模块信息,这些函数不受自定义__info__的影响:
- 获取模块属性:
Module.attributes(Mirror.Imports.Product) - 获取模块函数:
Module.definitions_in(Mirror.Imports.Product, :def)
内容的提问来源于stack exchange,提问作者Chen Yu
相关产品推荐
相关产品推荐

