"टाइप डिसंक्शन" (संघ प्रकार) को कैसे परिभाषित करें?


181

एक तरीका यह है कि है सुझाव दिया गया अतिभारित तरीकों में से डबल परिभाषाओं से निपटने के लिए पैटर्न मिलान के साथ अधिक भार को बदलने के लिए है:

object Bar {
   def foo(xs: Any*) = xs foreach { 
      case _:String => println("str")
      case _:Int => println("int")
      case _ => throw new UglyRuntimeException()
   }
}

इस दृष्टिकोण के लिए आवश्यक है कि हम तर्कों पर जाँच करने के लिए स्थैतिक प्रकार का आत्मसमर्पण करें foo। यह लिखने के लिए सक्षम होने के लिए बहुत अच्छा होगा

object Bar {
   def foo(xs: (String or Int)*) = xs foreach {
      case _: String => println("str")
      case _: Int => println("int")
   }
}

मैं करीब पहुँच सकता हूँ Either, लेकिन यह दो से अधिक प्रकारों के साथ बदसूरत तेज़ हो जाता है:

type or[L,R] = Either[L,R]

implicit def l2Or[L,R](l: L): L or R = Left(l)
implicit def r2Or[L,R](r: R): L or R = Right(r)

object Bar {
   def foo(xs: (String or Int)*) = xs foreach {
      case Left(l) => println("str")
      case Right(r) => println("int")
   }
}

यह एक सामान्य (सुंदर, कुशल) समाधान की आवश्यकता होगी परिभाषित करने की तरह दिखता है Either3, Either4एक ही अंत को प्राप्त करने के लिए एक वैकल्पिक समाधान की, .... क्या किसी को पता है कि? मेरी जानकारी के लिए, स्काला में "टाइप डिसंक्शन" बिल्ट-इन नहीं है। इसके अलावा, मानक पुस्तकालय में कहीं और निहितार्थ के रूप में परिभाषित रूपांतरण दिए गए हैं ताकि मैं उन्हें आयात कर सकूं?

जवाबों:


142

खैर, के विशिष्ट मामले में Any*, यह ट्रिक नीचे काम नहीं करेगा, क्योंकि यह मिश्रित प्रकारों को स्वीकार नहीं करेगा। हालाँकि, चूंकि मिश्रित प्रकार ओवरलोडिंग के साथ काम नहीं करेंगे, इसलिए यह वही हो सकता है जो आप चाहते हैं।

सबसे पहले, निम्न के रूप में आप जिस प्रकार को स्वीकार करना चाहते हैं, उसके साथ एक वर्ग घोषित करें:

class StringOrInt[T]
object StringOrInt {
  implicit object IntWitness extends StringOrInt[Int]
  implicit object StringWitness extends StringOrInt[String]
}

अगला, fooइस तरह घोषित करें:

object Bar {
  def foo[T: StringOrInt](x: T) = x match {
    case _: String => println("str")
    case _: Int => println("int")
  }
}

और बस। आप कॉल कर सकते हैं foo(5)या foo("abc"), और यह काम करेगा, लेकिन कोशिश करें foo(true)और यह विफल हो जाएगा। यह क्लाइंट कोड द्वारा साइड-स्टेप किया जा सकता है StringOrInt[Boolean], जब तक कि रान्डेल द्वारा नोट नहीं किया जाता है, तब तक आप StringOrIntएक sealedक्लास बनाते हैं ।

यह काम करता है क्योंकि T: StringOrIntइसका मतलब है कि टाइप का एक अंतर्निहित पैरामीटर है StringOrInt[T], और क्योंकि स्केल उस प्रकार के साथी ऑब्जेक्ट्स के अंदर दिखता है, यह देखने के लिए कि क्या उस प्रकार के काम के लिए कोड पूछने के लिए वहाँ इम्पीचिट्स हैं।


14
यदि class StringOrInt[T]बनाया गया है sealed, तो आपके द्वारा संदर्भित "लीक" (निश्चित रूप से, यह क्लाइंट कोड द्वारा StringOrInt[Boolean]" बनाकर साइड-स्टेप किया जा सकता है ) प्लग किया गया है, कम से कम यदि StringOrIntइसकी स्वयं की फ़ाइल में रहता है। फिर गवाह वस्तुओं को उसी तरह से परिभाषित किया जाना चाहिए जैसा कि StringOrInt
रान्डेल शुल्ज

3
मैंने कुछ हद तक इस समाधान को सामान्य करने की कोशिश की (नीचे एक उत्तर के रूप में पोस्ट किया गया)। Eitherदृष्टिकोण की तुलना में मुख्य दोष यह लगता है कि हम मैच की जांच के लिए बहुत सारे संकलक समर्थन खो देते हैं।
आरोन नोवस्ट्रुप

अच्छी चाल! हालाँकि, यहां तक ​​कि सीलबंद वर्ग के साथ, आप अभी भी क्लाइंट कोड में या तो एक निहित वैल b = नया StringOrInt [बूलियन] को फू के साथ दायरे में परिभाषित कर सकते हैं, या स्पष्ट रूप से foo (2.9) (नया StringOrInt [डबल]) कॉल करके इसे दरकिनार कर सकते हैं। मुझे लगता है कि आपको कक्षा को भी सारगर्भित बनाने की आवश्यकता है।
पाओलो फलाबेला

2
हाँ; इसका इस्तेमाल करना बेहतर होगाtrait StringOrInt ...
मैकेनिकल घोंघा

7
Ps यदि आप उप-प्रकारों का समर्थन करना चाहते हैं, तो बस बदल StringOrInt[T]जाएं StringOrInt[-T](देखें stackoverflow.com/questions/24387701/… )
Eran Medan

178

मीलों साबिन उनकी हाल ही ब्लॉग पोस्ट में संघ प्रकार प्राप्त करने के लिए एक बहुत अच्छा तरीका का वर्णन करी-हावर्ड समाकृतिकता के माध्यम से स्काला में अनबॉक्स्ड संघ प्रकार :

वह पहले प्रकार की उपेक्षा को परिभाषित करता है

type ¬[A] = A => Nothing

डी मॉर्गन के कानून का उपयोग करने से यह यूनियन प्रकारों को परिभाषित करने की अनुमति देता है

type[T, U] = ¬[¬[T] with ¬[U]]

निम्नलिखित सहायक निर्माणों के साथ

type ¬¬[A] = ¬[¬[A]]
type ||[T, U] = { type λ[X] = ¬¬[X] <:< (T ∨ U) }

आप संघ प्रकार इस प्रकार लिख सकते हैं:

def size[T : (Int || String)#λ](t : T) = t match {
    case i : Int => i
    case s : String => s.length
}

13
मैंने देखा है कि सबसे भयानक चीजों में से एक है।
सबमोनॉइड 10

18
यहां मीलों के विचार का मेरा विस्तारित कार्यान्वयन है: github.com/GenslerAppsPod/scalavro/blob/master/util/src/main/… - उदाहरणों के साथ: github.com/GenslerAppsPod/scalavro/blob/master/util/src/ परीक्षा/…
कॉनर डॉयल

6
उपरोक्त टिप्पणी का अपने आप में एक उत्तर होना चाहिए। यह सिर्फ माइल्स के विचार का एक कार्यान्वयन है, लेकिन मावेन सेंट्रल पर एक पैकेज में अच्छी तरह से लिपटा हुआ है, और उन सभी यूनिकोड प्रतीकों के बिना जो (?) एक निर्माण प्रक्रिया में कुछ के लिए एक समस्या पैदा करते हैं।
जिम पिवार्स्की

2
वह मजाकिया चरित्र बूलियन नकार है
मिचिड

1
प्रारंभ में, यह विचार मेरे लिए बहुत दूर तक देखा गया था। इस धागे में उल्लिखित लगभग हर लिंक को पढ़ते हुए, मुझे इस विचार और इसके कार्यान्वयन की सुंदरता से पकड़ लिया गया था :-) ... लेकिन मुझे अभी भी ऐसा महसूस हो रहा है कि यह कुछ जटिल है ... अब सिर्फ इसलिए कि यह अभी तक सीधे उपलब्ध नहीं है स्काला से दूर। जैसा कि माइल्स कहते हैं: "अब हमें बस मार्टिन और एड्रियन को सीधे पहुंच बनाने के लिए पास्टर की जरूरत है।"
रिचर्ड गोम्स

44

डॉट्टी , एक नया प्रायोगिक स्काला कंपाइलर, यूनियन प्रकारों (लिखित A | B) का समर्थन करता है , इसलिए आप वही कर सकते हैं जो आप चाहते थे:

def foo(xs: (String | Int)*) = xs foreach {
   case _: String => println("str")
   case _: Int => println("int")
}

1
इन दिनों में।
माइकल अहलर्स

5
वैसे, डॉट्टी नया स्कैला 3 होगा (कुछ महीने पहले यह घोषणा की गई थी)।
इनफिनिटी 8

1
और 2020 के उत्तरार्ध में कहीं उपलब्ध होगा
जुलिएनड

31

यहां यूनियन प्रकारों को एनकोड करने का रेक्स केर तरीका दिया गया है। सीधे और सरल!

scala> def f[A](a: A)(implicit ev: (Int with String) <:< A) = a match {
     |   case i: Int => i + 1
     |   case s: String => s.length
     | }
f: [A](a: A)(implicit ev: <:<[Int with String,A])Int

scala> f(3)
res0: Int = 4

scala> f("hello")
res1: Int = 5

scala> f(9.2)
<console>:9: error: Cannot prove that Int with String <:< Double.
       f(9.2)
        ^

स्रोत: टिप्पणी # 27 मीलों सबिन द्वारा इस उत्कृष्ट ब्लॉग पोस्ट के तहत जो स्काला में यूनियन प्रकार के एन्कोडिंग का एक और तरीका प्रदान करता है।


6
दुर्भाग्य से, इस एन्कोडिंग को हराया जा सकता है: scala> f(9.2: AnyVal)टाइपसेकर को पास करता है।
किप्टन बैरोस

@ किपटन: यह दुखद है। क्या माइल्स सबिन की एन्कोडिंग भी इस समस्या से ग्रस्त है?
अक्टूबर

9
माइल्स कोड का थोड़ा सरल संस्करण है; चूंकि वह वास्तव में फ़ंक्शन के कंट्रावेरेंट पैरामीटर के रिवर्स निहितार्थ का उपयोग कर रहा है, न कि एक सख्त "नहीं", आप trait Contra[-A] {}सभी कार्यों के स्थान पर कुछ भी नहीं करने के लिए उपयोग कर सकते हैं । इसलिए आपको type Union[A,B] = { type Check[Z] = Contra[Contra[Z]] <:< Contra[Contra[A] with Contra[B]] }इस्तेमाल किए जाने वाले सामान मिलते हैं def f[T: Union[Int, String]#Check](t: T) = t match { case i: Int => i; case s: String => s.length }( जैसे बिना फैंसी यूनिकोड)।
रेक्स केर

यह संघ प्रकार की विरासत की समस्या को हल कर सकता है? stackoverflow.com/questions/45255270/…
jhegedus

हम्म, मैं इसे करने की कोशिश की, मैं इस एन्कोडिंग के साथ वापसी प्रकार नहीं बना सकते, इसलिए इसकी subtyping लागू करने के लिए संभव हो सकता है नहीं लगता है stackoverflow.com/questions/45255270/...
jhegedus

18

इस प्रकार डेनियल के समाधान को सामान्य बनाना संभव है:

sealed trait Or[A, B]

object Or {
   implicit def a2Or[A,B](a: A) = new Or[A, B] {}
   implicit def b2Or[A,B](b: B) = new Or[A, B] {}
}

object Bar {
   def foo[T <% String Or Int](x: T) = x match {
     case _: String => println("str")
     case _: Int => println("int")
   }
}

इस दृष्टिकोण की मुख्य कमियां हैं

  • जैसा कि डैनियल ने बताया, यह मिश्रित प्रकार के साथ संग्रह / वैरगल्स को संभालता नहीं है
  • कंपाइलर चेतावनी जारी नहीं करता है यदि मैच समाप्त नहीं होता है
  • कंपाइलर एक त्रुटि जारी नहीं करता है यदि मैच में एक असंभव मामला शामिल है
  • जैसा Eitherदृष्टिकोण, आगे सामान्यीकरण को परिभाषित अनुरूप की आवश्यकता होगी Or3, Or4आदि लक्षण। बेशक, ऐसे लक्षणों को परिभाषित करना संबंधित Eitherवर्गों को परिभाषित करने की तुलना में बहुत सरल होगा ।

अपडेट करें:

मिच बिल्विन्स एक बहुत ही समान दृष्टिकोण प्रदर्शित करता है और दिखाता है कि इसे दो प्रकार से अधिक कैसे सामान्य किया जाए, इसे "हकलाना या" करार दिया।


17

मैं इस क्षेत्र में माइल्स सबिन के काम के सरलीकरण के साथ टाइप सूचियों की धारणा को जोड़कर एन-एरी यूनियन प्रकारों के अपेक्षाकृत स्वच्छ कार्यान्वयन पर ठोकर खाई है , जो किसी अन्य उत्तर में उल्लेख करता है।

यह देखते हुए कि परिभाषा ¬[-A]किस प्रकार Aदी गई है, परिभाषा के अनुसार A <: Bहम लिख सकते हैं ¬[B] <: ¬[A], प्रकारों के क्रम को बदलते हुए।

दिए गए प्रकार A, Bऔर X, हम व्यक्त करना चाहते हैं X <: A || X <: B। विरोधाभासी को लागू करते हुए, हम प्राप्त करते हैं ¬[A] <: ¬[X] || ¬[B] <: ¬[X]। बदले में इस कर सकते हैं के रूप में व्यक्त किया जा ¬[A] with ¬[B] <: ¬[X]जिसमें से एक Aया Bके एक महाप्रकार होना चाहिए Xया Xखुद (समारोह तर्क के बारे में सोचना)।

object Union {
  import scala.language.higherKinds

  sealed trait ¬[-A]

  sealed trait TSet {
    type Compound[A]
    type Map[F[_]] <: TSet
  }

  sealed traitextends TSet {
    type Compound[A] = A
    type Map[F[_]] =}

  // Note that this type is left-associative for the sake of concision.
  sealed trait[T <: TSet, H] extends TSet {
    // Given a type of the form `∅ ∨ A ∨ B ∨ ...` and parameter `X`, we want to produce the type
    // `¬[A] with ¬[B] with ... <:< ¬[X]`.
    type Member[X] = T#Map[¬]#Compound[¬[H]] <:< ¬[X]

    // This could be generalized as a fold, but for concision we leave it as is.
    type Compound[A] = T#Compound[H with A]

    type Map[F[_]] = T#Map[F] ∨ F[H]
  }

  def foo[A : (∅ ∨ StringIntList[Int])#Member](a: A): String = a match {
    case s: String => "String"
    case i: Int => "Int"
    case l: List[_] => "List[Int]"
  }

  foo(42)
  foo("bar")
  foo(List(1, 2, 3))
  foo(42d) // error
  foo[Any](???) // error
}

मैंने कुछ समय इस प्रकार के सदस्यों के साथ ऊपरी तौर पर बंधे रहने के लिए इस विचार को मिलाने TListमें बिताया है , जैसा कि हर्र / अप में देखा गया है , हालाँकि इस Mapप्रकार की सीमा के साथ कार्यान्वयन अब तक चुनौतीपूर्ण साबित हुआ है।


1
यह शानदार है, धन्यवाद! मैंने पहले के दृष्टिकोणों की कोशिश की, लेकिन संघ के हिस्से के रूप में सामान्य प्रकारों के साथ इसका उपयोग करने में समस्या रही। यह एकमात्र कार्यान्वयन था जिसे मैं सामान्य प्रकारों के साथ काम कर सका।
समर आद्रा

अफसोस की बात है, लेकिन शायद उम्मीद की जा सकती है, जब मैं जावा कोड से एक संघ प्रकार में एक स्केल विधि का उपयोग करने की कोशिश करता हूं, तो यह काम नहीं करता है। त्रुटि: (४०, २ ९) जावा: विधि सेटावल्यू इन क्लास कॉन्फिगरेशन को दिए गए प्रकारों पर लागू नहीं किया जा सकता है; आवश्यक: X, scala.Predef। $ कम $ कोलन $ कम <UnionTypes.package। $ u00AC <java.lang.Object>, UnionTypes.package। $ u00AC <X >> पाया: java.lang.String कारण: यह अनुमान नहीं लगा सकता है। टाइप-वेरिएबल (एक्स) एक्स (वास्तविक और औपचारिक तर्क सूचियां लंबाई में भिन्न होती हैं)
समीर आद्रा

अभी भी इस कार्यान्वयन में कुछ विवरणों पर स्पष्ट नहीं है। उदाहरण के लिए, मूल लेख ने नकार को "टाइप] [A] = A => कुछ नहीं" के रूप में परिभाषित किया है, लेकिन इस संस्करण में यदि बस "सील विशेषता" [-ए] "है और विशेषता कहीं भी विस्तारित नहीं है। यह कैसे काम करता है?
समर आद्रा

@Samer Adra यह किसी भी तरह से काम करेगा, लेख Function1एक मौजूदा कंट्रावेरिएंट प्रकार के रूप में उपयोग करता है । आपको एक कार्यान्वयन की आवश्यकता नहीं है, आपको केवल आवश्यकता के अनुरूप ( <:<) का सबूत है ।
जे क्रैकनेल

कोई भी विचार कैसे एक कंस्ट्रक्टर है जो एक संघ प्रकार स्वीकार करता है?
समीर आद्रा

12

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

abstract class NameOf[T] {
  def get : String
}

implicit object NameOfStr extends NameOf[String] {
  def get = "str"
}

implicit object NameOfInt extends NameOf[Int] {
 def get = "int"
}

def printNameOf[T](t:T)(implicit name : NameOf[T]) = println(name.get)

यदि आप इसे REPL में चलाते हैं:

scala> printNameOf(1)
int

scala> printNameOf("sss")
str

scala> printNameOf(2.0f)
<console>:10: error: could not find implicit value for parameter nameOf: NameOf[
Float]
       printNameOf(2.0f)

              ^

मैं गलत हो सकता है, लेकिन मुझे नहीं लगता कि यह वही है जो ओपी देख रहा था। ओपी एक डेटा प्रकार है जो प्रकार की एक संबंध तोड़ना संघ का प्रतिनिधित्व कर सकता है, और फिर उस पर मामला विश्लेषण कर के बारे में पूछ रहा था रनटाइम पर क्या वास्तविक प्रकार निकला होना करने के लिए देखने के लिए। प्रकार वर्ग इस समस्या को हल नहीं करेंगे, क्योंकि वे एक शुद्ध संकलन-समय निर्माण हैं।
टॉम क्रॉकेट

4
पूछा जा रहा असली सवाल यह था कि विभिन्न प्रकारों के लिए अलग-अलग व्यवहार को कैसे उजागर किया जाए, लेकिन ओवरलोडिंग के बिना। प्रकार वर्गों (और शायद C / C ++ के लिए कुछ जोखिम) के ज्ञान के बिना, एक संघ प्रकार एकमात्र समाधान प्रतीत होता है। Eitherइस मान्यता को सुदृढ़ करने के लिए स्काला के पहले से मौजूद प्रकार का चलन है। स्काला के इम्पीचिट्स के माध्यम से टाइप कक्षाओं का उपयोग करना अंतर्निहित समस्या का एक बेहतर समाधान है, लेकिन यह एक अपेक्षाकृत नई अवधारणा है और अभी भी व्यापक रूप से ज्ञात नहीं है, यही वजह है कि ओपी उन्हें संघ प्रकार के संभावित विकल्प के रूप में विचार करना भी नहीं जानता था।
केविन राइट

क्या यह सबटाइपिंग के साथ काम करता है? stackoverflow.com/questions/45255270/…
jhegedus

10

हम एक प्रकार ऑपरेटर चाहते हैं Or[U,V]कि एक प्रकार पैरामीटर विवश करने के लिए इस्तेमाल किया जा सकता Xइस तरह से है कि या तो में X <: Uया X <: V। यहाँ एक परिभाषा है जो करीब आती है जितना हम प्राप्त कर सकते हैं:

trait Inv[-X]
type Or[U,T] = {
    type pf[X] = (Inv[U] with Inv[T]) <:< Inv[X]
}

यहाँ इसका उपयोग कैसे किया जाता है:

// use

class A; class B extends A; class C extends B

def foo[X : (B Or String)#pf] = {}

foo[B]      // OK
foo[C]      // OK
foo[String] // OK
foo[A]      // ERROR!
foo[Number] // ERROR!

यह कुछ स्काला टाइप ट्रिक्स का उपयोग करता है। मुख्य एक सामान्यीकृत प्रकार की बाधाओं का उपयोग है । दिए गए प्रकार Uऔर V, स्काला संकलक एक वर्ग प्रदान करता है, जिसे U <:< V(और उस वर्ग का एक अंतर्निहित वस्तु) कहा जाता है, यदि और केवल अगर स्काला संकलक साबित कर सकता है कि Uइसका उप-प्रकार है V। यहाँ सामान्यीकृत प्रकार की बाधाओं का उपयोग करके एक सरल उदाहरण दिया गया है जो कुछ मामलों के लिए काम करता है:

def foo[X](implicit ev : (B with String) <:< X) = {}

इस उदाहरण काम करता है जब Xवर्ग का एक उदाहरण Bहै, एक String, या एक प्रकार न तो एक महाप्रकार है और न ही की एक उप-प्रकार है कि है Bया String। पहले दो मामलों में, यह withकीवर्ड की परिभाषा के अनुसार सही है , (B with String) <: Bऔर (B with String) <: Stringइसलिए, स्काला एक अंतर्निहित वस्तु प्रदान करेगा जो इस प्रकार से पारित किया जाएगा ev: स्काला संकलक सही रूप से स्वीकार करेगा foo[B]औरfoo[String]

पिछले मामले में, मैं इस तथ्य पर भरोसा कर रहा हूं कि यदि U with V <: X, तब U <: Xया V <: X। यह सहज रूप से सच लगता है, और मैं बस इसे मान रहा हूं। यह इस धारणा से स्पष्ट है कि यह सरल उदाहरण तब विफल हो जाता है जब Xया तो Bया String: या इसके बाद के संस्करण के उदाहरण foo[A]में गलत रूप से स्वीकार किया जाता है और foo[C]गलत तरीके से अस्वीकार कर दिया जाता है। फिर से, हम जो चाहते हैं U, वह चर पर कुछ प्रकार की अभिव्यक्ति है V, और Xयह बिल्कुल सही है जब X <: Uया X <: V

यहाँ पर विरोधाभास की स्काला की धारणा मदद कर सकती है। लक्षण याद है trait Inv[-X]? क्योंकि यह अपने प्रकार के पैरामीटर में X, Inv[X] <: Inv[Y]यदि और केवल यदि है , तो इसके विपरीत है Y <: X। इसका मतलब है कि हम उपरोक्त उदाहरण को एक के साथ बदल सकते हैं जो वास्तव में काम करेगा:

trait Inv[-X]
def foo[X](implicit ev : (Inv[B] with Inv[String]) <:< Inv[X]) = {}

ऐसा इसलिए है क्योंकि अभिव्यक्ति (Inv[U] with Inv[V]) <: Inv[X]सही है, ऊपर की धारणा के अनुसार, बिल्कुल जब Inv[U] <: Inv[X]या Inv[V] <: Inv[X], और विरोधाभासी की परिभाषा से, यह वास्तव में सही है जब X <: Uया X <: V

यह संभव है कि एक पैरामीरिज़ेबल प्रकार की घोषणा BOrString[X]करके और इसे निम्नानुसार उपयोग करके चीजों को थोड़ा अधिक पुन: प्रयोज्य बनाया जाए :

trait Inv[-X]
type BOrString[X] = (Inv[B] with Inv[String]) <:< Inv[X]
def foo[X](implicit ev : BOrString[X]) = {}

स्काला अब BOrString[X]हर Xउस प्रकार के निर्माण का प्रयास करेगा fooजिसके साथ कहा जाता है, और प्रकार का निर्माण ठीक उसी समय किया जाएगा जब या Xतो इसका उप-प्रकार हो Bया String। यह काम करता है, और एक शॉर्टहैंड नोटेशन है। नीचे सिंटैक्स समतुल्य है (सिवाय इसके कि evअब इसे implicitly[BOrString[X]]केवल निकाय के बजाय विधि निकाय में संदर्भित किया जाना चाहिए ev) और BOrStringएक प्रकार के बाउंड के रूप में उपयोग करता है :

def foo[X : BOrString] = {}

हम वास्तव में जो चाहेंगे वह एक प्रकार का संदर्भ बनाने के लिए एक लचीला तरीका है। एक प्रकार का संदर्भ एक पैरामीरिजेबल प्रकार होना चाहिए, और हम एक पैरामीट्रिकबल को एक बनाने का तरीका चाहते हैं। ऐसा लगता है कि हम प्रकारों पर करी कार्यों को करने की कोशिश कर रहे हैं जैसे हम मूल्यों पर करी कार्यों को करते हैं। दूसरे शब्दों में, हम निम्नलिखित की तरह कुछ करना चाहते हैं:

type Or[U,T][X] = (Inv[U] with Inv[T]) <:< Inv[X]

यह सीधे स्काला में संभव नहीं है , लेकिन एक चाल है जिसे हम बहुत करीब लाने के लिए उपयोग कर सकते हैं। यह हमें Orउपरोक्त की परिभाषा में लाता है :

trait Inv[-X]
type Or[U,T] = {
    type pf[X] = (Inv[U] with Inv[T]) <:< Inv[X]
}

यहां हम संरचनात्मक टाइपिंग और स्काला के पाउंड ऑपरेटर का उपयोग करते हुए एक संरचनात्मक प्रकार बनाते हैं Or[U,T]जो एक आंतरिक प्रकार की गारंटी है। यह एक अजीब जानवर है। कुछ संदर्भ देने के लिए, फ़ंक्शन def bar[X <: { type Y = Int }](x : X) = {}को उन उपवर्गों के साथ बुलाया जाना चाहिए AnyRef, जिनमें एक प्रकार Yपरिभाषित है:

bar(new AnyRef{ type Y = Int }) // works!

पाउंड ऑपरेटर का उपयोग करने से हमें आंतरिक प्रकार को संदर्भित करने की अनुमति मिलती है Or[B, String]#pf, और टाइप ऑपरेटर के लिए infix संकेतन का उपयोग करते हुए Or, हम अपनी मूल परिभाषा में आते हैं foo:

def foo[X : (B Or String)#pf] = {}

हम इस तथ्य का उपयोग कर सकते हैं कि फ़ंक्शन प्रकार उनके पहले प्रकार के पैरामीटर के विपरीत हैं ताकि लक्षण को परिभाषित न किया जा सके Inv:

type Or[U,T] = {
    type pf[X] = ((U => _) with (T => _)) <:< (X => _)
} 

क्या इससे A|B <: A|B|Cसमस्या हल हो सकती है? stackoverflow.com/questions/45255270/… मैं नहीं बता सकता।
जेजेडस जूल

8

यह हैक भी है :

implicit val x: Int = 0
def foo(a: List[Int])(implicit ignore: Int) { }

implicit val y = ""
def foo(a: List[String])(implicit ignore: String) { }

foo(1::2::Nil)
foo("a"::"b"::Nil)

प्रकार के आस-पास अस्पष्टता (स्काला) के आसपास कार्य करना देखें ।


Stackoverflow.com/questions/3422336/… देखें । वास्तव में एक आसान हैक है: बस (implicit e: DummyImplicit)एक प्रकार के हस्ताक्षर में जोड़ें।
एरोन नोवस्त्रुप

7

आप मेटाकाला पर एक नज़र डाल सकते हैं , जिसमें कुछ कहा जाता है OneOf। मुझे लगता है कि यह matchबयानों के साथ अच्छी तरह से काम नहीं करता है, लेकिन आप उच्च-क्रम के कार्यों का उपयोग करके मिलान का अनुकरण कर सकते हैं। उदाहरण के लिए, इस स्निपेट पर एक नज़र डालें , लेकिन ध्यान दें कि "नकली मिलान" भाग पर टिप्पणी की गई है, हो सकता है कि यह अभी तक काफी काम नहीं करता है।

अब कुछ संपादकीय के लिए: मुझे नहीं लगता कि आपके द्वारा वर्णित के रूप में या तो 3, ई 4, आदि को परिभाषित करने के बारे में कुछ भी नहीं है। यह आवश्यक रूप से स्काला में निर्मित मानक 22 टपल प्रकारों के लिए दोहरी है। यह निश्चित रूप से अच्छा होगा यदि स्काला ने अंतर्निरोधी प्रकारों का निर्माण किया था, और शायद उनके लिए कुछ अच्छा वाक्यविन्यास {x, y, z}


6

मैं सोच रहा हूं कि वैकल्पिक उपप्रकारों के साथ प्रथम श्रेणी के डिसऑन टाइप प्रकार एक सील सुपरटेप है, और इन वैकल्पिक उपप्रकारों के लिए वांछित प्रकार के विच्छेदन से / के लिए अंतर्निहित रूपांतरण हैं।

मुझे लगता है कि यह मील्स सबिन के समाधान के 33 - 36 टिप्पणियों को संबोधित करता है , इसलिए प्रथम श्रेणी का प्रकार जिसे उपयोग स्थल पर नियोजित किया जा सकता है, लेकिन मैंने इसका परीक्षण नहीं किया।

sealed trait IntOrString
case class IntOfIntOrString( v:Int ) extends IntOrString
case class StringOfIntOrString( v:String ) extends IntOrString
implicit def IntToIntOfIntOrString( v:Int ) = new IntOfIntOrString(v)
implicit def StringToStringOfIntOrString( v:String ) = new StringOfIntOrString(v)

object Int {
   def unapply( t : IntOrString ) : Option[Int] = t match {
      case v : IntOfIntOrString => Some( v.v )
      case _ => None
   }
}

object String {
   def unapply( t : IntOrString ) : Option[String] = t match {
      case v : StringOfIntOrString => Some( v.v )
      case _ => None
   }
}

def size( t : IntOrString ) = t match {
    case Int(i) => i
    case String(s) => s.length
}

scala> size("test")
res0: Int = 4
scala> size(2)
res1: Int = 2

एक समस्या यह है कि स्कैला मिलान के संदर्भ में, (और से ) के IntOfIntOrStringलिए एक अंतर्निहित रूपांतरण के मामले में नियोजित नहीं करेगा , इसलिए निकालने वाले को परिभाषित करना चाहिए और इसके बजाय उपयोग करना चाहिए ।IntStringOfIntOrStringStringcase Int(i)case i : Int


ADD: मैंने अपने ब्लॉग पर मीलों सबिन का जवाब इस प्रकार दिया। शायद या तो कई सुधार हैं:

  1. यह उपयोग या परिभाषा साइट पर किसी भी अतिरिक्त शोर के बिना, 2 से अधिक प्रकारों तक फैली हुई है।
  2. तर्क को स्पष्ट रूप से बॉक्सिंग किया जाता है, जैसे कि जरूरत नहीं है size(Left(2))या size(Right("test"))
  3. पैटर्न मिलान का सिंटैक्स अनुमानित रूप से अनबॉक्स है।
  4. बॉक्सिंग और अनबॉक्सिंग को जेवीएम हॉटस्पॉट द्वारा अनुकूलित किया जा सकता है।
  5. वाक्यविन्यास भविष्य के प्रथम श्रेणी के यूनियन प्रकार द्वारा अपनाया जा सकता है, इसलिए प्रवास शायद सहज हो सकता है? शायद संघ प्रकार के नाम के लिए, इसके Vबजाय Or, उदाहरण के लिए IntVString, ` Int |v| String`, ` Int or String`, या मेरे पसंदीदा ` Int|String` का उपयोग करना बेहतर होगा ?

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

sealed trait `Int or String`
sealed trait `not an Int or String`
sealed trait `Int|String`[T,E]
case class `IntOf(Int|String)`( v:Int ) extends `Int|String`[Int,`Int or String`]
case class `StringOf(Int|String)`( v:String ) extends `Int|String`[String,`Int or String`]
case class `NotAn(Int|String)`[T]( v:T ) extends `Int|String`[T,`not an Int or String`]
implicit def `IntTo(IntOf(Int|String))`( v:Int ) = new `IntOf(Int|String)`(v)
implicit def `StringTo(StringOf(Int|String))`( v:String ) = new `StringOf(Int|String)`(v)
implicit def `AnyTo(NotAn(Int|String))`[T]( v:T ) = new `NotAn(Int|String)`[T](v)
def disjunction[T,E](x: `Int|String`[T,E])(implicit ev: E =:= `Int or String`) = x
def negationOfDisjunction[T,E](x: `Int|String`[T,E])(implicit ev: E =:= `not an Int or String`) = x

scala> disjunction(5)
res0: Int|String[Int,Int or String] = IntOf(Int|String)(5)

scala> disjunction("")
res1: Int|String[String,Int or String] = StringOf(Int|String)()

scala> disjunction(5.0)
error: could not find implicit value for parameter ev: =:=[not an Int or String,Int or String]
       disjunction(5.0)
                  ^

scala> negationOfDisjunction(5)
error: could not find implicit value for parameter ev: =:=[Int or String,not an Int or String]
       negationOfDisjunction(5)
                            ^

scala> negationOfDisjunction("")
error: could not find implicit value for parameter ev: =:=[Int or String,not an Int or String]
       negationOfDisjunction("")
                            ^
scala> negationOfDisjunction(5.0)
res5: Int|String[Double,not an Int or String] = NotAn(Int|String)(5.0)

उत्तर प्रदेश: माइल साबिन के समाधान के बारे में 23 और 35 टिप्पणियों के बारे में , यहाँ एक तरह से उपयोग स्थल पर संघ की घोषणा की गई है। ध्यान दें कि यह पहले स्तर के बाद अनबॉक्स है, अर्थात इसमें किसी भी प्रकार के किसी भी प्रकार के डिस्पेंस करने के लिए एक्स्टेंसिबल होने का फायदा है , जबकि Eitherनेस्टेड बॉक्सिंग की आवश्यकता है और मेरी पूर्व टिप्पणी में प्रतिमान 41 नहीं था। दूसरे शब्दों में, एक D[Int ∨ String]करने के लिए (यानी का एक उपप्रकार) के लिए उपलब्ध है D[Int ∨ String ∨ Double]

type ¬[A] = (() => A) => A
type[T, U] = ¬[T] with ¬[U]
class D[-A](v: A) {
  def get[T](f: (() => T)) = v match {
    case x : ¬[T] => x(f)
  }
}
def size(t: D[IntString]) = t match {
  case x: D[¬[Int]] => x.get( () => 0 )
  case x: D[¬[String]] => x.get( () => "" )
  case x: D[¬[Double]] => x.get( () => 0.0 )
}
implicit def neg[A](x: A) = new D[¬[A]]( (f: (() => A)) => x )

scala> size(5)
res0: Any = 5

scala> size("")
error: type mismatch;
 found   : java.lang.String("")
 required: D[?[Int,String]]
       size("")
            ^

scala> size("hi" : D[¬[String]])
res2: Any = hi

scala> size(5.0 : D[¬[Double]])
error: type mismatch;
 found   : D[(() => Double) => Double]
 required: D[?[Int,String]]
       size(5.0 : D[?[Double]])
                ^

जाहिर तौर पर स्काला कंपाइलर में तीन बग होते हैं।

  1. यह गंतव्य प्रकार की गड़बड़ी में पहले प्रकार के बाद किसी भी प्रकार के लिए सही निहित फ़ंक्शन का चयन नहीं करेगा।
  2. यह D[¬[Double]]मामले को मैच से बाहर नहीं करता है ।

3।

scala> class D[-A](v: A) {
  def get[T](f: (() => T))(implicit e: A <:< ¬[T]) = v match {
    case x : ¬[T] => x(f)
  }
}
error: contravariant type A occurs in covariant position in
       type <:<[A,(() => T) => T] of value e
         def get[T](f: (() => T))(implicit e: A <:< ?[T]) = v match {
                                           ^

प्राप्त विधि को इनपुट प्रकार पर ठीक से विवश नहीं किया जाता है, क्योंकि कंपाइलर Aसहसंयोजक स्थिति में अनुमति नहीं देगा । एक तर्क हो सकता है कि यह एक बग है क्योंकि हम चाहते हैं कि सभी सबूत हैं, हम कभी भी फ़ंक्शन में सबूत तक नहीं पहुंचते हैं। और मैं पसंद नहीं परीक्षण के लिए बनाया case _में getविधि, तो मैं एक Unbox नहीं करनी होगी Optionमें matchमें size()


05 मार्च, 2012: पूर्व अद्यतन में सुधार की आवश्यकता है। माइल्स सबिन के समाधान ने सबटाइपिंग के साथ सही ढंग से काम किया।

type ¬[A] = A => Nothing
type[T, U] = ¬[T] with ¬[U]
class Super
class Sub extends Super

scala> implicitly[(SuperString) <:< ¬[Super]]
res0: <:<[?[Super,String],(Super) => Nothing] = 

scala> implicitly[(SuperString) <:< ¬[Sub]]
res2: <:<[?[Super,String],(Sub) => Nothing] = 

scala> implicitly[(SuperString) <:< ¬[Any]]
error: could not find implicit value for parameter
       e: <:<[?[Super,String],(Any) => Nothing]
       implicitly[(Super ? String) <:< ?[Any]]
                 ^

मेरे पूर्व अद्यतन के प्रस्ताव (प्रथम श्रेणी के संघ प्रकार के पास) ने सबटाइपिंग को तोड़ दिया।

 scala> implicitly[D[¬[Sub]] <:< D[(SuperString)]]
error: could not find implicit value for parameter
       e: <:<[D[(() => Sub) => Sub],D[?[Super,String]]]
       implicitly[D[?[Sub]] <:< D[(Super ? String)]]
                 ^

समस्या यह है कि दोनों सहसंयोजक (वापसी प्रकार) और contravariant (फ़ंक्शन इनपुट, या इस मामले में फ़ंक्शन का रिटर्न मान जो फ़ंक्शन इनपुट) स्थिति Aमें (() => A) => Aप्रकट होता है, इस प्रकार प्रतिस्थापन केवल अपरिवर्तनीय हो सकते हैं।

ध्यान दें कि A => Nothingकेवल इसलिए आवश्यक है क्योंकि हम Aकंट्रावेरिएंट स्थिति में चाहते हैं , ताकि सुपरपाइप्स A न तो सबटिप हैं D[¬[A]]और न ही D[¬[A] with ¬[U]]( यह भी देखें )। चूँकि हमें केवल दोहरे विरोधाभास की आवश्यकता होती है, इसलिए हम मीलों के समाधान के समतुल्य प्राप्त कर सकते हैं, भले ही हम ¬और को त्याग सकें

trait D[-A]

scala> implicitly[D[D[Super]] <:< D[D[Super] with D[String]]]
res0: <:<[D[D[Super]],D[D[Super] with D[String]]] = 

scala> implicitly[D[D[Sub]] <:< D[D[Super] with D[String]]]
res1: <:<[D[D[Sub]],D[D[Super] with D[String]]] = 

scala> implicitly[D[D[Any]] <:< D[D[Super] with D[String]]]
error: could not find implicit value for parameter
       e: <:<[D[D[Any]],D[D[Super] with D[String]]]
       implicitly[D[D[Any]] <:< D[D[Super] with D[String]]]
                 ^

तो पूरा तय है।

class D[-A] (v: A) {
  def get[T <: A] = v match {
    case x: T => x
  }
}

implicit def neg[A](x: A) = new D[D[A]]( new D[A](x) )

def size(t: D[D[Int] with D[String]]) = t match {
  case x: D[D[Int]] => x.get[D[Int]].get[Int]
  case x: D[D[String]] => x.get[D[String]].get[String]
  case x: D[D[Double]] => x.get[D[Double]].get[Double]
}

ध्यान दें कि स्केला में पहले के 2 कीड़े बने रहते हैं, लेकिन तीसरे को टाला जाता Tहै क्योंकि अब उप-प्रकार होने के लिए विवश है A

हम उप-निर्माण कार्यों की पुष्टि कर सकते हैं।

def size(t: D[D[Super] with D[String]]) = t match {
  case x: D[D[Super]] => x.get[D[Super]].get[Super]
  case x: D[D[String]] => x.get[D[String]].get[String]
}

scala> size( new Super )
res7: Any = Super@1272e52

scala> size( new Sub )
res8: Any = Sub@1d941d7

मैं सोच कर दिया गया है कि प्रथम श्रेणी के चौराहे प्रकार बहुत महत्वपूर्ण हैं, के लिए दोनों कारणों सीलोन उन्हें है , और क्योंकि बजाय subsuming को Anyजो साधन एक साथ unboxing matchउम्मीद प्रकार पर एक रनटाइम त्रुटि उत्पन्न कर सकते हैं, एक (के unboxing विषम संग्रह युक्त a) डिस्जंक्शन को चेक किया जा सकता है (Scala को मेरे द्वारा नोट किए गए बग्स को ठीक करना है)। यूनियन की तुलना में अधिक स्पष्ट हैं का उपयोग कर की जटिलता प्रयोगात्मक HList की metascala विषम संग्रह के लिए।


ऊपर # 3 आइटम स्केल कंपाइलर में बग नहीं है । नोट मैं मूल रूप से इसे बग के रूप में नहीं गिना था, फिर लापरवाही से आज एक संपादन किया और ऐसा किया (यह बताते हुए कि यह एक बग नहीं था मेरे मूल कारण को भूलकर)। मैंने पोस्ट को फिर से संपादित नहीं किया, क्योंकि मैं 7 संपादन सीमा पर हूं।
शेल्बी मूर तृतीय


# 2 आइटम बग नहीं है। स्काला पूरी तरह से एक संघ प्रकार व्यक्त नहीं कर सकता है । लिंक किए गए दस्तावेज़ कोड का एक और संस्करण प्रदान करते हैं, जिससे sizeअब D[Any]इनपुट के रूप में स्वीकार नहीं किया जाता है।
शेल्बी मूर III

मुझे यह उत्तर नहीं मिला, क्या यह इस सवाल का जवाब है: stackoverflow.com/questions/45255270/…
jhegedus

5

एक और तरीका है जो समझने में थोड़ा आसान है, अगर आप करी-हावर्ड को नहीं करते हैं:

type v[A,B] = Either[Option[A], Option[B]]

private def L[A,B](a: A): v[A,B] = Left(Some(a))
private def R[A,B](b: B): v[A,B] = Right(Some(b))  
// TODO: for more use scala macro to generate this for up to 22 types?
implicit def a2[A,B](a: A): v[A,B] = L(a)
implicit def b2[A,B](b: B): v[A,B] = R(b)
implicit def a3[A,B,C](a: A): v[v[A,B],C] = L(a2(a))
implicit def b3[A,B,C](b: B): v[v[A,B],C] = L(b2(b))
implicit def a4[A,B,C,D](a: A): v[v[v[A,B],C],D] = L(a3(a))
implicit def b4[A,B,C,D](b: B): v[v[v[A,B],C],D] = L(b3(b))    
implicit def a5[A,B,C,D,E](a: A): v[v[v[v[A,B],C],D],E] = L(a4(a))
implicit def b5[A,B,C,D,E](b: B): v[v[v[v[A,B],C],D],E] = L(b4(b))

type JsonPrimtives = (String v Int v Double)
type ValidJsonPrimitive[A] = A => JsonPrimtives

def test[A : ValidJsonPrimitive](x: A): A = x 

test("hi")
test(9)
// test(true)   // does not compile

मैं डिजन में इसी तरह की तकनीक का उपयोग करता हूं


क्या यह सबटाइपिंग के साथ काम कर सकता है? मेरी आंत महसूस: नहीं, लेकिन मैं गलत हो सकता है। stackoverflow.com/questions/45255270/…
jhegedus

1

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


5
हालांकि मैं कुछ समय के लिए स्काला का उपयोग कर रहा हूं, मैं न तो उतना जानकार हूं और ना ही उतना स्मार्ट हूं जितना आप सोचते हैं। इस उदाहरण में, मैं देख सकता हूँ कि कैसे एक पुस्तकालय समाधान प्रदान कर सकता है। यह समझ में आता है कि क्या ऐसा पुस्तकालय मौजूद है (या कुछ वैकल्पिक)।
आरोन नोवस्त्रुप

1

यहां पहले से ही शानदार उत्तरों को जोड़ना। यहाँ एक सार है जो मील्स सबिन संघ प्रकारों (और जोश के विचारों) पर निर्मित होता है, लेकिन उन्हें पुनरावर्ती रूप से परिभाषित भी करता है, इसलिए आपके पास संघ में 2 प्रकार हो सकते हैं ( def foo[A : UNil Or Int Or String Or List[String])

https://gist.github.com/aishfenton/2bb3bfa12e0321acfc904a71dda9bfbb

एनबी: मुझे यह जोड़ना चाहिए कि एक परियोजना के लिए उपरोक्त के साथ खेलने के बाद, मैंने सादे-पुराने-योग-प्रकारों (यानी उपवर्गों के साथ सीलबंद विशेषता) को वापस जाना समाप्त कर दिया। माइल्स सबिन यूनियन प्रकार प्रकार पैरामीटर को सीमित करने के लिए बहुत अच्छे हैं, लेकिन अगर आपको यूनियन प्रकार को वापस करने की आवश्यकता है, तो यह बहुत अधिक पेशकश नहीं करता है।


क्या यह A|C <: A|B|Cसबटाइपिंग समस्या को हल कर सकता है? stackoverflow.com/questions/45255270/… मेरी आंत महसूस नहीं कर रही है क्योंकि तब इसका मतलब होगा कि A or Cइसके उपप्रकार की आवश्यकता होगी, (A or B) or Cलेकिन इसमें वह प्रकार A or Cनहीं है जिससे इस एन्कोडिंग के साथ कम से कम A or Cएक उपप्रकार बनाने की कोई उम्मीद न हो A or B or C.. । तुम क्या सोचते हो ?
जेजेडस जूल

0

से डॉक्स , के अलावा के साथ sealed:

sealed class Expr
case class Var   (x: String)          extends Expr
case class Apply (f: Expr, e: Expr)   extends Expr
case class Lambda(x: String, e: Expr) extends Expr

sealedभाग के बारे में :

आगे के मामले की कक्षाओं को परिभाषित करना संभव है जो प्रोग्राम के अन्य हिस्सों (...) में Expr टाइप करें। एक्स्टेंसिबिलिटी के इस रूप को बेस क्लास एक्स्प्र सील्ड घोषित करके बाहर रखा जा सकता है; इस स्थिति में, Expr का विस्तार करने वाली सभी कक्षाएं Expr के समान स्रोत फ़ाइल में होनी चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.