2010-01-01 5 views
19

में पैटर्न मिलान संरचनात्मक प्रकार इस प्रिंट wtf क्यों करता है? पैटर्न मिलान संरचनात्मक प्रकारों पर काम नहीं करता है?स्कैला

"hello" match { 
    case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?") 
    case _ => println("okie dokie") 
    } 
+0

मैं अपनी टिप्पणी :) के प्रकाश में मेरा उत्तर का विस्तार किया है। –

उत्तर

18

(scala -unchecked) पर अनियंत्रित चेतावनी के साथ स्काला दुभाषिया में इस उदाहरण चल रहा है निम्न चेतावनी का उत्पादन: warning: refinement AnyRef{def doesNotExist(Int,List[_]): Double} in type pattern is unchecked since it is eliminated by erasure। दुर्भाग्यवश, इस तरह के एक सामान्य प्रकार को रनटाइम पर चेक नहीं किया जा सकता क्योंकि JVM ने जेनिक्स को संशोधित नहीं किया है।

सभी कि JVM इस पैटर्न मैच में देखता है:

"hello" match { 
    case s: Object => ... 
    case annon: Object => ... 
} 

संपादित करें: अपनी टिप्पणी के जवाब में, मैं एक समाधान के बारे में सोच कर दिया गया है, लेकिन समय यह कल पोस्ट करने के लिए नहीं था । दुर्भाग्यवश, भले ही कार्य करना चाहिए, संकलक उचित Manifest को इंजेक्ट करने में विफल रहता है।

जिस समस्या को आप हल करना चाहते हैं वह तुलना करना है यदि कोई ऑब्जेक्ट किसी दिए गए संरचनात्मक प्रकार का है। यहाँ (, स्काला 2.8-r20019 के रूप में स्काला 2.7.6.final मुझ पर एक दो बार दुर्घटनाग्रस्त हो गया इसी तरह के विचारों के साथ खेलते हुए) कुछ कोड मैं के बारे में सोच कर दिया गया है

type Foo = AnyRef { def doesNotExist(i: Int, x: List[_]): Double } 

def getManifest[T](implicit m: Manifest[T]) = m 

def isFoo[T](x: T)(implicit mt: Manifest[T]) = 
    mt == getManifest[Foo] 

विधि isFoo मूल रूप से प्रकट होता है तुलना Foo की कक्षा x। एक आदर्श दुनिया में, संरचनात्मक प्रकार का प्रकटन आवश्यक विधियों वाले किसी भी प्रकार के प्रकट होने के बराबर होना चाहिए। कम से कम यह मेरी सोच की ट्रेन है। दुर्भाग्यवश यह संकलित करने में विफल रहता है, क्योंकि संकलक के बजाय Manifest[AnyRef] को getManifest[Foo] पर कॉल करते समय इंजेक्ट करता है। दिलचस्प बात यह है कि यदि आप संरचनात्मक प्रकार का उपयोग नहीं करते हैं (उदाहरण के लिए, type Foo = String), यह कोड संकलित और अपेक्षा के अनुसार काम करता है। मैं किसी बिंदु पर एक प्रश्न पोस्ट करूंगा कि यह संरचनात्मक प्रकारों के साथ क्यों विफल रहता है - क्या यह एक डिज़ाइन निर्णय है, या यह केवल प्रयोगात्मक प्रतिबिंब API की समस्या है।

विफल होने पर, आप जावा प्रतिबिंब का उपयोग यह देखने के लिए कर सकते हैं कि किसी ऑब्जेक्ट में कोई विधि है या नहीं।

def containsMethod(x: AnyRef, name: String, params: java.lang.Class[_]*) = { 
    try { 
    x.getClass.getMethod(name, params: _*) 
    true 
    } 
    catch { 
    case _ => false 
    } 
} 

जो काम करता है के रूप में उम्मीद:

containsMethod("foo", "concat", classOf[String]) // true 
containsMethod("foo", "bar", classOf[List[Int]]) // false 

... लेकिन यह बहुत अच्छा नहीं है।

इसके अलावा, ध्यान दें कि संरचनात्मक प्रकार की संरचना रनटाइम पर उपलब्ध नहीं है। यदि आपके पास def foo(x: {def foo: Int}) = x.foo विधि है, तो मिटाने के बाद आपको def foo(x: Object) = [some reflection invoking foo on x] मिलती है, प्रकार की जानकारी खो जाती है। यही कारण है कि प्रतिबिंब का उपयोग पहली जगह में किया जाता है, क्योंकि आपको Object पर एक विधि का आह्वान करना पड़ता है और JVM को यह नहीं पता कि Object में यह तरीका है या नहीं।

+0

धन्यवाद, Flaviu। वह मेरे प्रश्न का उत्तर देता है। लेकिन यह अभी भी मुझे आश्चर्यचकित करता है कि यह हासिल करने का सबसे अच्छा तरीका क्या होगा, क्योंकि संरचना कुछ ऐसा है जो वास्तव में प्रतिबिंब के माध्यम से रनटाइम पर उपलब्ध है। यह सिर्फ पाने के लिए बेकार है। –

+0

फॉलोअप के लिए धन्यवाद। बहुत दिलचस्प सामान। –

7

आप प्रतिबिंब का उपयोग करने के लिए जा रहे हैं, तो आप कम से कम यह एक निकालने के साथ अच्छे लग रहे बना सकते हैं:

object WithFoo { 
    def foo(){ 
     println("foo was called") 
    } 
} 

object HasFoo { 
    def containsMethod(x: AnyRef, name: String, params: Array[java.lang.Class[_]]) : Boolean = { 
     try { 
      x.getClass.getMethod(name, params: _*) 
      true 
     } catch { 
      case _ => false 
     } 
    } 

    def unapply(foo:AnyRef):Option[{def foo():Unit}] = { 
     if (containsMethod(foo, "foo", new Array[Class[_]](0))) { 
      Some(foo.asInstanceOf[{def foo():Unit}]) 
     } else None 
    } 
} 


WithFoo.asInstanceOf[AnyRef] match { 
    case HasFoo(foo) => foo.foo() 
    case _ => println("no foo") 
} 
+0

अच्छा होगा अगर वह 'हैसफू' को अधिक लचीला रूप से परिभाषित करने में सक्षम था, जैसे 'वैल हैसफू = नया है [{def foo(): unit}] ("foo") '।मैंने अभी ऐसा करने की कोशिश की, लेकिन अभी भी अधिक जटिल प्रकारों जैसे '{def foo (i: Int): Int}' के बारे में कुछ समस्याएं प्रतीत होती हैं। – Debilski

+0

संकलक क्यों स्वचालित रूप से ऐसा नहीं करता है? – Gabriel

+0

'includeMethod' को' try (x.getClass.getMethod (नाम, पैराम्स: _ *)) में निर्जलित किया जा सकता है। IsSuccess' –