エリクサー/ルビーでの機能/メソッドルックアップ


それで、序文に、私はElxirを学んでいるRuby devです.私は私がルビーで知っていることに戻ってエリクサーで学んでいることを比較するのが好きです.今日、私は機能/方法ルックアップについて話したいです.
この記事は私が学んでいた時に促された GenServer 通しPragmaticStudio's course (私は非常にそれをお勧めします)と、彼らは言及GenServer 手動で定義しない場合は、関数のデフォルト実装を注入できます.これらのデフォルト関数は以下の通りです:
  • init/1
  • child_spec/1
  • code_change/3
  • terminate/2
  • handle_info/2
  • handle_cast/2
  • handle_call/3
  • そして、それらをオーバーライドしたい場合は、現在のモジュール内の関数を同じシグネチャ(name + unique)で定義します.
    しかし、これは私が考えて、どのようにエリクサーは、これを可能にする関数定義をルックアップする方法ですか?
    Elixirに飛び込む前に、Rubyでのメソッドルックアップ方法を初めて見てみましょう.

    あなたのお父さん、お父さんのお父さん、お父さんのお父さんのお父さん。


    私は実際に前にこのブログについては、自分自身だった!(私は実際にこれがどれくらいの意味を持っているか全くわからないが、私はまだ名誉のバッジのようにそれを着るつもりです)


    Rubyのメソッド検索チェーンは文字通り1つのメソッドコールで要約できます.ancestors .
    Foo = Class.new
    Foo.ancestors # Lookup path for instance methods
    #=> [Foo, Object, Kernel, BasicObject]
    
    Foo.class.ancestors # Lookup path for class (singleton) methods, read my blog to learn more!
    #=> [Class, Module, Object, Kernel, BasicObject] 
    
    それはルビーのための全体のメソッドルックアップチェーンを見る方法です.
    事実、#ancestors が定義されているBasicObject だから、あなたが呼び出すときFoo.ancestors , これはこうです.
    You: Do you implement `#ancestors`?
    
    - Foo: No
    - Object: No
    - Kernel: No
    + BasicObject: Yes I do!
    
    そして、それはどのように動作します!
    そうすれば、私たちはinject Rubyへの関数は、新しく注入された関数がルックアップ(定義)中に見つかった最初のメソッドであることを確認する必要があります#ancestors どこよりも前BasicObject ).
    したがって、Rubyはこれを行うことができますancestors , しかし、エリクサーは実際にはancestors/inheritance , では、何が本当にここで起こっている?
    推測するのを止める時間だから、例を試してみることにした.
    defmodule Parent do
      defmacro __using__(_opts) do
        quote do
          def injected do
            "This is from Parent"
          end
        end
      end
    end
    
    defmodule Child do
      use Parent
    
      def injected do
        "This is from Child"
      end
    end
    
    私の期待される行動はParent.injected/0 私のそばで上り詰められるでしょうChild.injected/0 , しかし、このファイルを実行しようとすると-iex lookup.exs , 次の警告メッセージがポップアップします.

    warning: this clause cannot match because a previous clause at line 12 always matches lookup.exs:14


    奇妙な警告が、実際にそれを実行しようとしましょう.
    iex(1)> Child.injected
    "This is from Parent"
    
    驚いたことに(私は警告メッセージを見たとき、驚きはちょっと台無しにされます)、走ることChild.injected 私を返しますParent.injected/0 の代わりにChild.injected/0 , なぜ?
    私はdig into the doc そして、理由を見つけてください、しかし、ドキュメントは本当に私からそれほど私と言わない事実GenServer は関数を注入します.しかし、私はすでにそれを知っていました、私はちょうどそれがそうしている方法を知りたいです.
    それで、医者は、何を助けませんでしたか?

    ソースコード!


    他のほとんどの言語と同様に、エリクサーはオープンソースですので、私は非常に簡単に潜入することができますsource code itself .
    今何かsuspiciously named defoverridable , そして、私はそれがそうであるかもしれないことを熱望します、しかし、私は私の仮説に更なる証明を望みます.
    クイックグーグルdefoverridable 私を郵送してくださいElixir Forum , 人自身によって書かれて、ジョスはvalimです.

    解決策


    それで、そのポストを通して、私は私が使うことができるとわかりましたdefoverridable , つまり、次のコードを更新できます.
    defmodule Parent do
      defmacro __using__(_opts) do
        quote do
          def injected do
            "This is from Parent"
          end
    
    +     defoverridable injected: 0
        end
      end
    end
    
    defmodule Child do
      use Parent
    
      def injected do
        "This is from Child"
      end
    end
    
    そして、我々がそれを走らせるならば.
    iex(1)> Child.injected
    "This is from Child"
    
    VILはそれを動作させる!パーフェクト🎉

    結論


    彼のポストではdefoverridable 私にとって、彼はまたdefoverridable は推奨されません、そして、彼らは実際に@optional_callback オプションとしてメソッドをマークするには@impl (“子”がその機能を実装するのを確実にするためにコンパイラが使用する)ので、それは私が学んだ興味深いことでもありました.私は、あなたがもう少しそれを理解するためにポストとコメントを読むことを勧めます.
    私の質問は文字通り彼のポストの最初の10行で答えられました、それで、私が最初にそれを見つけたならば、それは本当によかったでしょう.その後、ソースコードに入っていなかったら、どんなキーワードを探すのか知りませんでした.
    しかし、ある時点でエリクサーがルビーのような遺産を持っていると思ったので、おもしろい旅をしてきた.
    また、Elxirが本当に「関数検索」を持っていないので、タイトルは技術的に正しくありません.