2015-11-05 5 views
5

स्काला में पर काम कर के साथ उत्पाद के प्रकार को लागू स्काला में, मैं एक उत्पाद के प्रकार & कि एक यौगिक मूल्य का प्रतिनिधित्व करता, उदा .:सामान्य अद्यतन समारोह अपने हिस्से

val and: String & Int & User & ... = ??? 

यानी बनाने की जरूरत and में String भाग और Int भाग और User भागों के अंदर होना चाहिए। यह स्काला with कीवर्ड के समान है:

val and: String with Int with User with ... = ??? 

होने ऐसे उत्पाद प्रकार मैं करने के लिए कोई तरीका होना चाहिए, एक समारोह A => A, कुछ उत्पाद मूल्य के लिए इसे लागू करते हैं और उस उत्पाद A हिस्सा बदल के साथ वापस मिलता है। इसका तात्पर्य है कि उत्पाद में प्रत्येक प्रकार अद्वितीय होना चाहिए - यह स्वीकार्य है।

एक महत्वपूर्ण सीमा यह है कि, उत्पाद को A => A पर लागू करने पर, मुझे केवल इतना पता है कि उत्पाद में कहीं भी A है लेकिन इसमें अन्य प्रकारों के बारे में कोई जानकारी नहीं है। लेकिन फ़ंक्शन के कॉलर के रूप में, मैं इसे पूर्ण प्रकार की जानकारी वाले उत्पाद को पास करता हूं और फ़ंक्शन हस्ताक्षर के हिस्से के रूप में इस पूर्ण प्रकार को वापस पाने की अपेक्षा करता हूं।

छद्म कोड में:

def update[A, Rest](product: A & Rest, f: A => A): A & Rest 

निराकार या अन्य गूढ़ सामान का उपयोग करना मेरे लिए ठीक है। मैंने HList एस का उपयोग करने की कोशिश की लेकिन उन्हें आदेश दिया गया है, जबकि विषम सेट की तरह कुछ A & Rest भाग को दोबारा भेजने के लिए यहां अधिक उपयुक्त होगा।

अद्यतन:

object product { 

    /** Product of `left` and `right` values. */ 
    case class &[L, R](left: L, right: R) 

    implicit class AndPimp[L](val left: L) extends AnyVal { 
    /** Make a product of `this` (as left) and `right`. */ 
    def &[R](right: R): L & R = new &(left, right) 
    } 

    /* Updater. */ 

    /** Product updater able to update value of type `A`. */ 
    trait ProductUpdater[P, A] { 
    /** Update product value of type `A`. 
     * @return updated product */ 
    def update(product: P, f: A ⇒ A): P 
    } 

    trait LowPriorityProductUpdater { 
    /** Non-product value updater. */ 
    implicit def valueUpdater[A]: ProductUpdater[A, A] = new ProductUpdater[A, A] { 
     override def update(product: A, f: A ⇒ A): A = f(product) 
    } 
    } 

    object ProductUpdater extends LowPriorityProductUpdater { 
    /** Left-biased product value updater. */ 
    implicit def leftProductUpdater[L, R, A](implicit leftUpdater: ProductUpdater[L, A]): ProductUpdater[L & R, A] = 
     new ProductUpdater[L & R, A] { 
     override def update(product: L & R, f: A ⇒ A): L & R = 
      leftUpdater.update(product.left, f) & product.right 
     } 

    /** Right-biased product value updater. */ 
    implicit def rightProductUpdater[L, R, A](implicit rightUpdater: ProductUpdater[R, A]): ProductUpdater[L & R, A] = 
     new ProductUpdater[L & R, A] { 
     override def update(product: L & R, f: A ⇒ A): L & R = 
      product.left & rightUpdater.update(product.right, f) 
     } 
    } 

    /** Update product value of type `A` with function `f`. 
    * Won't compile if product contains multiple `A` values. 
    * @return updated product */ 
    def update[P, A](product: P)(f: A ⇒ A)(implicit updater: ProductUpdater[P, A]): P = 
    updater.update(product, f) 

    /* Reader. */ 

    /** Product reader able to read value of type `A`. */ 
    trait ProductReader[P, A] { 
    /** Read product value of type `A`. */ 
    def read(product: P): A 
    } 

    trait LowPriorityProductReader { 
    /** Non-product value reader. */ 
    implicit def valueReader[A]: ProductReader[A, A] = new ProductReader[A, A] { 
     override def read(product: A): A = product 
    } 
    } 

    object ProductReader extends LowPriorityProductReader { 
    /** Left-biased product value reader. */ 
    implicit def leftProductReader[L, R, A](implicit leftReader: ProductReader[L, A]): ProductReader[L & R, A] = 
     new ProductReader[L & R, A] { 
     override def read(product: L & R): A = 
      leftReader.read(product.left) 
     } 

    /** Right-biased product value reader. */ 
    implicit def rightProductReader[L, R, A](implicit rightReader: ProductReader[R, A]): ProductReader[L & R, A] = 
     new ProductReader[L & R, A] { 
     override def read(product: L & R): A = 
      rightReader.read(product.right) 
     } 
    } 

    /** Read product value of type `A`. 
    * Won't compile if product contains multiple `A` values. 
    * @return value of type `A` */ 
    def read[P, A](product: P)(implicit productReader: ProductReader[P, A]): A = 
    productReader.read(product) 

    // let's test it 

    val p = 1 & 2.0 & "three" 

    read[Int & Double & String, Int](p) // 1 
    read[Int & Double & String, Double](p) // 2.0 
    read[Int & Double & String, String](p) // three 

    update[Int & Double & String, Int](p)(_ * 2) // 2 & 2.0 & three 
    update[Int & Double & String, Double](p)(_ * 2) // 1 & 4.0 & three 
    update[Int & Double & String, String](p)(_ * 2) // 1 & 2.0 & threethree 

} 
+1

बारे में 'HList' ordere किया जा रहा है डी: यह कभी तय नहीं किया जा सकता है। जबकि आप विधियों/प्रकारों को परिभाषित कर सकते हैं जो दो उत्पादों की तुलना करेंगे और आपको बताएंगे कि ऑर्डरिंग के बावजूद उनके पास एक ही प्रकार है, जब प्रति प्रकार की प्रणाली की बात आती है तो आप भाग्य से बाहर हैं। आप हर जादू को आजमा सकते हैं, आप कभी भी कंपाइलर को यह सोचने में सक्षम नहीं होंगे कि '& [Int, String] '' और [स्ट्रिंग, Int]' जैसा ही है (एक कंपाइलर प्लगइन को लागू करने से कम जो पूरी तरह से खत्म हो जाएगा उन प्रकारों के लिए प्रकार की जांच, यदि यह भी संभव है)। –

+0

@ रेजीज-गिल्स, इसे मिला, धन्यवाद। – Tvaroh

उत्तर

4

यहाँ एक समाधान नहीं आवश्यक पुस्तकालय के साथ केवल शुद्ध स्केला का उपयोग कर रहा है। यह एक नहीं बल्कि मानक दृष्टिकोण का उपयोग कर एक प्रकार वर्ग पर निर्भर करता है:

scala> :paste 
// Entering paste mode (ctrl-D to finish) 
case class &[L,R](left: L, right: R) 
implicit class AndOp[L](val left: L) { 
    def &[R](right: R): L & R = new &(left, right) 
} 

trait ProductUpdater[P,A] { 
    def apply(p: P, f: A => A): P 
} 
trait LowPriorityProductUpdater { 
    implicit def noopValueUpdater[P,A]: ProductUpdater[P,A] = { 
    new ProductUpdater[P,A] { 
     def apply(p: P, f: A => A): P = p // keep as is 
    } 
    } 
} 
object ProductUpdater extends LowPriorityProductUpdater { 
    implicit def simpleValueUpdater[A]: ProductUpdater[A,A] = { 
    new ProductUpdater[A,A] { 
     def apply(p: A, f: A => A): A = f(p) 
    } 
    } 
    implicit def productUpdater[L, R, A](
    implicit leftUpdater: ProductUpdater[L, A], rightUpdater: ProductUpdater[R, A] 
): ProductUpdater[L & R, A] = { 
    new ProductUpdater[L & R, A] { 
     def apply(p: L & R, f: A => A): L & R = &(leftUpdater(p.left, f), rightUpdater(p.right, f)) 
    } 
    } 
} 
def update[A,P](product: P)(f: A => A)(implicit updater: ProductUpdater[P,A]): P = updater(product, f) 
// Exiting paste mode, now interpreting. 

के परीक्षण करते हैं:

scala> case class User(name: String, age: Int) 
defined class User 

scala> val p: String & Int & User & String = "hello" & 123 & User("Elwood", 25) & "bye" 
p: &[&[&[String,Int],User],String] = &(&(&(hello,123),User(Elwood,25)),bye) 

scala> update(p){ i: Int => i + 1 } 
res0: &[&[&[String,Int],User],String] = &(&(&(hello,124),User(Elwood,25)),bye) 

scala> update(p){ s: String => s.toUpperCase } 
res1: &[&[&[String,Int],User],String] = &(&(&(HELLO,123),User(Elwood,25)),BYE) 

scala> update(p){ user: User => 
    | user.copy(name = user.name.toUpperCase, age = user.age*2) 
    | } 
res2: &[&[&[String,Int],User],String] = &(&(&(hello,123),User(ELWOOD,50)),bye) 

अद्यतन: जवाब में करने के लिए:

क्या यह संभव है यह संकलित नहीं करने के लिए जब किसी उत्पाद में

अपडेट करने के लिए कोई मान नहीं है

हाँ, यह सबसे निश्चित रूप से संभव है। हम ProductUpdater प्रकार वर्ग बदल सकता है लेकिन इस मामले में मैं इसे बहुत आसान एक सबूत है कि किसी दिए गए उत्पाद P प्रकार A कम से कम एक तत्व शामिल के रूप में एक अलग प्रकार वर्ग ProductContainsType लागू करने के लिए लगता है: अब हम कर सकते हैं

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

@annotation.implicitNotFound("Product ${P} does not contain type ${A}") 
abstract sealed class ProductContainsType[P,A] 
trait LowPriorityProductContainsType { 
    implicit def compositeProductContainsTypeInRightPart[L, R, A](
    implicit rightContainsType: ProductContainsType[R, A] 
): ProductContainsType[L & R, A] = null 
} 
object ProductContainsType extends LowPriorityProductContainsType { 
    implicit def simpleProductContainsType[A]: ProductContainsType[A,A] = null 
    implicit def compositeProductContainsTypeInLeftPart[L, R, A](
    implicit leftContainsType: ProductContainsType[L, A] 
): ProductContainsType[L & R, A] = null 
} 
// Exiting paste mode, now interpreting. 

हमारे सख्त update विधि को परिभाषित:

def strictUpdate[A,P](product: P)(f: A => A)(
    implicit 
    updater: ProductUpdater[P,A], 
    containsType: ProductContainsType[P,A] 
): P = updater(product, f) 

के देखते हैं:

scala> strictUpdate(p){ s: String => s.toUpperCase } 
res21: &[&[&[String,Int],User],String] = &(&(&(HELLO,123),User(Elwood,25)),BYE) 

scala> strictUpdate(p){ s: Symbol => Symbol(s.name.toUpperCase) } 
<console>:19: error: Product &[&[&[String,Int],User],String] does not contain type Symbol 
       strictUpdate(p){ s: Symbol => Symbol(s.name.toUpperCase) } 
+0

मैं ऐसे उत्पाद के लिए एक प्रकार कैसे लिखूं जिसमें 'ए' और कुछ और है? 'Val pp: उपयोगकर्ता और कोई = पी' या आपके उदाहरण का उपयोग करने जैसा ही। – Tvaroh

+1

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

+0

क्या यह संकलित नहीं करना संभव है जब किसी उत्पाद में अद्यतन करने के लिए कोई मान न हो? – Tvaroh

0

:

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

scala> case class And[A, B](first: A, second: B) 
defined class And 

scala> val x: String And Double And Int = And(And("test", 1.1), 10) 
x: And[And[String,Double],Int] = And(And(test,1.1),10) 

scala> x.copy(second = 100) 
res0: And[And[String,Double],Int] = And(And(test,1.1),100) 
बेशक

आप इस तरह के समर्थक के साथ कार्यों को परिभाषित कर सकते हैं नलिकाओं:

def update(product: String And Int, f: String => String): String And Int 
3

नहीं एक इष्टतम संस्करण है, मुझे लगता है @TravisBrown या @MilesSabin अधिक पूरा जवाब दे सकता है।

उदाहरणों में हम shapeless 2.2.5 का उपयोग करेंगे। तो हम HList (कोई धैर्य समस्या नहीं) के रूप में आवश्यक प्रकार का प्रतिनिधित्व कर सकते हैं। यह एक HList है, क्योंकि यह एक Poly समारोह लागू करने के लिए संभव है:

trait A 
def aFunc(a: A) = a 

trait lowPriority extends Poly1 { 
    implicit def default[T] = at[T](poly.identity) 
} 

object polyApplyToTypeA extends lowPriority { 
    implicit def caseA = at[A](aFunc(_)) 
} 

list.map(polyApplyToTypeA) //> applies only to type A 

यह पहला दृष्टिकोण था, इसे का उपयोग हम केवल विशेष Poly कार्यों का उपयोग करना चाहिए (यह उन्हें उत्पन्न करने के लिए संभव है), वास्तव में, कि एक समस्या है ।

दूसरा दृष्टिकोण एक स्वयं के समारोह है, जो थोड़ा मुश्किल तर्क है परिभाषित करने के लिए है: एक प्रकार से

def applyToType[L <: HList, P <: HList, PO <: HList, S <: HList, F] 
(fun: F => F, l: L) 
(implicit partition: Partition.Aux[L, F, P, S], 
       tt: ToTraversable.Aux[P, List, F], 
       ft: FromTraversable[P], 
        p: Prepend.Aux[S, P, PO], 
        a: Align[PO, L]): L = 
(l.filterNot[F] ::: l.filter[F].toList[F].map(fun).toHList[P].get).align[L] 

इस समारोह फिल्टर HList एक List में बदल देता है, हमारे समारोह लागू होता है, और इसे वापस धर्मान्तरित HList पर HList टाइप संरेखण को बदलने के क्रम में, प्रकारों को भी संरेखित करता है। अपेक्षित के रूप में काम करता है। यहाँ पूर्ण उदाहरण: https://gist.github.com/pomadchin/bf46e21cb180c2a81664

+0

दूसरा दृष्टिकोण काफी दिलचस्प है (+1)। क्या फ़ंक्शन 'f: f => ए' को सामान्यीकृत करना संभव होगा? फिर कुछ मामलों में एक मानक मोनोमोर्फिक फ़ंक्शन, 'पॉली' की आवश्यकता के बिना करेगा। –

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