Evet, yüzeyde çok basit bir soru. Ama bunu sonuna kadar düşünmek için zaman ayırırsanız, ölçülemez tip teorisinin derinliklerine girersiniz. Ve tür teorisi de size bakıyor.
İlk olarak, elbette, F # 'ın tip sınıfları olmadığını doğru bir şekilde anladınız ve bu yüzden. Fakat bir arayüz öneriyorsunuz Mappable
. Tamam, buna bakalım.
Diyelim ki böyle bir arayüz beyan edebiliriz. İmzasının nasıl olacağını hayal edebiliyor musunuz?
type Mappable =
abstract member map : ('a -> 'b) -> 'f<'a> -> 'f<'b>
f
Arayüzü uygulayan tip nerede . Bekle! F # da buna sahip değil! İşte f
daha yüksek türlü bir değişken ve F # daha yüksek türlüğe sahip değil. Bir işlevi f : 'm<'a> -> 'm<'b>
veya bunun gibi bir şeyi bildirmenin bir yolu yoktur .
Ama tamam, diyelim ki bu engeli aştık. Ve şimdi bir arayüze sahip Mappable
tarafından uygulanabilir List
, Array
, Seq
ve mutfak lavabo. Fakat bekle! Şimdi bir fonksiyon yerine bir metodumuz var ve metodlar iyi oluşturmuyor! Yuvalanmış bir listenin her öğesine 42 eklemeye bakalım:
// Good ol' functions:
add42 nestedList = nestedList |> List.map (List.map ((+) 42))
// Using an interface:
add42 nestedList = nestedList.map (fun l -> l.map ((+) 42))
Bakın: Şimdi bir lambda ifadesi kullanmalıyız! Bu .map
uygulamayı bir değer olarak başka bir işleve geçirmenin bir yolu yoktur . Etkili bir şekilde "değerler olarak işlevler" in sonu (ve evet, biliyorum, bir lambda kullanmak bu örnekte çok kötü görünmüyor, ama bana güven, çok çirkinleşiyor)
Ama bekle, hala işimiz bitmedi. Artık bir yöntem çağrısı olduğuna göre, tür çıkarım işe yaramıyor! .NET yönteminin tür imzası nesnenin türüne bağlı olduğundan, derleyicinin her ikisini de çıkarması için bir yol yoktur. Bu aslında yeni başlayanlar .NET kitaplıklarıyla birlikte çalışırken çok sık karşılaşılan bir sorundur. Ve tek tedavi bir tür imza sağlamaktır:
add42 (nestedList : #Mappable) = nestedList.map (fun l -> l.map ((+) 42))
Oh, ama bu hala yeterli değil! nestedList
Kendisi için bir imza vermiş olmama rağmen, lambda'nın parametresi için bir imza vermedim l
. Böyle bir imza ne olmalı? Olması gerektiğini söyleyebilir misiniz fun (l: #Mappable) -> ...
? Oh, ve şimdi nihayet sıralamak-N türleri, gördüğünüz gibi, " böyle #Mappable
herhangi bir tür" için bir kısayol - yani kendisi genel bir lambda ifade.'a
'a :> Mappable
Ya da alternatif olarak, daha yüksek nezakete geri dönebilir ve nestedList
daha kesin bir şekilde beyan edebiliriz :
add42 (nestedList : 'f<'a<'b>> where 'f :> Mappable, 'a :> Mappable) = ...
Ama tamam, şimdilik tür çıkarımını bir kenara bırakalım ve lambda ifadesine ve şimdi map
başka bir işleve değer olarak nasıl geçemeyeceğimize geri dönelim . Diyelim ki söz dizimini Elm'in kayıt alanlarıyla yaptığı gibi bir şeye izin vermek için biraz genişletiyoruz:
add42 nestedList = nestedList.map (.map ((+) 42))
Ne tür olurdu .map
? Haskell'de olduğu gibi kısıtlı bir tip olmalı !
.map : Mappable 'f => ('a -> 'b) -> 'f<'a> -> 'f<'b>
Vay canına tamam. .NET'in bu türlerin var olmasına bile izin vermediğini bir kenara bırakarak, etkili bir şekilde geri tip sınıfları aldık!
Ancak F # 'ın ilk başta tip sınıfları olmamasının bir nedeni vardır. Bu nedenin birçok yönü yukarıda açıklanmıştır, ancak bunu daha açık bir şekilde ifade etmek gerekirse: basitlik .
Gördüğünüz gibi, bu bir iplik yumağı. Yazım sınıflarına sahip olduğunuzda, kısıtlamalar, daha yüksek nezaket, rütbe-N (veya en azından rütbe-2) olması gerekir ve bunu bilmeden önce, kestirimci türler, tür işlevleri, GADT'ler ve tüm geri kalanı.
Ama Haskell bütün güzellikler için bir bedel ödüyor. Tüm bu şeyleri çıkarmanın iyi bir yolu olmadığı ortaya çıkıyor . Daha yüksek tür tipleri sorta çalışır, ancak kısıtlamalar zaten yoktur. Rütbe-N - hayal bile etme. Ve işe yaradığında bile, anlamanız gereken bir doktora sahip olmanız gereken tür hataları alırsınız. Bu yüzden Haskell'de her şeye nazikçe imza atmanız önerilir . Şey, her şey değil -her şey , ama gerçekten neredeyse her şey. Ve tip imzaları (örneğin içeride let
ve where
) koymadığınızda - sürpriz-sürpriz, bu yerler aslında monomorfize edilmiştir, bu yüzden esasen basit F # alanına geri dönersiniz.
Öte yandan, F # 'da, tür imzalar çoğunlukla belgeler veya .NET birlikte çalışma için nadirdir. Bu iki durumun dışında, F # içine büyük bir karmaşık program yazabilir ve bir kez tip imzası kullanamazsınız. Tür çıkarımı iyi çalışır, çünkü başa çıkması için çok karmaşık veya belirsiz bir şey yoktur.
Ve bu F #'nın Haskell'e göre büyük avantajı. Evet, Haskell süper karmaşık şeyleri çok hassas bir şekilde ifade etmenize izin veriyor, bu iyi. Ancak F #, neredeyse Python veya Ruby gibi çok istekli olmanıza izin verir ve eğer yanılırsanız derleyicinin sizi yakalamasını sağlar.