2017-09-04 4 views
1

Par exemple, je veux avoir une fonction qui peut être appelée comme ceci:Comment construire des fonctions pour ne pas avoir de conflits?

foo :bar, key: "value" do 
    "some block value" 
end 

ou

foo key: "value" do 
    "some block value" 
end 

ou

foo :bar do 
    "some block value" 
end 

ou simplement

foo key: "value" 

Pour commencer, la définition de la fonction ressemble à quelque chose comme ceci:

def foo(bar, opts \\ [], [do: block]) 

Au moment où je dois adapter le cas où je viens de passer des options mais pas le bar, je reçois erreur de compilation. Je comprends tout à fait pourquoi (opts \\ [] crée plus de fonctions qui correspondent à mon autre définition de fonction). Alors, quel est le bon travail pour permettre à cette méthode foo de fonctionner?

Répondre

4

Je définirais les diverses possibilités comme des fonctions d'arité distinctes qui appellent une fonction do_foo/3 avec les 3 valeurs. Supposant bar est jamais une liste (si elle peut être une liste cette chose devient ambiguë pour arité 1 et 2 parce que nous ne pouvons pas savoir si la valeur est pour bar ou opts) vous pouvez faire quelque chose comme:

defmodule A do 
    def foo(opts) when is_list(opts), do: do_foo(nil, opts, nil) 
    def foo(bar), do: do_foo(bar, nil, nil) 
    def foo(opts, [do: block]) when is_list(opts), do: do_foo(nil, opts, block) 
    def foo(bar, [do: block]), do: do_foo(bar, nil, block) 
    def foo(bar, opts, [do: block]), do: do_foo(bar, opts, block) 

    defp do_foo(bar, opts, block) do 
    IO.inspect {bar, opts, block} 
    end 
end 

A.foo :bar, key: "value" do 
    "some block value" 
end 

A.foo key: "value" do 
    "some block value" 
end 

A.foo :bar do 
    "some block value" 
end 

A.foo key: "value" 

Sortie:

{:bar, [key: "value"], "some block value"} 
{nil, [key: "value"], "some block value"} 
{:bar, nil, "some block value"} 
{nil, [key: "value"], nil} 
+0

Ah, brillant. N'a pas pensé à faire une fonction secondaire non ambiguë. Merci! – Grocery