最近Mojoでトレイトが紹介されたので試してみようと思いました。現在、組み込みの特性には、 、 、 、 、 、 、 が含まれます (これらの命名規則では、接尾辞 "-able" が必要のようです!)。 トレイトは構造体で機能します。特性を実装するには、その特性に一致するメソッドを構造体に追加するだけです。次に、特性名をパラメータとして渡します。 CollectionElement Copyable Destructable Intable Movable Sized Stringable @value struct Duck(Quackable): fn quack(self): print("Quack!") Mojo の デコレーターは 、 、 などのライフサイクル メソッドを構造体に挿入し、自分で追加する必要がないため、作業が少し簡素化されます。 @value __init__() __copyinit__() __moveinit__() Mojo のトレイトはメソッドのデフォルト実装をまだサポートしていないため、上記の メソッド本体の サポートされています。 を使用することもできます。これにより、Python と同様の効果が得られます。 Quackable ... pass Mojo の特性と Go インターフェイス 名前は異なりますが、Mojo のトレイトに対する基本的なアプローチは Go インターフェイスを思い出させます。 Go では、次のように 構造体を定義し、 インターフェイスを実装します。 Duck Quackable type Quackable interface { quack() } そして、このインターフェースを満たす構造体を作成するには、次のようにします。 type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") } Mojo の実装と比較してください。 trait Quackable: fn quack(self): ... @value struct Duck(Quackable): fn quack(self): print("Quack!") Mojo バージョンはさらに簡潔で、 構造体型内に メソッド定義をネストしていると思います。構造体での作業に加えて、Mojo のトレイトには Go と同様に、 キーワードが必要ありません。 Duck quack() implements 興味深いことに、Mojo のドキュメントでは、デフォルトの実装は 許可されていない、つまり将来的には許可される可能性があると指摘されています。これは、Mojo が、インターフェース するのではなく、インターフェース 構造体に重点を置く Go とは異なるアプローチを追求する可能性があることを意味します。 まだ を実装 を満たす Go ではデフォルトのメソッド実装は必要ありません。インターフェイス/トレイト実装は単に Go に存在しない概念であるため、埋め込みでも同様の効果が得られます。 Mojo では状況が異なる場合があります。 Mojoの特性の使用方法 特性とインターフェイスの価値は、コードを再利用可能にすることです。たとえば、Mojo では、特性タイプを受け入れる関数を作成できます。 fn make_it_quack[T: Quackable](could_be_duck: T): could_be_duck.quack() そして、同じトレイトを実装するさまざまな構造体を入力として渡します。すべてが正常に機能します。たとえば、これは を実装する 構造体です。 Quackable Goose @value struct Goose(Quackable): fn quack(self): print("Honk!") ここで、 と 両方で 呼び出すには (プログラムへのエントリ ポイントとして Mojo の 関数が必要であることに注意してください): Goose Duck make_it_quack main def main(): make_it_quack(Duck()) make_it_quack(Goose()) これの出力は次のようになります Quack! Honk! 特性エラー 特性を実装していないもの、たとえば 関数に渡そうとすると、プログラムはコンパイルされません。 Quackable StealthCow make_it_quack @value struct StealthCow(): pass make_it_quack(StealthCow()) 以下のエラー メッセージはもっと説明的だったかもしれません。おそらく Mojo チームがそれを改善してくれるでしょうか? error: invalid call to 'make_it_quack': callee expects 1 input parameter, but 0 were specified から メソッドを削除した場合も同じです。ここでは、わかりやすい説明的なエラーが表示されます。 Goose quack struct 'Goose' does not implement all requirements for 'Quackable' required function 'quack' is not implemented 純粋な Python アプローチに対するこの種の静的型チェックの利点は、エラーを簡単にキャッチでき、コードが単純にコンパイルされないため、本番環境へのミスの送信を回避できることです。 継承 Mojo のトレイトはすでに継承をサポートしているため、「Quackable」トレイトは次のように「Audible」トレイトを拡張できます。 trait Audible: fn make_sound(self): ... trait Quackable(Audible): fn quack(self): ... これは、 構造体が 特性に準拠するために と の両方を実装する必要があることを意味します。 Duck Quackable quack make_sound これは、Go の「インターフェイスの埋め込み」の概念に似ています。他のインターフェイスから継承する新しいインターフェイスを作成するには、次のように親インターフェイスを埋め込みます。 type Quackable interface { Audible // includes methods of Audible in Quackable's method set } 静的メソッド 特性は、構造体のインスタンスを作成せずに機能する静的メソッドも受け入れます。 trait Swims: @staticmethod fn swim(): ... @value struct Duck(Quackable, Swims): fn quack(self): print("Quack!") @staticmethod fn swim(): print("Swimming") fn make_it_swim[T: Swims](): T.swim() 次のように静的メソッドを呼び出します。 def main(): make_it_quack(Duck()) make_it_quack(Goose()) Duck.swim() これは出力します: Quack! Honk! Swimming 最後のメソッド呼び出しでは、Duck インスタンスが作成されないことに注意してください。これは、オブジェクト指向プログラミングとは多少異なりますが、Python の静的メソッドがどのように動作するかです。 Mojo は、この Python 機能に基づいて構築されています。 Go インターフェイスと比較した Mojo トレイトの制限 興味深いことに、空の を使用した Go トリックは、任意の型を渡すことができ、Go ジェネリックが導入される前に Go コミュニティで人気がありましたが、Mojo 型関数 では機能しません。{} interface{} fn 構造体は、 や などのライフサイクル メソッドの少なくとも 1 つを実装する必要があります。この場合、構造体は または に準拠し、特性タイプを受け入れる関数で使用されます。 __len__ __str__ Sized Stringable これは実際には本当の制限ではなく、Mojo では非常に理にかなっています。Mojo では、動的型付けが必要な場合は、より柔軟な 関数にフォールバックし、通常の Python マジックを適用して未知の関数を処理できるからです。種類。 def より厳密な Mojo 関数は、 のような型を使用する汎用構造体でも動作します。詳細については ご覧ください。 fn DynamicVector 、こちらを 私が見たもう 1 つの制限は、特性ではなく構造体とそのメソッドに関係しており、クリーン アーキテクチャの実装やコードをさまざまな部分に分離する際の潜在的な障害となります。 Go と Mojo の構造体メソッド定義を使用した前述の例の 1 つを考えてみましょう。 type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") } @value struct Duck(Quackable): fn quack(self): print("Quack!") Mojo の例では、より Python に似た構文に従い、メソッド定義を構造体の内部に直接入れ子にしていますが、Go バージョンではメソッド定義を構造体自体から分離できます。このバージョンでは、多くの型とメソッドを含む非常に長い構造体がある場合、多少読みにくくなります。 ただし、これは決定的な違いではなく、注意すべき点です。 Mojo の特性は、この言語がまだ初期段階にあるにもかかわらず、すでにかなり便利になっています。 Go の哲学はシンプルさを重視し、機能を最小限に抑えようとしますが、将来的には Mojo トレイトにさらに多くの機能が追加され、さらに強力になり、さまざまなユースケースが可能になる可能性があります。