Scala derleyicisi neden varsayılan bağımsız değişkenlerle aşırı yüklenmiş yöntemlere izin vermiyor?


151

Bu tür yöntem aşırı yüklemelerinin belirsiz hale gelebileceği geçerli durumlar olabilirken, derleyici neden derleme zamanında ve çalışma zamanında belirsiz olmayan koda izin vermiyor?

Misal:

// This fails:
def foo(a: String)(b: Int = 42) = a + b
def foo(a: Int)   (b: Int = 42) = a + b

// This fails, too. Even if there is no position in the argument list,
// where the types are the same.
def foo(a: Int)   (b: Int = 42) = a + b
def foo(a: String)(b: String = "Foo") = a + b

// This is OK:
def foo(a: String)(b: Int) = a + b
def foo(a: Int)   (b: Int = 42) = a + b    

// Even this is OK.
def foo(a: Int)(b: Int) = a + b
def foo(a: Int)(b: String = "Foo") = a + b

val bar = foo(42)_ // This complains obviously ...

Bu kısıtlamaların biraz gevşetilememesinin herhangi bir nedeni var mı?

Özellikle aşırı derecede aşırı yüklenmiş Java kodunu Scala varsayılan argümanlarına dönüştürmek çok önemlidir ve birçok Java yöntemini tek bir Scala yöntemiyle değiştirdikten sonra spec / derleyicinin keyfi kısıtlamalar getirdiğini öğrenmek hoş değildir.


18
"keyfi kısıtlamalar" :-)
KajMagnus

1
Tür bağımsız değişkenlerini kullanarak sorunu çözebileceğiniz görülüyor. Bu derleme:object Test { def a[A](b: Int, c: Int, d: Int = 7): Unit = {}; def a[A](a:String, b: String = ""): Unit = {}; a(2,3,4); a("a");}
user1609012

@ user1609012: Numaranız benim için işe yaramadı. Scala 2.12.0 ve Scala 2.11.8 kullanarak denedim.
Kara ile çevrili sörfçü

4
IMHO Bu, Scala'daki en güçlü acı noktalarından biridir. Ne zaman esnek bir API sağlamaya çalışsam, bu sorunla sık sık karşılaşıyorum, özellikle de tamamlayıcı nesnenin apply () öğesini aşırı yüklerken. Scala'yı Kotlin'e biraz tercih etmeme rağmen, Kotlin'de bu tür bir aşırı yükleme yapabilirsiniz ...
cubic lettuce

Bununla ilgili kayıt bileti github.com/scala/bug/issues/8161
Seth Tisue

Yanıtlar:


115

Lukas Rytz'dan alıntı yapmak istiyorum ( buradan ):

Bunun nedeni, varsayılan argümanları döndüren üretilen yöntemler için deterministik bir adlandırma şeması istememizdir. Eğer yazarsan

def f(a: Int = 1)

derleyici oluşturur

def f$default$1 = 1

Aynı parametre konumunda varsayılanlara sahip iki aşırı yükünüz varsa, farklı bir adlandırma şemasına ihtiyacımız olacaktır. Ancak, üretilen bayt kodunu birden çok derleyici çalışması boyunca sabit tutmak istiyoruz.

Gelecekteki Scala sürümü için bir çözüm, varsayılan olmayan argümanların tür adlarını (aşırı yüklenmiş sürümleri belirsizleştiren bir yöntemin başındakiler) adlandırma şemasına dahil etmek olabilir, örneğin bu durumda:

def foo(a: String)(b: Int = 42) = a + b
def foo(a: Int)   (b: Int = 42) = a + b

şöyle bir şey olurdu:

def foo$String$default$2 = 42
def foo$Int$default$2 = 42

Bir SIP teklifi yazmak isteyen biri ?


2
Buradaki önerinizin çok mantıklı olduğunu düşünüyorum ve bunu belirlemek / uygulamakla ilgili neyin bu kadar karmaşık olacağını anlamıyorum. Esasen, parametre türleri işlevin kimliğinin bir parçasıdır. Derleyici şu anda foo (String) ve foo (Int) ile ne yapıyor (yani, varsayılan OLMADAN aşırı yüklenmiş yöntemler)?
Mark

Bu, Java'dan Scala yöntemlerine erişirken zorunlu Macar Notasyonunu etkili bir şekilde getirmez mi? Arayüzleri son derece kırılgan hale getirecek gibi görünüyor, bu da kullanıcıyı tip parametrelerinden fonksiyonlara değiştiğinde dikkatli olmaya zorluyor.
blast_hardcheese

Ayrıca, karmaşık türler ne olacak? A with B, Örneğin?
blast_hardcheese

68

Varsayılan argümanlarla aşırı yükleme çözümünün etkileşimleri için okunabilir ve kesin bir spesifikasyon elde etmek çok zor olurdu. Elbette, burada sunulan gibi birçok münferit vaka için ne olması gerektiğini söylemek kolaydır. Ama bu yeterli değil. Olası tüm köşe durumlarına karar veren bir spesifikasyona ihtiyacımız var. Aşırı yükleme çözünürlüğünü belirlemek zaten çok zor. Karışıma varsayılan bağımsız değişkenler eklemek, işi daha da zorlaştıracaktır. Bu yüzden ikisini ayırmayı seçtik.


4
Cevabınız için teşekkürler. Muhtemelen kafamı karıştıran şey, temelde başka her yerde derleyicinin yalnızca gerçekten bir belirsizlik varsa şikayet etmesiydi. Ancak burada derleyici şikayet ediyor çünkü belirsizliğin ortaya çıkabileceği benzer durumlar olabilir. Dolayısıyla, ilk durumda derleyici yalnızca kanıtlanmış bir sorun varsa şikayet eder, ancak ikinci durumda derleyici davranışı çok daha az hassastır ve "görünüşte geçerli" kod için hataları tetikler. Bunu en az şaşkınlık ilkesiyle görmek, biraz talihsizlik.
soc

2
"Okunabilir ve kesin bir spesifikasyon [...] elde etmek çok zor olurdu", birisi iyi bir spesifikasyon ve / veya uygulama ile adım atarsa ​​mevcut durumun iyileştirilebileceği gerçek bir şans olduğu anlamına mı geliyor? Mevcut durum imho, adlandırılmış / varsayılan parametrelerin kullanılabilirliğini oldukça sınırlıyor ...
soc

Spesifikasyonda değişiklik önermek için bir süreç var. scala-lang.org/node/233
James Iry

2
Scala'nın aşırı yüklemeyi göz ardı etmesi ve ikinci sınıf bir vatandaşla ilgili bazı yorumlarım var (bağlantılı cevabın altındaki yorumlarıma bakın). Scala'da aşırı yüklemeyi kasıtlı olarak zayıflatmaya devam edersek, yazmayı IMO'nun gerileyici bir yön olduğu isimlerle değiştiriyoruz.
Shelby Moore III

10
Python yapabiliyorsa, Scala'nın yapamaması için iyi bir neden göremiyorum. Karmaşıklık argümanı iyi bir argümandır: Bu özelliğin uygulanması, Ölçeği kullanıcının bakış açısından daha az karmaşık hale getirecektir. Diğer cevapları okuduğunuzda, insanların, sadece kullanıcıların bakış açısından var olmaması gereken bir sorunu çözmek için çok karmaşık şeyler icat ettiğini göreceksiniz.
Richard Gomes

12

Sorunuza cevap veremem ama işte bir çözüm:

implicit def left2Either[A,B](a:A):Either[A,B] = Left(a)
implicit def right2Either[A,B](b:B):Either[A,B] = Right(b)

def foo(a: Either[Int, String], b: Int = 42) = a match {
  case Left(i) => i + b
  case Right(s) => s + b
}

Yalnızca bir argümanla farklılık gösteren çok uzun iki argüman listeniz varsa, bu zahmete değer olabilir ...


1
Kodumu daha kısa ve okunabilir hale getirmek için varsayılan argümanları kullanmaya çalıştım ... aslında, alternatif türü kabul edilen türe dönüştüren bir durumda sınıfa örtük bir dönüşüm ekledim. Sadece çirkin hissettiriyor. Ve varsayılan argümanlar ile yaklaşım sadece işe yaramalı!
soc

Onlar tüm kullanımları için başvuruda beri, bu tür dönüşüm ile dikkatli olmalıdır Eithersadece değil, foobir zaman bu şekilde, - Either[A, B]değeri istenen, hem Ave Bkabul edilir. Bunun yerine foo, bu yönde gitmek istiyorsanız , yalnızca varsayılan argümanlar ( burada olduğu gibi ) olan işlevler tarafından kabul edilen bir tür tanımlanmalıdır ; Tabii ki, bunun uygun bir çözüm olup olmadığı daha da netleşiyor.
Blaisorblade

9

Benim için işe yarayan şey, aşırı yükleme yöntemlerini yeniden tanımlamak (Java tarzı).

def foo(a: Int, b: Int) = a + b
def foo(a: Int, b: String) = a + b
def foo(a: Int) = a + "42"
def foo(a: String) = a + "42"

Bu, derleyiciye mevcut parametrelere göre istediğiniz çözünürlüğü sağlar.


3

İşte @ Landi cevabının bir genellemesi:

Gerçekten ne istiyorsun:

def pretty(tree: Tree, showFields: Boolean = false): String = // ...
def pretty(tree: List[Tree], showFields: Boolean = false): String = // ...
def pretty(tree: Option[Tree], showFields: Boolean = false): String = // ...

Çalışma alanı

def pretty(input: CanPretty, showFields: Boolean = false): String = {
  input match {
    case TreeCanPretty(tree)       => prettyTree(tree, showFields)
    case ListTreeCanPretty(tree)   => prettyList(tree, showFields)
    case OptionTreeCanPretty(tree) => prettyOption(tree, showFields)
  }
}

sealed trait CanPretty
case class TreeCanPretty(tree: Tree) extends CanPretty
case class ListTreeCanPretty(tree: List[Tree]) extends CanPretty
case class OptionTreeCanPretty(tree: Option[Tree]) extends CanPretty

import scala.language.implicitConversions
implicit def treeCanPretty(tree: Tree): CanPretty = TreeCanPretty(tree)
implicit def listTreeCanPretty(tree: List[Tree]): CanPretty = ListTreeCanPretty(tree)
implicit def optionTreeCanPretty(tree: Option[Tree]): CanPretty = OptionTreeCanPretty(tree)

private def prettyTree(tree: Tree, showFields: Boolean): String = "fun ..."
private def prettyList(tree: List[Tree], showFields: Boolean): String = "fun ..."
private def prettyOption(tree: Option[Tree], showFields: Boolean): String = "fun ..."

1

Olası senaryolardan biri


  def foo(a: Int)(b: Int = 10)(c: String = "10") = a + b + c
  def foo(a: Int)(b: String = "10")(c: Int = 10) = a + b + c

Derleyicinin hangisini arayacağı konusunda kafası karışacaktır. Diğer olası tehlikelerin önlenmesinde, derleyici en fazla bir aşırı yüklenmiş yöntemin varsayılan argümanlara sahip olmasına izin verir.

Sadece benim tahminim :-)


0

Anladığım kadarıyla, derlenmiş sınıflarda varsayılan argüman değerlerine sahip ad çakışmaları olabilir. Bu satırlar boyunca birkaç başlıkta bahsedilen bir şey gördüm.

Adlandırılmış bağımsız değişken özelliği burada: http://www.scala-lang.org/sites/default/files/sids/rytz/Mon,%202009-11-09,%2017:29/named-args.pdf

Belirtir:

 Overloading If there are multiple overloaded alternatives of a method, at most one is
 allowed to specify default arguments.

Yani, şimdilik hiçbir şekilde işe yaramayacak.

Java'da yapabileceğiniz gibi bir şey yapabilirsiniz, örneğin:

def foo(a: String)(b: Int) =  a + (if (b > 0) b else 42)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.