2017-05-04 1 views
1

j'ai un protocole qui a une seule fonction avec une API relativement complexe:Mettre en œuvre un protocole avec un comportement dans une macro dans Elixir

defprotocol Foo do 
    def complex(foo, x, y) 
end 

Je veux fournir un moyen de mettre en œuvre ce protocole pour une commune et cas d'utilisation plus simple. Après quelques essais, je suis venu avec ce qui suit:

defmodule Bar do 
    @callback simple(any, any) :: boolean 

    defmacro __using__(_) do 
    quote do 
     @behaviour Bar 

     defimpl Foo do 
     # TODO this is really hacky. What is a better way to reference parent module? 
     # Note that the 1 in drop(1) is from Foo's module name 
     @parent_module __MODULE__ |> Module.split |> Enum.drop(1) |> Module.concat 
     def complex(bar, x, _y) do 
      matches = @parent_module.simple(bar, x) 
      if matches, do: [x], else: [] 
     end 
     end 
    end 
    end 
end 

Maintenant, je peux mettre en œuvre Foo via , mais la façon dont @parent_module est déterminé et utilisé semble mal. Y at-il une meilleure façon que d'utiliser le hack dans le TODO ci-dessus? Quelle est la manière idiomatique de le faire? Je préfère ne pas transformer Foo en un comportement parce que la répartition et la consolidation des protocoles correspond au modèle d'utilisation.

+0

Je ne vois pas vraiment le problème que vous essayez de résoudre . Pouvez-vous fournir un cas d'utilisation complexe et simple? Ce qui m'embrouille, c'est l'utilisation de defimpl sans l'option 'for:'. –

+1

Le paramètre 'for' utilise par défaut le module dans lequel' defimpl' est défini –

+0

Pourquoi '__MODULE__' ne fonctionne-t-il pas? –

Répondre

3

Vous pouvez obtenir et stocker dans une variable locale du nom du module parent, puis le mettre dans un attribut dans la mise en œuvre, puis l'utiliser:

defmodule Bar do 
    @callback simple(any, any) :: boolean 

    defmacro __using__(_) do 
    quote do 
     @behaviour Bar 

     parent_module = __MODULE__ 

     defimpl Foo do 
     @parent_module parent_module 

     def complex(bar, x, _y) do 
      matches = @parent_module.simple(bar, x) 
      if matches, do: [x], else: [] 
     end 
     end 
    end 
    end 
end 
+0

Great! La variable 'parent_module' est-elle réellement présente dans le module résultant généré, ou est-elle seulement présente dans la macro? Je demande parce que je pensais que vous ne pouviez pas déclarer des variables comme ça dans les modules qui ne sont pas des attributs de module. –

+1

Il sera dans le code généré, mais en raison de l'hygiène macro, il ne serait pas accessible à partir des parties restantes du module appelant avec le nom 'parent_module'. Et il ne sera pas présent lors de l'exécution, seulement au moment de la compilation. – Dogbert