paint-brush
Mojo Özellikleri: Go Arayüzleriyle Nasıl Karşılaştırılır?ile@a2svior
1,836 okumalar
1,836 okumalar

Mojo Özellikleri: Go Arayüzleriyle Nasıl Karşılaştırılır?

ile Valentin Erokhin6m2023/12/26
Read on Terminal Reader

Çok uzun; Okumak

Örneklerle Mojo Özellikleriyle çalışmaya ilişkin bir kılavuz ve Mojo Özelliklerinin Go Arayüzleriyle karşılaştırılması. Mojo özelliklerinin sınırlamaları ve uyarıları.
featured image - Mojo Özellikleri: Go Arayüzleriyle Nasıl Karşılaştırılır?
Valentin Erokhin HackerNoon profile picture
0-item

Özellikler yakın zamanda Mojo'ya tanıtıldı, bu yüzden onları deneyeyim diye düşündüm. Şu anda yerleşik özellikler arasında CollectionElement , Copyable , Destructable , Intable , Movable , Sized ve Stringable yer alıyor ("-able" son eki bu adlandırma kurallarında yer alıyor gibi görünüyor!).

Özellikler yapılar üzerinde çalışır. Bir özelliği uygulamak için, özellikle eşleşen bir yapıya bir yöntem eklemeniz yeterlidir; daha sonra özellik adını parametre olarak iletin:

 @value struct Duck(Quackable): fn quack(self): print("Quack!")


Mojo'daki @value dekoratörü __init__() , __copyinit__() ve __moveinit__() gibi yaşam döngüsü yöntemlerini yapıya ekleyerek, bunları kendimiz eklemek zorunda kalmadığımız için hayatımızı biraz basitleştirir.


Mojo'daki özellikler henüz yöntemler için varsayılan uygulamaları desteklememektedir, dolayısıyla yukarıdaki Quackable yönteminin gövdesinde ... bulunmaktadır. Python'dakiyle aynı etkiye sahip olacak pass da kullanabilirsiniz.

Mojo Özellikleri ve Go Arayüzleri

Farklı isme rağmen Mojo'daki özelliklere yönelik temel yaklaşım bana Go arayüzlerini hatırlatıyor. Go'da bir Duck yapısı tanımlayacak ve şöyle bir Quackable arayüzü uygulayacaksınız:


 type Quackable interface { quack() }


Ve bu arayüzü karşılayan bir yapı oluşturmak için:


 type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") }


Bunu bir Mojo uygulamasıyla karşılaştırın:


 trait Quackable: fn quack(self): ... @value struct Duck(Quackable): fn quack(self): print("Quack!")


Mojo versiyonunun daha da kısa olduğunu düşünüyorum; quack() yöntemi tanımını Duck yapı tipinin içine yerleştiriyor. Yapılar üzerinde çalışmanın yanı sıra, Mojo'daki özellikler, tıpkı Go'da olduğu gibi, bir implements anahtar sözcüğü gerektirmez.


İlginç bir şekilde, Mojo belgelerinde varsayılan uygulamalara henüz izin verilmediğine, yani bunlara gelecekte izin verilebileceğine dikkat çekiliyor. Bu, Mojo'nun, bir arayüzü uygulamaya değil, tatmin eden yapılara odaklanan Go'dan farklı bir yaklaşım izleyebileceği anlamına gelir.


Go'da varsayılan yöntem uygulamalarına gerek yoktur ve arayüz/özellik uygulaması Go'da mevcut olmayan bir kavram olduğundan benzer bir etki gömme ile elde edilir. Mojo'da işler farklı olabilir.

Mojo Özellikleri Nasıl Kullanılır?

Özelliklerin ve arayüzlerin değeri, kodu yeniden kullanılabilir kılmaktır. Mesela Mojo’da özellik tiplerini kabul eden fonksiyonlar yazabilirsiniz…


 fn make_it_quack[T: Quackable](could_be_duck: T): could_be_duck.quack()


Ve sonra girdiyle aynı özelliği uygulayan farklı yapıları aktarın; her şey işe yarayacak! Örneğin, burada Quackable uygulayan bir Goose yapısı var:


 @value struct Goose(Quackable): fn quack(self): print("Honk!")


Ve burada, hem Goose hem de Duck make_it_quack çağırmak için (unutmayın, programınıza giriş noktası olarak Mojo'daki main işlevine ihtiyacınız vardır):


 def main(): make_it_quack(Duck()) make_it_quack(Goose())


Bunun çıktısı olacak


 Quack! Honk!


Özellik Hataları

make_it_quack işlevine Quackable özelliğini uygulamayan bir şeyi (örneğin StealthCow aktarmaya çalışırsam program derlenmez:


 @value struct StealthCow(): pass


 make_it_quack(StealthCow())


Aşağıdaki hata mesajı daha açıklayıcı olabilirdi; belki Mojo ekibi bunu geliştirebilir?


 error: invalid call to 'make_it_quack': callee expects 1 input parameter, but 0 were specified


Goose quack yöntemini kaldırırsam da aynısı olur; burada güzel, açıklayıcı bir hata alıyoruz:


 struct 'Goose' does not implement all requirements for 'Quackable' required function 'quack' is not implemented


Bu tür statik tip kontrolünün saf Python yaklaşımına göre avantajı, hataları kolayca yakalayabilmemiz ve kodun derlenmeyeceği için hataların üretime gönderilmesini önleyebilmemizdir.

Miras

Mojo'daki özellikler zaten kalıtımı desteklemektedir, dolayısıyla "Quackable" özelliğimiz "Audible" özelliğini şu şekilde genişletebilir:


 trait Audible: fn make_sound(self): ...


 trait Quackable(Audible): fn quack(self): ...


Bu, Duck yapısının Quackable özelliğine uyum sağlamak için hem quack hem de make_sound uygulaması gerektiği anlamına gelir.


Bu, Go'daki "Arayüz yerleştirme" kavramına benzer; burada diğer arayüzlerden miras alan yeni bir arayüz oluşturmak için ana arayüzleri şu şekilde gömebilirsiniz:


 type Quackable interface { Audible // includes methods of Audible in Quackable's method set }

Statik Yöntemler

Özellikler ayrıca bir yapının örneğini oluşturmadan çalışan statik yöntemleri de kabul eder:


 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()


Bunun gibi statik bir yöntem çağırırsınız:


 def main(): make_it_quack(Duck()) make_it_quack(Goose()) Duck.swim()


Hangisi çıktı verecek:


 Quack! Honk! Swimming


Son yöntem çağrısında Duck örneğinin oluşturulmadığına dikkat edin. Python'daki statik yöntemler bu şekilde çalışır ve nesne yönelimli programlamadan biraz ayrılır. Mojo bu Python işlevselliğini temel alır.

Go Arayüzleriyle Karşılaştırıldığında Mojo Özelliklerinin Sınırlamaları

İlginç bir şekilde, herhangi bir türün geçişine izin veren ve Go Generics'in tanıtılmasından önce Go topluluğu arasında popüler olan boş arayüzlü Go hilesi interface{} , Mojo tipi fn işleviyle çalışmayacaktır.


Yapınız, __len__ veya __str__ gibi yaşam döngüsü yöntemlerinden en az birini uygulamalıdır; bu durumda, özellik türlerini kabul eden işlevlerle kullanılmak üzere Sized veya Stringable ile uyumlu olmasını sağlar.


Bu aslında gerçek bir sınırlama değildir ve Mojo'da çok mantıklıdır, çünkü Mojo'da dinamik yazmaya ihtiyacınız varsa, daha esnek olan def işlevine geri dönebilir ve ardından bilinmeyenlerle çalışmak için olağan Python büyüsünü uygulayabilirsiniz. türleri.


Daha katı Mojo fn işlevleri aynı zamanda DynamicVector gibi türleri kullanan genel yapılar üzerinde de çalışır; bununla ilgili daha fazlasını buradan okuyun.


Gördüğüm diğer bir sınırlama, özelliklerden ziyade yapılarla ve bunların yöntemleriyle ilgilidir ve Temiz Mimarinin uygulanmasının/kodun farklı parçalara ayrılmasının önünde potansiyel bir engeldir.


Go vs. Mojo yapı yöntemi tanımına sahip önceki örneklerden birini düşünün:


 type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") }


 @value struct Duck(Quackable): fn quack(self): print("Quack!")


Mojo örneği, Python benzeri bir söz dizimini takip ederek, yöntem tanımını doğrudan yapının içine yerleştirir; Go sürümü ise onu yapının kendisinden ayırmasına izin verir. Bu versiyonda eğer çok tür ve metod içeren çok uzun bir yapıya sahipsem, okunması biraz daha zor olacaktır.


Ancak bu kritik bir fark değil, sadece dikkat edilmesi gereken bir şey.


Mojo özellikleri, dilin ilk günlerinde olmasına rağmen zaten oldukça kullanışlıdır. Go felsefesi tamamen basitliğe odaklanmış ve özellikleri minimumda tutmaya çalışsa da, gelecekte Mojo özelliklerine daha fazla işlevsellik eklendiğini göreceğiz, bu da onları daha da güçlü hale getirecek ve tonlarca farklı kullanım senaryosunu mümkün kılacak.