स्केल कंपाइलर डिफॉल्ट तर्कों के साथ ओवरलोडेड तरीकों को अस्वीकार क्यों करता है?


148

हालांकि ऐसे वैध मामले हो सकते हैं जहां इस तरह की विधि अधिभार अस्पष्ट हो सकता है, संकलक कोड क्यों नहीं हटाता है जो न तो संकलन के समय में अस्पष्ट है और न ही रन समय पर?

उदाहरण:

// 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 ...

क्या कोई कारण है कि इन प्रतिबंधों को थोड़ा ढीला नहीं किया जा सकता है?

विशेष रूप से जब अत्यधिक ओवरलोडेड जावा कोड को स्काला डिफॉल्ट तर्कों में परिवर्तित करना बहुत महत्वपूर्ण होता है और जावा के बहुत सारे तरीकों को एक स्काला विधियों द्वारा बदलने के बाद यह पता लगाना अच्छा नहीं होता है कि ऐनक / कंपाइलर मनमाना प्रतिबंध लगाता है।


18
"मनमाना प्रतिबंध" :-)
काजाग्नस

1
ऐसा लगता है कि आप इस प्रकार के तर्कों का उपयोग करके समस्या को हल कर सकते हैं। यह संकलन: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: आपकी चाल मेरे काम नहीं आई। मैंने इसे Scala 2.12.0 और Scala 2.11.8 का उपयोग करके आज़माया।
लैंडल सर्फर

4
IMHO यह स्काला में सबसे मजबूत दर्द-बिंदुओं में से एक है। जब भी मैं एक लचीली एपीआई प्रदान करने की कोशिश करता हूं, तो मैं अक्सर इस मुद्दे पर दौड़ता हूं, विशेष रूप से जब साथी ऑब्जेक्ट के लागू होने () को ओवरलोड करते हैं। हालाँकि मैं कोटलिन के ऊपर स्काला को थोड़ा पसंद करता हूं, कोटलिन में आप इस तरह का ओवरलोडिंग कर सकते हैं ...
क्यूबिक लेटस

जवाबों:


113

मैं लुकास रिट्ज़ ( यहाँ से ) का हवाला देना चाहूंगा :

कारण यह है कि हम उत्पन्न विधियों के लिए एक नियतात्मक नामकरण-योजना चाहते थे जो डिफ़ॉल्ट तर्क लौटाते हैं। अगर आप लिखेंगे

def f(a: Int = 1)

संकलक उत्पन्न करता है

def f$default$1 = 1

यदि आपके पास एक ही पैरामीटर स्थिति पर चूक के साथ दो ओवरलोड हैं, तो हमें एक अलग नामकरण योजना की आवश्यकता होगी। लेकिन हम उत्पन्न बाइट-कोड को कई कंपाइलर रनों पर स्थिर रखना चाहते हैं।

भविष्य के स्काला संस्करण का एक समाधान गैर-डिफ़ॉल्ट तर्कों के प्रकार के नामों को शामिल किया जा सकता है (एक विधि की शुरुआत में, जो नामांकित स्कीमा में अतिभारित संस्करणों की अवहेलना करते हैं, उदाहरण के लिए इस मामले में:

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

यह कुछ इस तरह होगा:

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

कोई SIP प्रस्ताव लिखने को तैयार है ?


2
मुझे लगता है कि यहां आपका प्रस्ताव बहुत मायने रखता है, और मैं यह नहीं देखता कि इसे निर्दिष्ट / कार्यान्वित करने के बारे में इतना जटिल क्या होगा। अनिवार्य रूप से, पैरामीटर प्रकार फ़ंक्शन की आईडी का हिस्सा हैं। वर्तमान में कंपाइलर foo (String) और foo (Int) के साथ क्या करता है (यानी, डिफ़ॉल्ट के बिना ओवरलोड तरीके)?
मार्क

जावा से स्केल विधियों तक पहुँचने पर यह अनिवार्य रूप से अनिवार्य हंगेरियन संकेतन का परिचय नहीं देगा? ऐसा लगता है कि यह इंटरफेस को बेहद नाजुक बना देगा, जिससे उपयोगकर्ता को ध्यान रखने के लिए मजबूर किया जा सकता है जब प्रकार पैरामीटर फ़ंक्शन में बदल जाते हैं।
ब्लास्ट_हार्डीज

इसके अलावा, जटिल प्रकारों के बारे में क्या? A with B, उदाहरण के लिए?
ब्लास्ट_हार्डीज

66

डिफ़ॉल्ट तर्कों के साथ ओवरलोडिंग रिज़ॉल्यूशन के इंटरैक्शन के लिए एक पठनीय और सटीक कल्पना प्राप्त करना बहुत कठिन होगा। बेशक, कई व्यक्तिगत मामलों के लिए, यहां प्रस्तुत एक की तरह, यह कहना आसान है कि क्या होना चाहिए। पर यह पर्याप्त नहीं है। हमें एक युक्ति की आवश्यकता होगी जो सभी संभव कोने के मामलों को तय करती है। ओवरलोडिंग रिज़ॉल्यूशन निर्दिष्ट करने के लिए पहले से ही बहुत कठिन है। मिश्रण में डिफ़ॉल्ट तर्क जोड़ने से यह अभी भी कठिन हो जाएगा। इसलिए हमने दोनों को अलग करने का विकल्प चुना है।


4
आपके उत्तर के लिए धन्यवाद। जो बात शायद मुझे भ्रमित करती थी वह यह कि मूल रूप से हर जगह कंपाइलर केवल शिकायत करता है अगर वास्तव में कुछ अस्पष्टता है। लेकिन यहाँ संकलक शिकायत करता है क्योंकि ऐसे ही मामले हो सकते हैं जहाँ अस्पष्टता उत्पन्न हो सकती है। इसलिए पहले मामले में कंपाइलर केवल तभी शिकायत करता है जब कोई सिद्ध समस्या होती है, लेकिन दूसरे मामले में कंपाइलर का व्यवहार बहुत कम सटीक होता है और "उचित रूप से मान्य" कोड के लिए त्रुटियों को ट्रिगर करता है। इसे कम से कम विस्मय के सिद्धांत के साथ देखना, यह थोड़ा दुर्भाग्यपूर्ण है।
सामाजिक

2
क्या "एक पठनीय और सटीक युक्ति प्राप्त करना बहुत कठिन होगा [...]" का अर्थ है कि एक वास्तविक मौका है कि वर्तमान स्थिति में सुधार किया जा सकता है यदि कोई व्यक्ति एक अच्छे विनिर्देशन और / या कार्यान्वयन के साथ कदम रखता है? वर्तमान स्थिति इम्हो नामांकित / डिफ़ॉल्ट मापदंडों की प्रयोज्यता को थोड़ा सीमित करती है ...
सामाजिक

कल्पना में परिवर्तन के प्रस्ताव के लिए एक प्रक्रिया है। scala-lang.org/node/233
जेम्स इरी

2
मुझे कुछ टिप्पणियां हैं (स्कैलाॅड के नीचे अपनी टिप्पणी देखें) स्कैला ओवरलोडिंग के बारे में और दूसरे दर्जे के नागरिक के रूप में। यदि हम स्केल में जानबूझकर ओवरलोडिंग को कम करना जारी रखते हैं, तो हम नामों के साथ टाइपिंग की जगह ले रहे हैं, जो आईएमओ एक प्रतिगामी दिशा है।
शेल्बी मूर III

10
यदि पायथन कर सकता है, तो मुझे कोई अच्छा कारण नहीं दिखता है कि स्काला क्यों नहीं कर सकता है। जटिलता के लिए तर्क एक अच्छा है: इस सुविधा को लागू करने से उपयोगकर्ता के दृष्टिकोण से स्केल कम जटिल हो जाएगा। अन्य उत्तर पढ़ें और आप देखेंगे कि लोग बहुत जटिल चीजों का आविष्कार कर रहे हैं एक समस्या को हल करने के लिए जो उपयोगकर्ताओं के दृष्टिकोण से भी मौजूद नहीं होनी चाहिए।
रिचर्ड गोम्स

12

मैं आपके प्रश्न का उत्तर नहीं दे सकता, लेकिन यहाँ एक समाधान है:

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
}

यदि आपके पास दो बहुत लंबी arg सूचियां हैं जो केवल एक arg में भिन्न हैं, तो यह परेशानी के लायक हो सकता है ...


1
खैर, मैंने अपने कोड को अधिक संक्षिप्त और पठनीय बनाने के लिए डिफ़ॉल्ट तर्कों का उपयोग करने की कोशिश की ... वास्तव में मैंने एक मामले में कक्षा में एक अंतर्निहित रूपांतरण जोड़ा है जो वैकल्पिक प्रकार को स्वीकार किए गए प्रकार में परिवर्तित करता है। यह सिर्फ बदसूरत लगता है। और डिफ़ॉल्ट args के साथ दृष्टिकोण बस काम करना चाहिए!
सामाजिक

तुम्हें पता है, इस तरह के रूपांतरण के साथ सावधान रहना चाहिए क्योंकि वे के सभी उपयोगों के लिए आवेदन Eitherऔर नहीं बस के लिए foo- इस तरह, जब भी एक Either[A, B]मूल्य अनुरोध किया जाता है, दोनों Aऔर Bस्वीकार किए जाते हैं। एक को एक प्रकार के बजाय परिभाषित करना चाहिए जो केवल डिफ़ॉल्ट तर्कों वाले कार्यों द्वारा स्वीकार किया जाता है (जैसे fooयहां), यदि आप इस दिशा में जाना चाहते हैं; बेशक, यह और भी कम स्पष्ट हो जाता है कि क्या यह एक सुविधाजनक समाधान है।

9

मेरे लिए जो काम किया है वह ओवरलोडिंग के तरीकों (जावा-शैली) को फिर से परिभाषित करने के लिए है।

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"

यह संकलक सुनिश्चित करता है कि आप वर्तमान मापदंडों के अनुसार क्या संकल्प चाहते हैं।


3

यहाँ @Landei उत्तर का सामान्यीकरण है:

आप वास्तव में क्या चाहते हैं:

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 = // ...

Workarround

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

संभावित परिदृश्य में से एक है


  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

कंपाइलर भ्रमित हो जाएगा कि किसको कॉल करना है। अन्य संभावित खतरों को रोकने के लिए, कंपाइलर एक से अधिक ओवरलोड विधि की अनुमति देगा जिसमें डिफ़ॉल्ट तर्क हैं।

बस मेरा अनुमान :-)


0

मेरी समझ यह है कि डिफ़ॉल्ट तर्क मूल्यों के साथ संकलित कक्षाओं में नाम टकराव हो सकते हैं। मैंने कई धागों में उल्लिखित इन पंक्तियों के साथ कुछ देखा है।

नामित तर्क युक्ति यहाँ है: http://www.scala-lang.org/sites/default/files/sids/rytz/Mon,%202009-11-09,%2017:29/onym-args.pdf

य़ह कहता है:

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

इसलिए, किसी भी दर पर, यह काम करने वाला नहीं है।

आप कुछ ऐसा कर सकते हैं जैसे आप जावा में क्या कर सकते हैं, जैसे:

def foo(a: String)(b: Int) =  a + (if (b > 0) b else 42)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.