最近Mojoでトレイトが紹介されたので試してみようと思いました。現在、組み込みの特性には、 CollectionElement 、 Copyable 、 Destructable 、 Intable 、 Movable 、 Sized 、 Stringableが含まれます (これらの命名規則では、接尾辞 "-able" が必要のようです!)。
トレイトは構造体で機能します。特性を実装するには、その特性に一致するメソッドを構造体に追加するだけです。次に、特性名をパラメータとして渡します。
@value struct Duck(Quackable): fn quack(self): print("Quack!")
Mojo の@value
デコレーターは__init__()
、 __copyinit__()
、 __moveinit__()
などのライフサイクル メソッドを構造体に挿入し、自分で追加する必要がないため、作業が少し簡素化されます。
Mojo のトレイトはメソッドのデフォルト実装をまだサポートしていないため、上記のQuackable
メソッド本体の...
サポートされています。 pass
を使用することもできます。これにより、Python と同様の効果が得られます。
名前は異なりますが、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 バージョンはさらに簡潔で、 Duck
構造体型内にquack()
メソッド定義をネストしていると思います。構造体での作業に加えて、Mojo のトレイトには Go と同様に、 implements
キーワードが必要ありません。
興味深いことに、Mojo のドキュメントでは、デフォルトの実装はまだ許可されていない、つまり将来的には許可される可能性があると指摘されています。これは、Mojo が、インターフェースを実装するのではなく、インターフェースを満たす構造体に重点を置く Go とは異なるアプローチを追求する可能性があることを意味します。
Go ではデフォルトのメソッド実装は必要ありません。インターフェイス/トレイト実装は単に Go に存在しない概念であるため、埋め込みでも同様の効果が得られます。 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!")
ここで、 Goose
とDuck
両方でmake_it_quack
呼び出すには (プログラムへのエントリ ポイントとして Mojo の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 機能に基づいて構築されています。
興味深いことに、空のinterface{}
を使用した Go トリックは、任意の型を渡すことができ、Go ジェネリックが導入される前に Go コミュニティで人気がありましたが、Mojo 型関数fn
では機能しません。{}
構造体は、 __len__
や__str__
などのライフサイクル メソッドの少なくとも 1 つを実装する必要があります。この場合、構造体はSized
またはStringable
に準拠し、特性タイプを受け入れる関数で使用されます。
これは実際には本当の制限ではなく、Mojo では非常に理にかなっています。Mojo では、動的型付けが必要な場合は、より柔軟なdef
関数にフォールバックし、通常の Python マジックを適用して未知の関数を処理できるからです。種類。
より厳密な 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 トレイトにさらに多くの機能が追加され、さらに強力になり、さまざまなユースケースが可能になる可能性があります。