क्या इतिहासकार तुच्छ लेखन के जटिल तरीके से ज्यादा कुछ नहीं हैं?


144

मैं वास्तव में यह जानने में दिलचस्पी रखता हूं कि मतभेद कहां हैं, और अधिक आम तौर पर, विहित उपयोग के मामलों की पहचान करने के लिए जहां HLists का उपयोग नहीं किया जा सकता है (या बल्कि, नियमित सूचियों पर कोई लाभ नहीं मिलता है)।

(मुझे पता है कि TupleNस्काला में 22 (मुझे विश्वास है) हैं , जबकि किसी को केवल एक ही एचएलिस्ट की जरूरत है, लेकिन यह वैचारिक अंतर नहीं है, जिसमें मुझे दिलचस्पी है।)

मैंने नीचे पाठ में कुछ प्रश्नों को चिह्नित किया है। यह वास्तव में उन्हें जवाब देने के लिए आवश्यक नहीं हो सकता है, वे उन चीजों को इंगित करने के लिए अधिक हैं जो मेरे लिए अस्पष्ट हैं, और कुछ दिशाओं में चर्चा का मार्गदर्शन करने के लिए।

प्रेरणा

मैं हाल ही में (के रूप में द्वारा प्रदान की, उदाहरण के लिए, जहां लोगों को HLists उपयोग करने के लिए सुझाव दिया इतने पर जवाब के एक जोड़े को देखा है निराकार में किसी हटाए गए जवाब भी शामिल है), इस सवाल का । इसने इस चर्चा को जन्म दिया , जिसने बदले में इस प्रश्न को जन्म दिया।

पहचान

यह मुझे लगता है, कि hlists केवल तब उपयोगी होते हैं जब आप तत्वों की संख्या और उनके सटीक प्रकारों को सांख्यिकीय रूप से जानते हैं। संख्या वास्तव में महत्वपूर्ण नहीं है, लेकिन यह संभावना नहीं लगती है कि आपको कभी भी अलग-अलग लेकिन सांख्यिकीय रूप से ज्ञात प्रकारों के तत्वों के साथ एक सूची तैयार करने की आवश्यकता है, लेकिन यह कि आप सांख्यिकीय रूप से उनकी संख्या नहीं जानते हैं। प्रश्न 1: क्या आप ऐसा उदाहरण भी लिख सकते हैं, उदाहरण के लिए, लूप में? मेरा अभिप्राय यह है कि एक अज्ञात रूप से अज्ञात संख्या के साथ मनमाने ढंग से सटीक सूची होना (किसी दिए गए वर्ग पदानुक्रम के लिए मनमाना सापेक्ष) केवल संगत नहीं है।

HLists बनाम ट्यूपल्स

यदि यह सच है, अर्थात, आप स्टेटिक रूप से संख्या और प्रकार जानते हैं - प्रश्न 2: क्यों न केवल n-tuple का उपयोग करें? निश्चित रूप से, आप टाइप कर सकते हैं मानचित्र और एक HList पर मोड़ो (जो आप भी कर सकते हैं, लेकिन प्रकारों पर नहीं , इसकी सहायता से एक टपल पर productIterator), लेकिन चूंकि संख्या और प्रकार के तत्व सांख्यिकीय रूप से ज्ञात हैं, आप शायद केवल टपल तत्वों का उपयोग कर सकते हैं सीधे और संचालन करते हैं।

दूसरी ओर, यदि fआप जिस कार्य को किसी सूची में रखते हैं, तो वह इतना सामान्य होता है कि वह सभी तत्वों को स्वीकार कर लेता है - प्रश्न 3: इसका उपयोग क्यों नहीं करते productIterator.map? ठीक है, एक दिलचस्प अंतर विधि ओवरलोडिंग से आ सकता है: यदि हमारे पास कई अतिभारित हैं f, तो हिस्टल द्वारा प्रदान की जाने वाली मजबूत प्रकार की जानकारी (उत्पादक के विपरीत) कंपाइलर को अधिक विशिष्ट चुनने की अनुमति दे सकती है f। हालांकि, मुझे यकीन नहीं है कि अगर वास्तव में स्काला में काम किया जाएगा, क्योंकि विधियां और कार्य समान नहीं हैं।

HLists और उपयोगकर्ता इनपुट

एक ही धारणा पर निर्माण, अर्थात्, आपको संख्या और तत्वों के प्रकार को सांख्यिकीय रूप से जानने की आवश्यकता है - प्रश्न 4: क्या hlists का उपयोग उन स्थितियों में किया जा सकता है जहां तत्व किसी भी प्रकार के उपयोगकर्ता सहभागिता पर निर्भर करते हैं? उदाहरण के लिए, एक पाश के अंदर तत्वों के साथ एक सूची को आबाद करने की कल्पना करें; तत्वों को कहीं से पढ़ा जाता है (यूआई, कॉन्फ़िगर फ़ाइल, अभिनेता इंटरैक्शन, नेटवर्क) जब तक कि एक निश्चित स्थिति नहीं होती है। हिटलर का प्रकार क्या होगा? इंटरफ़ेस विनिर्देशन के लिए समान है getElements: HList [...] जो सांख्यिकीय रूप से अज्ञात लंबाई की सूचियों के साथ काम करना चाहिए, और जो कि सिस्टम A में घटक A को घटक B से ऐसे मनमाने तत्वों की सूची प्राप्त करने की अनुमति देता है।

जवाबों:


144

एक से तीन तक के प्रश्नों को संबोधित करना: मुख्य अनुप्रयोगों में से एक है, HListsआरीति पर अमूर्त करना। एअर्ट को आमतौर पर एक अमूर्त के किसी भी उपयोग साइट पर सांख्यिकीय रूप से जाना जाता है, लेकिन साइट से साइट पर भिन्न होता है। इसे आकारहीन उदाहरणों से लें ,

def flatten[T <: Product, L <: HList](t : T)
  (implicit hl : HListerAux[T, L], flatten : Flatten[L]) : flatten.Out =
    flatten(hl(t))

val t1 = (1, ((2, 3), 4))
val f1 = flatten(t1)     // Inferred type is Int :: Int :: Int :: Int :: HNil
val l1 = f1.toList       // Inferred type is List[Int]

val t2 = (23, ((true, 2.0, "foo"), "bar"), (13, false))
val f2 = flatten(t2)
val t2b = f2.tupled
// Inferred type of t2b is (Int, Boolean, Double, String, String, Int, Boolean)

का उपयोग किए बिना HLists(या कुछ समतुल्य) टपल तर्कों के तर्क पर अमूर्त flattenकरने के लिए एक भी कार्यान्वयन के लिए असंभव होगा जो इन दो बहुत अलग आकृतियों के तर्कों को स्वीकार कर सकता है और उन्हें एक सुरक्षित तरीके से बदल सकता है।

समता पर अमूर्तता की क्षमता कहीं भी ब्याज की होने की संभावना है जो कि निश्चित आकृतियाँ शामिल हैं: साथ ही साथ ट्यूपल्स, ऊपर, जिसमें विधि / फ़ंक्शन पैरामीटर सूचियाँ और केस क्लासेस शामिल हैं। उदाहरणों के लिए यहाँ देखें कि हम कैसे प्रकार के वर्ग उदाहरण प्राप्त करने के लिए मनमाने ढंग से केस कक्षाओं की समता पर अमूर्त हो सकते हैं,

// A pair of arbitrary case classes
case class Foo(i : Int, s : String)
case class Bar(b : Boolean, s : String, d : Double)

// Publish their `HListIso`'s
implicit def fooIso = Iso.hlist(Foo.apply _, Foo.unapply _)
implicit def barIso = Iso.hlist(Bar.apply _, Bar.unapply _)

// And now they're monoids ...

implicitly[Monoid[Foo]]
val f = Foo(13, "foo") |+| Foo(23, "bar")
assert(f == Foo(36, "foobar"))

implicitly[Monoid[Bar]]
val b = Bar(true, "foo", 1.0) |+| Bar(false, "bar", 3.0)
assert(b == Bar(true, "foobar", 4.0))

यहां कोई रनटाइम चलना नहीं है , लेकिन दोहराव है , जिसे HLists(या समकक्ष संरचनाओं) का उपयोग समाप्त कर सकता है। बेशक, अगर दोहराए जाने वाले बॉयलरप्लेट के लिए आपकी सहिष्णुता अधिक है, तो आप प्रत्येक और हर आकार के लिए कई कार्यान्वयन लिखकर एक ही परिणाम प्राप्त कर सकते हैं, जिसकी आपको परवाह है।

प्रश्न तीन में आप पूछते हैं "... यदि फ़ंक्शन च आप किसी चित्र पर मैप करते हैं तो यह इतना सामान्य है कि यह सभी तत्वों को स्वीकार करता है ... क्यों नहीं इसका उपयोग productIterator.map?" के माध्यम से करें। यदि आप किसी एचएलस्ट से अधिक का फंक्शन मैप करते हैं, Any => Tतो फॉर्म की मैपिंग productIteratorपूरी तरह से अच्छी तरह से हो जाएगी। लेकिन फार्म के कार्य Any => Tआम तौर पर दिलचस्प नहीं होते हैं (कम से कम, वे तब तक नहीं होते हैं जब तक वे आंतरिक रूप से नहीं डाले जाते हैं)। आकारहीन बहुरूपता फ़ंक्शन मान का एक रूप प्रदान करता है जो कंपाइलर को उस प्रकार के विशिष्ट मामलों का चयन करने की अनुमति देता है जिस तरह से आप संदिग्ध हैं। उदाहरण के लिए,

// size is a function from values of arbitrary type to a 'size' which is
// defined via type specific cases
object size extends Poly1 {
  implicit def default[T] = at[T](t => 1)
  implicit def caseString = at[String](_.length)
  implicit def caseList[T] = at[List[T]](_.length)
}

scala> val l = 23 :: "foo" :: List('a', 'b') :: true :: HNil
l: Int :: String :: List[Char] :: Boolean :: HNil =
  23 :: foo :: List(a, b) :: true :: HNil

scala> (l map size).toList
res1: List[Int] = List(1, 3, 2, 1)

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

trait Fruit
case class Apple() extends Fruit
case class Pear() extends Fruit

type FFFF = Fruit :: Fruit :: Fruit :: Fruit :: HNil
type APAP = Apple :: Pear :: Apple :: Pear :: HNil

val a : Apple = Apple()
val p : Pear = Pear()

val l = List(a, p, a, p) // Inferred type is List[Fruit]

सूची का प्रकार lसूची की लंबाई या उसके तत्वों के सटीक प्रकारों को कैप्चर नहीं करता है। हालाँकि, अगर हम यह उम्मीद करते हैं कि यह एक विशिष्ट रूप है (यानी यदि यह किसी ज्ञात, निश्चित स्कीमा के अनुरूप होना चाहिए), तो हम उस तथ्य को स्थापित करने और उसके अनुसार कार्य करने का प्रयास कर सकते हैं,

scala> import Traversables._
import Traversables._

scala> val apap = l.toHList[Apple :: Pear :: Apple :: Pear :: HNil]
res0: Option[Apple :: Pear :: Apple :: Pear :: HNil] =
  Some(Apple() :: Pear() :: Apple() :: Pear() :: HNil)

scala> apap.map(_.tail.head)
res1: Option[Pear] = Some(Pear())

ऐसी अन्य स्थितियाँ हैं जहाँ हम किसी दी गई सूची की वास्तविक लंबाई के बारे में परवाह नहीं कर सकते हैं, इसके अलावा यह किसी अन्य सूची की समान लंबाई है। फिर, यह कुछ ऐसा है जो आकारहीन समर्थन करता है, दोनों पूरी तरह से सांख्यिकीय रूप से, और ऊपर के रूप में मिश्रित स्थिर / गतिशील संदर्भ में भी। एक विस्तारित उदाहरण के लिए यहां देखें ।

यह सच है, जैसा कि आप निरीक्षण करते हैं, कि इन सभी तंत्रों को स्थिर प्रकार की जानकारी उपलब्ध होने की आवश्यकता है, कम से कम सशर्त रूप से, और यह कि इन तकनीकों को पूरी तरह से गतिशील वातावरण में प्रयोग करने योग्य होने से बाहर रखा जाएगा, पूरी तरह से बाहरी डेटा प्रदान किए गए। लेकिन 2.10 में स्काला प्रतिबिंब के एक घटक के रूप में रनटाइम संकलन के लिए समर्थन के आगमन के साथ, यहां तक ​​कि यह अब एक अड़ियल बाधा नहीं है ... हम रनिंग संकलन का उपयोग हल्के मंचन के रूप में कर सकते हैं और हमारी स्थैतिक टाइपिंग रनटाइम के लिए की जाती है। गतिशील डेटा के जवाब में: नीचे दिए गए अंश से अंश ... पूर्ण उदाहरण के लिए लिंक का अनुसरण करें,

val t1 : (Any, Any) = (23, "foo") // Specific element types erased
val t2 : (Any, Any) = (true, 2.0) // Specific element types erased

// Type class instances selected on static type at runtime!
val c1 = stagedConsumeTuple(t1) // Uses intString instance
assert(c1 == "23foo")

val c2 = stagedConsumeTuple(t2) // Uses booleanDouble instance
assert(c2 == "+2.0")

मुझे यकीन है कि @PLT_Borat कुछ है कि के बारे में कहना होगा, यह देखते हुए उसकी dependently टाइप किया प्रोग्रामिंग भाषाओं के बारे में ऋषि टिप्पणी ;-)


2
मैं आपके उत्तर के अंतिम भाग से थोड़ा हैरान हूँ - लेकिन बहुत ही गहन! आपके महान जवाब और कई संदर्भों के लिए धन्यवाद, ऐसा लगता है जैसे मुझे पढ़ने के लिए बहुत कुछ मिला :-)
माल्टे श्वेरहॉफ़

1
एरीटी पर सार बेहद उपयोगी है। ScalaMock, दुख की बात है, काफी दोहराव से ग्रस्त है क्योंकि विभिन्न FunctionNलक्षण नहीं जानते कि कैसे सार पर सार है: github.com/paulbutcher/ScalaMock/blob/develop/core/src/main/… github.com/paulbutcher/ScalaMock/blob / विकास / कोर / src / main / ... अफसोस की बात है कि मैं किसी भी तरह से अवगत नहीं हूँ कि मैं इससे बचने के लिए Shapeless का उपयोग कर सकता हूं, यह देखते हुए कि मुझे "वास्तविक" FunctionNs से निपटने की आवश्यकता है
पॉल बुचर

1
मैंने एक (बहुत ही कृत्रिम) उदाहरण बनाया - ideone.com/sxIw1 -, जो प्रश्न एक की तर्ज पर है। क्या hlists से यह लाभ हो सकता है, शायद "गतिशील डेटा के जवाब में रनटाइम पर किए गए स्थिर टाइपिंग" के संयोजन में? (मैं अभी भी अनिश्चित हूं कि बाद की बात क्या है)
माल्टे श्वार्फ

17

बस स्पष्ट होने के लिए, एक एचएलस्ट अनिवार्य रूप से Tuple2शीर्ष पर थोड़ा अलग चीनी के साथ ढेर से ज्यादा कुछ नहीं है ।

def hcons[A,B](head : A, tail : B) = (a,b)
def hnil = Unit

hcons("foo", hcons(3, hnil)) : (String, (Int, Unit))

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


tuples को hlists और किसी भी तरह वापस मैप किया जा सकता है, इसलिए एक स्पष्ट समरूपता है।
एरिक कापलुन

10

बहुत सारी चीज़ें हैं जो आप (अच्छी तरह से) टुपल्स के साथ नहीं कर सकते हैं:

  • एक सामान्य प्रीपेंड / एपेंड फ़ंक्शन लिखें
  • रिवर्स फ़ंक्शन लिखें
  • एक समारोह समारोह लिखें
  • ...

आप वह सब कर सकते हैं, जो सामान्य तौर पर नहीं, बल्कि सामान्य रूप से किया जा सकता है। इसलिए HLists का उपयोग करने से आपका कोड अधिक DRY हो जाता है।


8

मैं इसे सुपर सरल भाषा में समझा सकता हूं:

टपल बनाम सूची नामकरण महत्वपूर्ण नहीं है। HList को HTuples नाम दिया जा सकता है। अंतर यह है कि स्काला + हास्केल में, आप इसे एक ट्यूपल (स्काला सिंटैक्स का उपयोग करके) कर सकते हैं:

def append2[A,B,C](in: (A,B), v: C) : (A,B,C) = (in._1, in._2, v)

किसी भी प्रकार के दो तत्वों के एक इनपुट टपल को लेने के लिए, एक तीसरे तत्व को संलग्न करें, और ठीक तीन तत्वों के साथ पूरी तरह से टाइप किया हुआ टपल लौटाएं। लेकिन जब यह पूरी तरह से सामान्य प्रकार पर होता है, तो इसे इनपुट / आउटपुट लंबाई को स्पष्ट रूप से निर्दिष्ट करना होगा।

एक हास्केल शैली एचएलस्ट आपको क्या करने देती है यह सामान्य लंबाई से अधिक है, इसलिए आप किसी भी लम्बाई / सूची में संलग्न हो सकते हैं और पूरी तरह से वैधानिक रूप से टाइप किए गए ट्यूपल / सूची को वापस प्राप्त कर सकते हैं। यह लाभ समरूप रूप से टाइप किए गए संग्रहों पर भी लागू होता है, जहाँ आप एक इंट को बिल्कुल n n की सूची में जोड़ सकते हैं और एक सूची वापस प्राप्त कर सकते हैं, जो स्पष्ट रूप से n निर्दिष्ट किए बिना ठीक (n + 1) ints में टाइप की गई है।

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