Statik olarak yazılmış tam bir Lisp varyantı mümkün müdür? Böyle bir şeyin var olması mantıklı mı? Bir Lisp dilinin erdemlerinden birinin, tanımının basitliği olduğuna inanıyorum. Statik yazım bu temel prensibi tehlikeye atar mı?
Statik olarak yazılmış tam bir Lisp varyantı mümkün müdür? Böyle bir şeyin var olması mantıklı mı? Bir Lisp dilinin erdemlerinden birinin, tanımının basitliği olduğuna inanıyorum. Statik yazım bu temel prensibi tehlikeye atar mı?
Yanıtlar:
Evet, çok olasıdır, ancak standart HM tarzı bir sistem genellikle çoğu deyimsel Lisp / Scheme kodu için yanlış seçimdir. Statik yazımla "Tam Lisp" (aslında Scheme gibi) olan yeni bir dil için Typed Racket'e bakın .
Sexpr
.
coerce :: a->b
eval açısından yazabilirim . Tip güvenliği nerede?
eval
, ortaya çıkan şeyi görmek için sonucu test etmeniz gerekir, bu da Typed Racked'de yeni bir şey değildir ( String
ve birleşim türünü alan bir işlevle aynı anlaşma Number
). Örtülü bir yolu bu olduğunu görmek için olabilir yapılabilir o gerçektir olabilir yazıp bir HM-statik-daktilo dilde bir dinamik olarak yazılmış bir dil kullanırlar.
İstediğiniz tek şey, Lisp'e benzeyen statik olarak yazılmış bir dil olsaydı , dilinizi temsil eden soyut bir sözdizimi ağacı tanımlayarak ve ardından bu AST'yi S-ifadelerine eşleyerek bunu oldukça kolay bir şekilde yapabilirsiniz. Ancak sonuca Lisp diyeceğimi sanmıyorum.
Sözdiziminin yanı sıra aslında Lisp-y özelliklerine sahip bir şey istiyorsanız, bunu statik olarak yazılmış bir dille yapmak mümkündür. Bununla birlikte, Lisp'in çok kullanışlı statik yazmayı çıkarması zor olan birçok özelliği vardır. Göstermek için , Lisp'in birincil yapı taşını oluşturan eksiler adı verilen liste yapısının kendisine bir göz atalım .
Eksileri bir liste olarak adlandırmak, bir liste (1 2 3)
gibi görünse de, biraz yanlış bir isim. Örneğin, C ++ 'ın std::list
veya Haskell'in listesi gibi statik olarak yazılmış bir listeyle hiç karşılaştırılamaz . Bunlar, tüm hücrelerin aynı türden olduğu tek boyutlu bağlantılı listelerdir. Lisp mutlu bir şekilde izin verir (1 "abc" #\d 'foo)
. Ayrıca, statik tipli listelerinizi liste listelerini kapsayacak şekilde genişletseniz bile, bu nesnelerin türü listedeki her öğenin bir alt liste olmasını gerektirir . Nasıl temsil edersin((1 2) 3 4)
Onlarda ?
Lisp, yaprakları (atomları) ve dalları (eksileri) olan ikili bir ağaç oluşturur. Dahası, böyle bir ağacın yaprakları herhangi bir atomik (eksersiz) Lisp türü içerebilir! Bu yapının esnekliği, Lisp'i sembolik hesaplamayı, AST'leri işlemede ve Lisp kodunu dönüştürmede bu kadar iyi yapan şeydir!
Öyleyse, statik olarak yazılmış bir dilde böyle bir yapıyı nasıl modellersiniz? Son derece güçlü ve hassas bir statik tip sistemine sahip olan Haskell'de deneyelim:
type Symbol = String
data Atom = ASymbol Symbol | AInt Int | AString String | Nil
data Cons = CCons Cons Cons
| CAtom Atom
İlk probleminiz Atom tipinin kapsamı olacak. Açıkçası, etrafta dolanmak istediğimiz tüm nesne türlerini kapsayacak yeterli esnekliğe sahip bir Atom türü seçmedik. Yukarıda listelendiği gibi Atom veri yapısını genişletmeye çalışmak yerine (ki bu kırılgandır), diyelim ki Atomic
atomik yapmak istediğimiz tüm türleri ayırt eden sihirli bir tip sınıfımız var. Sonra deneyebiliriz:
class Atomic a where ?????
data Atomic a => Cons a = CCons Cons Cons
| CAtom a
Ancak bu işe yaramayacak çünkü ağaçtaki tüm atomların aynı türde olmasını gerektiriyor . Yapraktan yaprağa farklılık gösterebilmelerini istiyoruz. Daha iyi bir yaklaşım, Haskell'in varoluşsal niceleyicilerinin kullanılmasını gerektirir :
class Atomic a where ?????
data Cons = CCons Cons Cons
| forall a. Atomic a => CAtom a
Ama şimdi meselenin özüne geldiniz. Bu tür bir yapıdaki atomlarla ne yapabilirsiniz? Modellenebilecek ortak yapıları nelerdir Atomic a
? Böyle bir türle hangi seviyede güvenlik garanti ediliyor? Tür sınıfımıza herhangi bir işlev eklemediğimizi ve bunun iyi bir nedeni olduğunu unutmayın: Atomlar Lisp'te ortak hiçbir şeyi paylaşmaz. Lisp'teki süper türleri basitçe t
(yani üst) olarak adlandırılır .
Bunları kullanmak için, bir atomun değerini gerçekten kullanabileceğiniz bir şeye dinamik olarak zorlamak için mekanizmalar bulmanız gerekir. Ve bu noktada, statik olarak yazılmış dilinizde temelde dinamik olarak yazılmış bir alt sistem uyguladınız! (Biri yardım edemez, ancak Greenspun'un Onuncu Programlama Kuralı'nın olası bir sonucuna dikkat edin .)
Haskell böyle bir destek sağladığını Not dinamik alt sistem bir ile Obj
bir birlikte kullanılan türü, Dynamic
tipi ve bir tiplenemeyen sınıfa yerine bizimAtomic
, keyfi değerlerin türleriyle birlikte depolanmasına ve bu türlerden açık zorlamaya izin verir. Bu, Lisp eksper yapılarıyla tam genel olarak çalışmak için kullanmanız gereken türden bir sistemdir.
Ayrıca yapabileceğiniz şey, diğer tarafa gitmek ve statik olarak yazılmış bir alt sistemi temelde dinamik olarak yazılmış bir dil içine yerleştirmektir. Bu, programınızın daha katı tür gereksinimlerinden yararlanabilecek bölümleri için statik tür denetimi avantajına olanak tanır. Bu , örneğin CMUCL'nin sınırlı hassas tip denetimi biçiminde alınan yaklaşım gibi görünüyor .
Son olarak, ikisi arasındaki geçişte gezinmeye yardımcı olmak için sözleşme tarzı programlama kullanan, dinamik ve statik olarak yazılmış iki ayrı alt sisteme sahip olma olasılığı vardır. Bu şekilde, dil, statik tip kontrolünün bir yardımdan çok bir engel teşkil edeceği Lisp kullanımlarının yanı sıra statik tip kontrolünün avantajlı olacağı kullanımları da barındırabilir. Aşağıdaki yorumlardan da göreceğiniz gibi, Typed Racket tarafından alınan yaklaşım budur.
(Listof Integer)
ve gibi şeyler dahil (Listof Any)
. Açıkçası, tür hakkında hiçbir şey bilmediğiniz için ikincisinin yararsız olduğundan şüphelenirsiniz, ancak TR'de daha sonra kullanabilirsiniz (if (integer? x) ...)
ve sistem x
bunun 1. dalda bir Tamsayı olduğunu bilecektir .
dynamic
statik dillerde türler, dinamik olarak yazılmış dillerin bazı faydalarını elde etmek için bir tür geçici çözüm olarak popüler hale geliyor ve bu değerlerin olağan değiş tokuşu, türleri tanımlanabilir hale getirecek şekilde sarılıyor. Ancak burada da tiplenmiş raket, onu dil içinde uygun hale getirmede çok iyi bir iş çıkarıyor - tür denetleyicisi, türler hakkında daha fazla bilgi edinmek için yüklemlerin oluşumlarını kullanır. Örneğin, raket sayfasındaki yazılı örneğe bakın ve string?
bir dizi ve sayı listesini bir dizi dizisine nasıl "indirgediğini" görün.
Cevabım, yüksek derecede güven olmadan muhtemelen . Örneğin SML gibi bir dile bakarsanız ve onu Lisp ile karşılaştırırsanız, her birinin işlevsel çekirdeği neredeyse aynıdır. Sonuç olarak, Lisp'in çekirdeğine (işlev uygulaması ve ilkel değerler) bir tür statik yazım uygularken çok fazla sorun yaşamayacaksınız.
Sorunuz yine de tam olarak söylüyor ve bazı problemlerin geldiğini gördüğüm yer veri olarak kod yaklaşımı. Türler, ifadelerden daha soyut bir düzeyde bulunur. Lisp'in bu ayrımı yoktur - her şeyin yapısı "düzdür". E: T'nin bir ifadesini (burada T, türünün bir temsilidir) düşünürsek ve bu ifadeyi düz veri olarak düşünürsek, o zaman burada T türü tam olarak nedir? Bu bir tür! Bir tür daha yüksek, sipariş türüdür, bu yüzden devam edip kodumuzda bununla ilgili bir şeyler söyleyelim:
E : T :: K
Bununla nereye gittiğimi görebilirsiniz. Eminim tür bilgisini koddan ayırarak, türlerin bu tür kendi kendine referanslarından kaçınmak mümkün olabilirdi, ancak bu türlerin lezzetlerinde çok "lisp" olmamasına neden olur. Muhtemelen bunun üstesinden gelmenin birçok yolu var, ancak hangisinin en iyisi olacağı bana açık değil.
DÜZENLEME: Oh, biraz googling ile, statik olarak yazılması dışında Lisp'e çok benzeyen Qi'yi buldum . Belki de, oraya statik yazmayı sağlamak için nerede değişiklik yaptıklarını görmeye başlamak için iyi bir yerdir.