Cristian Garcia
Cristian Garcia

Reputation: 9859

Can "function" implement a protocol in Elixir?

Is there a way to make the function "type" implement a protocol in Elixir? I am playing around with a custom Functorprotocol and was wondering if I could make functions implement it (ignoring all the limitations of Elixir's type system).

The implementation (if function where a type) would be

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

Given that you could do

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

Not that I'd use this for anything serious, just wondering.

Upvotes: 1

Views: 289

Answers (4)

Cristian Garcia
Cristian Garcia

Reputation: 9859

It turns out you can. Here is the implementation I am working on for what might result in a library called 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

It was on the Protocol section of Getting Started

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

Upvotes: 1

Your example doesn't make a whole lot of sense to me because there is only a single type of function in elixir, but Function is a type that you can implement protocols for.

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

The problem that I see is that arity is very important in Elixir/Erlang and I don't see how to account for that in a straightforward way.

Upvotes: 1

Chris Meyer
Chris Meyer

Reputation: 1611

I'm not sure why you need a Protocol for this... sorry if I've missed something important in your question. You can implement a function with guards that does this though:

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

and then you can write:

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

I guess I'm just not sure what the protocol gets you that a function doesn't.

Upvotes: 0

asonge
asonge

Reputation: 1267

Because Erlang/Elixir are dynamically typed, the strict answer is no. Functions really are only differentiated by arity (or for named functions, module, function name, and arity).

You can specify more specific types via Typespecs, and dialyzer (usable in a mix task via dialyxer) will do some assertions on that, but this is not part of the compile toolchain or runtime and provides no guarantees. Not all the violations will be caught by dialyzer (it's based on success typing), but it's a good start.

Upvotes: 0

Related Questions