2012-07-01 24 views
9

के साथ वर्ग के लिए लागू सबूत यहां दो लक्षणों के साथ एक सरल सेटअप है, एक वर्ग जो पिछले गुणों से घिरे एक कॉन्वेंट प्रकार के पैरामीटर के साथ है, और दूसरी कक्षा द्वारा बाध्य प्रकार के पैरामीटर वाला दूसरा वर्ग है। दोनों वर्गों के लिए, एक विशेष विधि उपलब्ध है (अंतर्निहित सबूत के माध्यम से) केवल दो गुणों में से एक टाइप पैरामीटर को कम करता है। यह ठीक संकलित:स्कैला: प्रकार पैरामीटर

trait Foo 
trait ReadableFoo extends Foo {def field: Int} 

case class Bar[+F <: Foo](foo: F) { 
    def readField(implicit evidence: F <:< ReadableFoo) = foo.field 
} 

case class Grill[+F <: Foo, +B <: Bar[F]](bar: B) { 
    def readField(implicit evidence: F <:< ReadableFoo) = bar.readField 
} 

हालांकि, बाद से Bar covariant है F में, मैं Grill में F पैरामीटर की आवश्यकता नहीं होनी चाहिए। मुझे बस B की आवश्यकता है Bar[ReadableFoo] का एक उप प्रकार है। लेकिन यह विफल रहता है:

case class Grill[+B <: Bar[_]](bar: B) { 
    def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField 
} 
त्रुटि के साथ

:

error: Cannot prove that Any <:< this.ReadableFoo. 
    def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField 

क्यों अंतर्निहित सबूत ध्यान में नहीं रखा जा रहा है?

उत्तर

6

कॉल bar.readField संभव है क्योंकि सबूत उदाहरण <:<B से Bar[ReadableFoo] पर एक अंतर्निहित रूपांतरण की अनुमति देता है।

समस्या मुझे लगता है कि readField पर कॉल करने के लिए आपको लगातार सबूत पैरामीटर F <:< ReadableFoo पर कॉल करना होगा। तो मेरा अनुमान है कि, कंपाइलर अंतर्निहित रिज़ॉल्यूशन के पहले खोज चरण में Bar के प्रकार पैरामीटर को पूरी तरह से प्रतिस्थापित नहीं करता है (क्योंकि readField खोजने के लिए, इसे केवल पहले Bar की आवश्यकता है)। और फिर यह दूसरे निहित संकल्प पर चोक करता है, क्योंकि जहां तक ​​मुझे पता है 'बैकट्रैकिंग' का कोई रूप नहीं है।

वैसे भी। अच्छी बात आप संकलक की तुलना में अधिक जानते हैं और आप रूपांतरण स्पष्ट रूप से शामिल कर सकते हैं, या तो, है <:< की apply विधि का उपयोग कर, या सहायक विधि implicitly का उपयोग करके:

case class Grill[+B <: Bar[_]](bar: B) { 
    def readField(implicit evidence: B <:< Bar[ReadableFoo]) = evidence(bar).readField 
} 

case class Grill[+B <: Bar[_]](bar: B) { 
    def readField(implicit evidence: B <:< Bar[ReadableFoo]) = 
    implicitly[Bar[ReadableFoo]](bar).readField 
} 

एक और संभावना है जो हो सकता है साफ है, यह <:< के कार्यान्वयन जो एक समस्या हो सकती है के रूप में @Kaito पता चलता है पर निर्भर नहीं करता है:

case class Grill[+B <: Bar[_]](bar: B) { 
    def readField(implicit evidence: B <:< Bar[ReadableFoo]) = 
    (bar: Bar[ReadableFoo]).readField 
} 
+0

मुझे यकीन है कि अगर <नहीं कर रहा हूँ: <कहा जा करने के लिए है, यह केवल एक मान है जो मौजूद है यदि पहला पैरामीटर दूसरे का उप प्रकार है। कार्यान्वयन मूल रूप से पहचान समारोह है। वे उदाहरण काम करते हैं क्योंकि वे मूल रूप से एक (सुरक्षित) टाइपकास्ट कर रहे हैं जो संकलक को readField विधि के लिए अंतर्निहित पैरामीटर खोजने में मदद करता है। – Kaito

+0

@ कaitो: क्या आपके पास कोई सबूत है कि '<: <' '' '' लागू होने का मतलब नहीं है? यह [दस्तावेजी व्यवहार] है (http://www.scala-lang.org/api/rc/scala/Predef$$$less$colon$less.html)। आप '(बार: बार [पठनीय फू]) लिख सकते हैं। ReadField' और अंतर्निहित रूपांतरण स्वचालित रूप से किक कर दें, लेकिन स्किस का संस्करण मुझे क्लीनर महसूस करता है। –

+0

@TravisBrown: कोई नहीं। लेकिन मुझे उस रेखा को नहीं मिल रहा है जो वास्तव में कार्य के व्यवहार को मौखिक रूप से दस्तावेज करता है, केवल अमूर्त फ़ंक्शन 1 विशेषता का विरासत स्पष्टीकरण। मैं मानता हूं कि यह अच्छा है लेकिन मुझे यकीन नहीं है कि क्या उस व्यवहार पर भरोसा किया जा सकता है, यह मेरे अगले संस्करण में एक अपवाद फेंक सकता है। – Kaito

5

0 __ के जवाब (अंतर्निहित सबूत तर्क सही प्रकार में bar चालू करने के लिए उपयोग करें) जवाब है मैं विशिष्ट खोज को देना चाहूंगा आयन आपने पूछा (हालांकि मैं सुझाव देता हूं कि implicitly का उपयोग न करें अगर आपको वहां पर निहित तर्क मिला है)।

यह ध्यान देने योग्य है कि जिस स्थिति का आप वर्णन कर रहे हैं, वह लगता है कि यह टाइप क्लास के माध्यम से विज्ञापन-प्रसार पॉलीमोर्फिज्म के लिए एक अच्छा उपयोग केस हो सकता है।उदाहरण के लिए कहते हैं कि हमें निम्नलिखित सेटअप मिल गया है:

trait Foo 
case class Bar[F <: Foo](foo: F) 
case class Grill[B <: Bar[_]](bar: B) 

और एक प्रकार वर्ग, नए उदाहरणों बनाने के लिए और किसी भी प्रकार के दायरे में एक उदाहरण है कि पर एक readField विधि रोगी के लिए कुछ सुविधा तरीकों के साथ साथ:

trait Readable[A] { def field(a: A): Int } 

object Readable { 
    def apply[A, B: Readable](f: A => B) = new Readable[A] { 
    def field(a: A) = implicitly[Readable[B]].field(f(a)) 
    } 

    implicit def enrich[A: Readable](a: A) = new { 
    def readField = implicitly[Readable[A]].field(a) 
    } 
} 

import Readable.enrich 

और उदाहरणों की एक जोड़ी:

implicit def barInstance[F <: Foo: Readable] = Readable((_: Bar[F]).foo) 
implicit def grillInstance[B <: Bar[_]: Readable] = Readable((_: Grill[B]).bar) 

और अंत में एक पठनीय Foo:

case class MyFoo(x: Int) extends Foo 

implicit object MyFooInstance extends Readable[MyFoo] { 
    def field(foo: MyFoo) = foo.x 
} 

यह हमें उदाहरण के लिए, निम्नलिखित करने के लिए अनुमति देता है:

scala> val readableGrill = Grill(Bar(MyFoo(11))) 
readableGrill: Grill[Bar[MyFoo]] = Grill(Bar(MyFoo(11))) 

scala> val anyOldGrill = Grill(Bar(new Foo {})) 
anyOldGrill: Grill[Bar[java.lang.Object with Foo]] = Grill(Bar([email protected])) 

scala> readableGrill.readField 
res0: Int = 11 

scala> anyOldGrill.readField 
<console>:22: error: could not find implicit value for evidence parameter of 
type Readable[Grill[Bar[java.lang.Object with Foo]]] 
       anyOldGrill.readField 
      ^

है कौन सा हम क्या चाहते हैं।

1

इस प्रश्न का उत्तर नहीं है, लेकिन पता चलता है कि 'प्रकार बाधा' वास्तव में सिर्फ एक अंतर्निहित रूपांतरण है:

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

scala> trait A { def test() {} } 
defined trait A 

scala> class WhatHappens[T] { def test(t: T)(implicit ev: T <:< A) = t.test() } 
defined class WhatHappens 

scala> :javap -v WhatHappens 
... 
public void test(java.lang.Object, scala.Predef$$less$colon$less); 
    Code: 
    Stack=2, Locals=3, Args_size=3 
    0: aload_2 
    1: aload_1 
    2: invokeinterface #12, 2; //InterfaceMethod scala/Function1.apply:(Ljava/lang/Object;)Ljava/lang/Object; 
    7: checkcast #14; //class A 
    10: invokeinterface #17, 1; //InterfaceMethod A.test:()V 
    15: return 
... 
    LocalVariableTable: 
    Start Length Slot Name Signature 
    0  16  0 this  LWhatHappens; 
    0  16  1 t  Ljava/lang/Object; 
    0  16  2 ev  Lscala/Predef$$less$colon$less; 
... 
+0

मुझे पता है कि यह कैसे काम करता है। जो सवाल मैं पूछ रहा था वह यह है कि क्या यह इरादा व्यवहार है और भविष्य के अपडेट में भरोसा किया जा सकता है। दस्तावेज केवल इतना उल्लेख करता है कि वे प्रकार की बाधाएं हैं, न कि उनके उपयोग को अंतर्निहित रूपांतरण के रूप में। – Kaito