【问题标题】:Can "function" implement a protocol in Elixir?“函数”可以在 Elixir 中实现协议吗?
【发布时间】:2016-03-05 20:14:32
【问题描述】:

有没有办法让函数“类型”在 Elixir 中实现协议?我正在玩自定义的Functorprotocol,想知道是否可以让函数实现它(忽略 Elixir 类型系统的所有限制)。

实现(如果 function 有一个类型)将是

defimpl Category.Functor, for: function do
  def fmap(f, g), do: &(g.(f.(x))
end

因为你可以做到

f_2x_plus_1 = &(&1 * 2) |> Functor.fmap(&(&1 + 1))
f_2x_plus_1.(1) == 3

并不是说我会把它用于任何严肃的事情,只是想知道。

【问题讨论】:

标签: elixir


【解决方案1】:

你的例子对我来说没有多大意义,因为在 elixir 中只有一种类型的函数,但 Function 是一种你可以 实施协议。

defimpl Category.Functor, for: Function do
  def fmap(f, g), do: &(g.(f.(&1))
end

我看到的问题是,arity 在 Elixir/Erlang 中非常重要,我不知道如何以直接的方式解释这一点。

【讨论】:

  • 查看我自己的答案。手动检查 arity 并不理想,但我想这是唯一的方法。
  • 您有时可以使用 Kernel.apply 来解决 arity 问题。
  • 嗯...但我不认为我可以实际使用apply,因为它的功能组合,您无法实际评估。
【解决方案2】:

事实证明你可以。这是我正在为可能会产生一个名为excategory的库的实现@

defmodule Category.Function do
  def compose(f, g) do
    arity_f = :erlang.fun_info(f)[:arity]
    arity_g = :erlang.fun_info(g)[:arity]

    case {arity_f, arity_g} do
      {1, 1} ->
        &(g.(f.(&1)))
      arities ->
        raise "Function are not of arity 1, got #{arities}"
    end

  end
end

defimpl Category.Functor.P, for: Function do
  defdelegate map(f, g), to: Category.Function, as: :compose
end

它位于入门的协议部分

http://elixir-lang.org/getting-started/protocols.html

【讨论】:

  • 这对你有什么好处?我仍然不清楚您要启用什么语法。
  • @ChrisMeyer 在 Haskell(和 Cathegory 理论)中,函数是 Functor(也是 Applicative 和 Monad)。有用与否我想使用协议在 Elixir 中实现常见的 Haskell 类型类。我猜 Monads 在 Elixir 中不会那么受欢迎,但 Monoids 可能非常有用。
【解决方案3】:

因为 Erlang/Elixir 是动态类型的,所以严格的答案是否定的。函数实际上只通过数量来区分(或者对于命名函数、模块、函数名和数量)。

您可以通过Typespecs 指定更具体的类型,并且dialyzer(可通过dialyxer 在混合任务中使用)将对此进行一些断言,但这不是编译工具链或运行时的一部分,并且不提供任何保证。并非所有违规行为都会被透析器捕获(它基于成功输入),但这是一个好的开始。

【讨论】:

  • 我真的不需要保证它的数量。查看更新。
  • 不过,我真的不知道答案会发生什么变化。如果你不能在 typespecs 中表达它,你可能什么都没有。如果你可以通过 typespecs 来表达它,你可以用宏做一些技巧来帮助自己。
【解决方案4】:

我不确定您为什么需要 Protocol 来解决这个问题...抱歉,如果我错过了您问题中的重要内容。你可以用守卫来实现一个函数:

defmodule Functor do
  def fmap(f, g) where is_function(f) and is_function(g) do
    &(g.(f.(&1)))
  end
end

然后你可以写:

iex> f_2x_plus_1 = Functor.fmap(&(&1 * 2), &(&1 + 1))
#Function<0.100612233/1 in Functor.fmap/2>
...> f_2x_plus_1.(1)
3

我想我只是不确定协议能得到什么而函数没有。

【讨论】:

  • 在 Haskell 中,Functor 是一个类型类,而不是具体类型。 Elixir 中最好的类似物(并且运行良好)是协议。所以我一直在移植一堆这些(类别理论)类型类,例如 Functor、Applicative、Monad、Monoid、SemiGroup 等。协议让我超越函数的是基于类型的调度和可扩展性。这个想法是您创建一个处理 Monoid 的库,为您的自定义结构/类型实现 Monoid 协议,然后可以将其传递给该模块。
猜你喜欢
  • 2016-05-09
  • 2017-07-01
  • 2017-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-02
  • 1970-01-01
  • 2015-12-31
相关资源
最近更新 更多