[elixir! #0031] 掌控元编程的魔法(2) ---- 编译时生成大量函数

网络媒体类型(MIME type) 是互联网通信协议中很重要的一部分,每种 MIME 类型对应着任意种文件的扩展名.

elixir 的 mime 包实现了 mime 类型和扩展名之间的转换. 让我们来看看它的实现方法.

mime.types 文件

# MIME type                    Extensions
application/1d-interleaved-parityfec
application/3gpdash-qoe-report+xml
application/3gpp-ims+xml
application/A2L                    a2l
application/activemessage
application/alto-costmap+json
application/alto-costmapfilter+json
application/alto-directory+json
application/alto-endpointcost+json
application/alto-endpointcostparams+json
application/alto-endpointprop+json
application/alto-endpointpropparams+json
application/alto-error+json
application/alto-networkmap+json
application/alto-networkmapfilter+json
application/AML                    aml
application/andrew-inset            ez
application/applefile
application/ATF                    atf
application/ATFX                atfx
application/ATXML                atxml
application/atom+xml                atom
application/atomcat+xml                atomcat
application/atomdeleted+xml            atomdeleted
application/atomicmail
application/atomsvc+xml                atomsvc
application/auth-policy+xml            apxml
application/bacnet-xdd+zip            xdd

该文件中包含了一千多种 mime 类型的对应关系. 首先, 我们在编译时读取这个文件的内容:

  stream = File.stream!("lib/mime.types")

  mapping = Enum.flat_map(stream, fn(line) ->
    if String.starts_with?(line, ["#", "\n"]) do
      []
    else
      [type|exts] = line |> String.strip |> String.split
      [{type, exts}]
    end
  end)

将文件转化成了元素为 {type, exts} 的列表 mapping. 然后利用宏生成函数:

  @spec mime_to_ext(String.t) :: list(String.t) | nil
  defp mime_to_ext(type)

  ...

  for {type, exts} <- mapping do
    defp mime_to_ext(unquote(type)), do: unquote(exts)
  end

这样, 我们就在编译时将 mime.types 文件转化成了一千多个函数, 有效地提高了运行的速度.

你可能感兴趣的:(元编程,elixir)