2011-12-26 18 views
26

मैं स्कैला में covariance और contravariance समझते हैं। कॉन्वर्सिस में असली दुनिया में कई अनुप्रयोग हैं, लेकिन मैं कार्यों के लिए पुराने पुराने उदाहरणों को छोड़कर, किसी भी तरह के contravariance अनुप्रयोगों के बारे में नहीं सोच सकता।स्कैला contravariance - असली जीवन उदाहरण

क्या कोई पर वास्तविक प्रकाश उदाहरणcontravariance उपयोग कर सकता है?

+0

डैनियल स्पिवाक का http: // stackoverflow का उत्तर देखें।कॉम/प्रश्न/663254/स्कैला-कॉन्वर्सिस-contravariance-question – sourcedelica

+3

... क्योंकि कोई भी असली दुनिया में कार्यों का उपयोग नहीं करता है? =) –

उत्तर

20

मेरी राय में, दो Function के बाद सबसे सरल उदाहरण आदेश देने और समानता कर रहे हैं। हालांकि, पहला स्कैला की मानक लाइब्रेरी में कॉन्ट्रैक्ट-वेरिएंट नहीं है, और दूसरा इसमें भी मौजूद नहीं है। तो, मैं स्कालाज़ समकक्षों का उपयोग करने जा रहा हूं: Order और Equal

अगला, मुझे कुछ वर्ग पदानुक्रम की आवश्यकता है, अधिमानतः एक जो परिचित है और, ज़ाहिर है, इसके ऊपर दोनों अवधारणाओं को इसके लिए समझदारी होनी चाहिए। यदि स्कैला के पास Number सभी संख्यात्मक प्रकारों का सुपरक्लास था, तो यह सही होगा। दुर्भाग्यवश, इसमें ऐसी कोई बात नहीं है।

तो मैं संग्रहों के साथ उदाहरण बनाने की कोशिश करने जा रहा हूं। इसे सरल बनाने के लिए, बस Seq[Int] और List[Int] पर विचार करें। यह स्पष्ट होना चाहिए कि List[Int]Seq[Int] का एक उप प्रकार है, यानी List[Int] <: Seq[Int]

तो, हम इसके साथ क्या कर सकते हैं? अब मैं कुछ इस तरह कर सकते हैं

implicit val seqOrder = new Order[Seq[Int]] { 
    def order(a: Seq[Int], b: Seq[Int]) = 
    if (a.size < b.size) LT 
    else if (b.size < a.size) GT 
    else EQ 
} 
इन परिभाषाओं के साथ

,:

def smaller(a: List[Int], b: List[Int])(implicit ord: Order[List[Int]]) = 
    if (ord.order(a,b) == LT) a else b 

अब मैं Seq[Int] के लिए एक अंतर्निहित Order लिखने के लिए जा रहा हूँ: सबसे पहले, के कुछ है कि दो सूचियों तुलना लिखना जाने

scala> smaller(List(1), List(1, 2, 3)) 
res0: List[Int] = List(1) 

नोट मैं एक Order[List[Int]] के लिए पूछ रहा हूँ कि, लेकिन मैं एक Order[Seq[Int]] गुजर रहा हूँ। इसका मतलब है कि Order[Seq[Int]] <: Order[List[Int]]। यह देखते हुए कि Seq[Int] >: List[Int], यह केवल भिन्नता के कारण संभव है।

अगला प्रश्न यह है: क्या इसका कोई अर्थ है?

चलो smaller फिर से विचार करें। मैं पूर्णांक की दो सूचियों की तुलना करना चाहता हूं। स्वाभाविक रूप से, जो कुछ सूचियों की तुलना करता है वह स्वीकार्य है, लेकिन दो Seq[Int] की तुलना में कुछ ऐसा तर्क है जो स्वीकार्य है?

seqOrder की परिभाषा में ध्यान दें कि चीजों की तुलना कैसे की जा रही है पैरामीटर पैरामीटर। जाहिर है, List[Int]Seq[Int] की अपेक्षा रखने वाले किसी पैरामीटर के लिए पैरामीटर हो सकता है। उस से कुछ ऐसा है जो Seq[Int] की तुलना में कुछ ऐसा है जो List[Int] की तुलना में कुछ स्वीकार्य है: वे दोनों एक ही पैरामीटर के साथ उपयोग किए जा सकते हैं।

रिवर्स के बारे में क्या? मान लीजिए कि मेरे पास एक विधि थी जिसकी तुलना केवल :: (सूची का विपक्ष) की तुलना में की गई थी, जो Nil के साथ List का एक उप प्रकार है। मैं स्पष्ट रूप से इसका उपयोग नहीं कर सका, क्योंकि smaller तुलना करने के लिए Nil प्राप्त कर सकता है। यह इस प्रकार है कि Order[List[Int]] के बजाय Order[::[Int]] का उपयोग नहीं किया जा सकता है।

की समानता के लिए आगे बढ़ना है, और इसके लिए एक विधि लिखें:

def equalLists(a: List[Int], b: List[Int])(implicit eq: Equal[List[Int]]) = eq.equal(a, b) 

क्योंकि Order फैली Equal, मैं इसे ऊपर एक ही निहित के साथ उपयोग कर सकते हैं:

scala> equalLists(List(4, 5, 6), List(1, 2, 3)) // we are comparing lengths! 
res3: Boolean = true 

तर्क यहाँ है वही है। कुछ भी जो बता सकता है कि दो Seq[Int] वही हैं, जाहिर है, यह भी बताएं कि दो List[Int] समान हैं या नहीं। उस से, यह Equal[Seq[Int]] <: Equal[List[Int]] का पालन करता है, जो सच है क्योंकि Equal अनुबंध-भिन्नता है।

+0

स्कैला है। एक्वाइव, लेकिन यह या तो contravariant नहीं है। – extempore

+0

आपके "छोटे" उदाहरण में, क्या मैं कर सकता था: 'छोटा छोटा (ए: सूची [Int], बी: सूची [Int]) (निहित ord: आदेश [सेक [Int]]) = यदि (ord। आदेश (ए, बी) == एलटी) एक और बी'? 'ऑर्डर [सूची [Int]]' के बजाय और उसी उद्देश्य को प्राप्त करें? Contravariance का उपयोग क्यों करें? – Chao

+0

@Coo हाँ, आप कर सकते हैं, लेकिन फिर आप एक और अधिक विशिष्ट क्रम नहीं उठाएंगे। –

18

यह उदाहरण आखिरी परियोजना से है जिस पर मैं काम कर रहा था। मान लें कि आपके पास एक टाइप-क्लास PrettyPrinter[A] है जो A प्रकार की सुंदर-प्रिंटिंग ऑब्जेक्ट्स के लिए तर्क प्रदान करता है। अब अगर B >: A (यानी BA का सुपरक्लास है) और आप जानते हैं कि B को सुंदर प्रिंट कैसे करें (यानी PrettyPrinter[B] उपलब्ध है) तो आप उसी तर्क को सुंदर प्रिंट A पर उपयोग कर सकते हैं। दूसरे शब्दों में, B >: A का अर्थ है PrettyPrinter[B] <: PrettyPrinter[A]। तो आप A पर PrettyPrinter[A] contravariant घोषित कर सकते हैं।

scala> trait Animal 
defined trait Animal 

scala> case class Dog(name: String) extends Animal 
defined class Dog 

scala> trait PrettyPrinter[-A] { 
    | def pprint(a: A): String 
    | } 
defined trait PrettyPrinter 

scala> def pprint[A](a: A)(implicit p: PrettyPrinter[A]) = p.pprint(a) 
pprint: [A](a: A)(implicit p: PrettyPrinter[A])String 

scala> implicit object AnimalPrettyPrinter extends PrettyPrinter[Animal] { 
    | def pprint(a: Animal) = "[Animal : %s]" format (a) 
    | } 
defined module AnimalPrettyPrinter 

scala> pprint(Dog("Tom")) 
res159: String = [Animal : Dog(Tom)] 

कुछ अन्य उदाहरण Ordering स्काला मानक पुस्तकालय से प्रकार स्तरीय, Equal, Show (isomorphic ऊपर PrettyPrinter के लिए), Scalaz से Resource प्रकार वर्गों होगा आदि

संपादित करें:
जैसा कि डैनियल ने बताया, स्कैला का Ordering contravariant नहीं है। (मुझे वास्तव में पता नहीं क्यों है।) आप इसके बजाय scalaz.Order पर विचार कर सकते हैं जिसका उद्देश्य scala.Ordering के समान उद्देश्य के लिए है, लेकिन इसके प्रकार पैरामीटर पर contravariant है।

परिशिष्ट:
महाप्रकार-उप प्रकार संबंध लेकिन रिश्ते का एक प्रकार है कि दो प्रकार के बीच मौजूद कर सकते हैं। ऐसे कई रिश्ते संभव हो सकते हैं। आइए दो प्रकार A और B फ़ंक्शन f: B => A (यानी एक मनमाना संबंध) से संबंधित मानें। डेटा-प्रकार F[_] को एक contravariant functor कहा जाता है यदि आप contramap ऑपरेशन को परिभाषित कर सकते हैं जो B => AF[A => B] पर फ़ंक्शन उठा सकता है।

  1. x.contramap(identity) == x
  2. x.contramap(f).contramap(g) == x.contramap(f compose g)

ऊपर (Show, Equal आदि) पर चर्चा की डेटा प्रकार के सभी कर रहे हैं:

निम्नलिखित कानूनों संतुष्ट होने की जरूरत है contravariant functors। यह गुण हमें इस तरह के एक वर्णन नीचे के रूप में उपयोगी चीजें करने देता है:

मान लीजिए आप एक वर्ग Candidate के रूप में परिभाषित किया है:

case class Candidate(name: String, age: Int) 

आप एक Order[Candidate] जो अपनी उम्र से उम्मीदवारों का आदेश देता है की जरूरत है। अब आप जानते हैं कि Order[Int] उदाहरण उपलब्ध है।आप contramap संचालन के साथ कि एक से Order[Candidate] उदाहरण प्राप्त कर सकते हैं:

val byAgeOrder: Order[Candidate] = 
    implicitly[Order[Int]] contramap ((_: Candidate).age) 
2

एक वास्तविक दुनिया घटना संचालित सॉफ्टवेयर सिस्टम पर आधारित एक उदाहरण। ऐसी प्रणाली घटनाओं की विस्तृत श्रेणियों पर आधारित है, जैसे सिस्टम (सिस्टम इवेंट्स) के कामकाज से संबंधित घटनाएं, उपयोगकर्ता क्रियाओं (उपयोगकर्ता घटनाओं) द्वारा उत्पन्न घटनाएं आदि।

एक संभावित घटना पदानुक्रम:

trait Event 

trait UserEvent extends Event 

trait SystemEvent extends Event 

trait ApplicationEvent extends SystemEvent 

trait ErrorEvent extends ApplicationEvent 

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

trait Sink[-In] { 
    def notify(o: In) 
} 

- प्रतीक के साथ प्रकार पैरामीटर अंकन का एक परिणाम के रूप में, सिंक प्रकार contravariant बन गया।

इच्छुक पार्टियों को सूचित करने का एक संभावित तरीका यह है कि कोई घटना एक विधि लिखना और संबंधित घटना को पास करना है। काल्पनिक सिंक कार्यान्वयन के एक जोड़े

def appEventFired(e: ApplicationEvent, s: Sink[ApplicationEvent]): Unit = { 
    // do some processing related to the event 
    // notify the event sink 
    s.notify(e) 
} 

def errorEventFired(e: ErrorEvent, s: Sink[ErrorEvent]): Unit = { 
    // do some processing related to the event 
    // notify the event sink 
    s.notify(e) 
} 

: यह विधि परिकल्पित कुछ प्रसंस्करण करना होगा और फिर इसे घटना सिंक को अधिसूचित करने का ख्याल रखना होगा।

trait SystemEventSink extends Sink[SystemEvent] 

val ses = new SystemEventSink { 
    override def notify(o: SystemEvent): Unit = ??? 
} 

trait GenericEventSink extends Sink[Event] 

val ges = new GenericEventSink { 
    override def notify(o: Event): Unit = ??? 
} 

निम्न विधि कॉल संकलक द्वारा स्वीकार किए जाते हैं:

appEventFired(new ApplicationEvent {}, ses) 

errorEventFired(new ErrorEvent {}, ges) 

appEventFired(new ApplicationEvent {}, ges) 

कॉल की श्रृंखला को देखते हुए आपको लगता है कि यह एक तरीका है एक Sink[SystemEvent] और यहां तक ​​कि एक साथ के साथ एक Sink[ApplicationEvent] उम्मीद कर कॉल करने के लिए संभव है नोटिस Sink[Event]। साथ ही, आप को Sink[Event] के साथ अपेक्षित विधि को कॉल कर सकते हैं।

एक contravariance बाधा के साथ invariance की जगह, Sink[SystemEvent]Sink[ApplicationEvent] का उप प्रकार बन जाता है। इसलिए, contravariance भी 'चौड़ा' संबंध के रूप में सोचा जा सकता है, क्योंकि प्रकार अधिक विशिष्ट से अधिक सामान्य से 'चौड़ा' हैं।

निष्कर्ष

यह उदाहरण my blog

अंत में पर पाया विचरण के बारे में लेख की एक श्रृंखला में वर्णित किया गया है, मैं इसे भी इसके पीछे सिद्धांत को समझने के लिए ... मदद करता है लगता है

+0

यहां सबसे अच्छा जवाब –

संबंधित मुद्दे