2008-09-23 21 views
8

Scala'nın komut satırı repl kullanma:Yinelemeli aşırı yükleme semantik - JVM dilleri

def foo(x: Int): Unit = {} 
def foo(x: String): Unit = {println(foo(2))} 

verir

error: type mismatch; 
found: Int(2) 
required: String 

Size Repl rekursif yöntemleri aşırı tanımlamak olamaz gibi görünüyor. Bunun Scala REPL'de bir hata olduğunu düşündüm ve dosyalama yaptım, ama neredeyse "wontfix ile kapatıldı: Tercümanın semantiği göz önüne alındığında bunun desteklenebileceğini görmüyorum, çünkü bu iki yöntem derlenmeli." birlikte." Yöntemleri kapalı bir nesneye koymanızı tavsiye etti.

Nedenini açıklayabilecek bir JVM dil uygulaması veya Scala uzmanı var mı? Örneklerin birbirini çağırması, ancak bu durumda bir problem olacağını görebiliyorum.

Ya da eğer bu çok büyük bir soru ve daha fazla ön bilgiye ihtiyaç duyduğumu düşünüyorsan, birisi JVM'de, özellikle dil uygulamalarıyla ilgili kitaplara veya sitelere iyi bir bağ var mı? (John Rose'un blogu ve Programlama Dili Pragmatiği kitabı hakkında biliyorum ... ama bu konuyla ilgili.)

cevap

11

Sorun, çeviricinin çoğunlukla numaralı varolan öğelerin yerine aşırı yüklenmek yerine sahip olduğu gerçeğinden kaynaklanmaktadır.

def test(x: Int) = x + x 

sonradan Biraz ı bir farklı deneme çalıştırırken diyelim ve ben adlı başka bir yöntem oluşturun: Örneğin, sık sık sık adı verilen bir yöntem test oluşturarak, bir şey denemeye çalışan edilecektir ilk ilgisiz test:

def test(ls: List[Int]) = (0 /: ls) { _ + _ } 

Bu tamamen gerçek dışı bir senaryo değildir. Aslında, çoğu kişi çoğu zaman bunu gerçekleştirmeden çoğu zaman tercümanı nasıl kullanır. Tercüman keyfi olarak test'un her iki versiyonunu da kapsamaya karar vermişse, bu durum test kullanımında anlam çıkarıcı farklılıklara yol açabilir. Örneğin, yanlışlıkla bir Int yerine List[Int] (değil dünyanın en olası kaza) geçen test bir çağrı yapmak olabilir:

test(1 :: Nil) // => 1 
test(2)   // => 4 (expecting 2) 

Zamanla tercüman kök kapsamı inanılmaz darmadağın olacaktı Yöntemlerin, alanların, vb. çeşitli versiyonları Ben tercümanımı bir gün boyunca açık bırakmaya eğilimliydim, ancak böyle aşırı yükleme yapılmasına izin verilirse, her zaman çok kafa karıştırıcı olması gerektiği için tercümanı “yıkamak” zorunda kaldık. .

Bu, JVM veya Scala derleyicisinin bir sınırlaması değildir, bu kasıtlı bir tasarım kararıdır. Hatada belirtildiği gibi, kök kapsamından başka bir şeydeyseniz bile aşırı yüklenebilirsiniz. Test yöntemlerinizi bir sınıfa dahil etmek benim için en iyi çözüm gibi görünüyor.

+0

Mükemmel cevap Daniel, teşekkürler. Ayrıca blog'unu beğendim. :) –

4

Her iki satırı da kopyalayıp yapıştırırsanız REPL, aynı anda yapıştırır.

5
% scala28 
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> def foo(x: Int): Unit =() ; def foo(x: String): Unit = { println(foo(2)) } 
foo: (x: String)Unit <and> (x: Int)Unit 
foo: (x: String)Unit <and> (x: Int)Unit 

scala> foo(5) 

scala> foo("abc") 
() 
1

extempore's cevabı ile gösterildiği gibi, aşırı yüklenmek mümkündür. Tasarım kararı hakkında Daniel's yorum doğru, ama, sanırım, eksik ve biraz yanıltıcı.'un aşırı yüklenme işlemini yasaklayan numarası yoktur, çünkü bunlar kolayca elde edilemez.Bu yol

tasarım kararları şunlardır:

  1. Önceki tüm tanımlamalar mevcut olmalıdır.
  2. Sadece yeni girilen kodlar derlenmiş, her seferinde girilen herşeyi yeniden derlemek yerine.
  3. Tanımları yeniden tanımlamak mümkün olmalıdır (Daniel'in bahsettiği gibi).
  4. Sadece sınıflar ve nesneler değil, vals ve def'ler gibi üyeleri tanımlamak mümkün olmalıdır.

Sorun şu ki ... tüm bu hedeflere nasıl ulaşılır? Örneğini nasıl işliyoruz? 4 öğe ile başlayan

def foo(x: Int): Unit = {} 
def foo(x: String): Unit = {println(foo(2))} 

A val veya def sadece class, trait, object veya package object bir iç tanımlanabilir. Yani, REPL bu gibi nesnelerin içine tanımları koyar (gerçek değil temsilin!)

package $line1 { // input line 
    object $read { // what was read 
    object $iw { // definitions 
     def foo(x: Int): Unit = {} 
    } 
    // val res1 would be here somewhere if this was an expression 
    } 
} 

Şimdi nedeniyle JVM bunlardan birini tanımlı bir kez, nasıl çalıştığı için, bunları uzatmak olamaz. Tabii ki, herşeyi yeniden derlersin, ama biz onu reddederiz. Yani farklı bir yerde yerleştirmek gerekir:

package $line1 { // input line 
    object $read { // what was read 
    object $iw { // definitions 
     def foo(x: String): Unit = { println(foo(2)) } 
    } 
    } 
} 

Ve bu senin örnekleri aşırı yükler değildir açıklıyor: bunlar iki farklı yerde tanımlanır. Eğer onları aynı çizgiye koyarsanız, hepsi birlikte tanımlanacaklardır, bu da onları örnek teşkil eden örnekte gösterildiği gibi aşırı yüklemeler yapar.

Diğer tasarım kararlarına gelince, her yeni paket ithalat tanımları ve önceki paketlerden "res", ve ithalatlar birbirini gölgeleyebilir, bu da malzemelerin "yeniden tanımlanmasını" mümkün kılar.