2014-10-15 7 views
11

एक प्ले एप्लिकेशन में मैं काम कर रहा हूं, मैं झंडे को संसाधित करने के लिए हमारी प्रणाली को बेहतर बनाने की कोशिश कर रहा हूं, जिनमें से कुछ लगातार विकल्प होने के लिए हैं क्योंकि उपयोगकर्ता लिंक के माध्यम से हमारे ऐप को नेविगेट करता है। मैं विकल्प के परिभाषा से अपने मूल्य के मानचित्र के लिए बेकार का उपयोग करना चाहता हूं, और प्रचारित होने के लिए चिह्नित किए गए केवल नए क्वेरी पैरामीटर को संश्लेषित करना चाहता हूं। मैं पैरामीटर मानों के दृढ़ता से टाइप किए गए dereferencing प्राप्त करने के लिए Shapeless की Record कार्यक्षमता का लाभ उठाने में भी सक्षम होना चाहता हूं। दुर्भाग्य से, मुझे यकीन नहीं है कि मैं इसे बेकार में वैध तरीके से देख रहा हूं।बेकार रिकॉर्ड पर मैपिंग

निम्नलिखित कोड की एक ब्लॉक है, कुछ व्याख्यात्मक टिप्पणियों में बाधित है।

यहाँ बुनियादी डेटा प्रकार के साथ मैं काम कर रहा हूँ कर रहे हैं:

import shapeless._ 
import poly._ 
import syntax.singleton._ 
import record._ 

type QueryParams = Map[String, Seq[String]] 

trait RequestParam[T] { 
    def value: T 

    /** Convert value back to a query parameter representation */ 
    def toQueryParams: Seq[(String, String)] 

    /** Mark this parameter for auto-propagation in new URLs */ 
    def propagate: Boolean 

    protected def queryStringPresent(qs: String, allParams: QueryParams): Boolean = allParams.get(qs).nonEmpty 
} 

type RequestParamBuilder[T] = QueryParams => RequestParam[T] 

def booleanRequestParam(paramName: String, willPropagate: Boolean): RequestParamBuilder[Boolean] = { params => 
    new RequestParam[Boolean] { 
    def propagate: Boolean = willPropagate 
    def value: Boolean = queryStringPresent(paramName, params) 
    def toQueryParams: Seq[(String, String)] = Seq(paramName -> "true").filter(_ => value) 
    } 
} 

def stringRequestParam(paramName: String, willPropagate: Boolean): RequestParamBuilder[Option[String]] = { params => 
    new RequestParam[Option[String]] { 
    def propagate: Boolean = willPropagate 
    def value: Option[String] = params.get(paramName).flatMap(_.headOption) 
    def toQueryParams: Seq[(String, String)] = value.map(paramName -> _).toSeq 
    } 
} 

हकीकत में, निम्नलिखित एक वर्ग निर्माता है कि इस मानचित्र एक पैरामीटर के रूप क्वेरी स्ट्रिंग से पढ़ लेता होगा, लेकिन सादगी की खातिर

val requestParams = Map("no_ads" -> Seq("true"), "edition" -> Seq("us")) 

// In reality, there are many more possible parameters, but this is simplified 
val options = ('adsDebug ->> booleanRequestParam("ads_debug", true)) :: 
    ('hideAds ->> booleanRequestParam("no_ads", true)) :: 
    ('edition ->> stringRequestParam("edition", false)) :: 
    HNil 

object bind extends (RequestParamBuilder ~> RequestParam) { 
    override def apply[T](f: RequestParamBuilder[T]): RequestParam[T] = f(requestParams) 
} 

// Create queryable option values record by binding the request parameters 
val boundOptions = options.map(bind) 

यह पिछले बयान से काम नहीं करता है, और त्रुटि देता है:, मैं सिर्फ एक val को परिभाषित कर रहा हूँ

<console>:79: error: could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[bind.type,shapeless.::[RequestParamBuilder[Boolean] with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("adsDebug")],RequestParamBuilder[Boolean]],shapeless.::[RequestParamBuilder[Boolean] with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("hideAds")],RequestParamBuilder[Boolean]],shapeless.::[RequestParamBuilder[Option[String]] with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("edition")],RequestParamBuilder[Option[String]]],shapeless.HNil]]]] 
      val boundOptions = options.map(bind) 
,210

लेकिन यह सोचते हैं कि काम किया, मैं निम्नलिखित करना चाहते हैं जाएगा:

object propagateFilter extends (RequestParam ~> Const[Boolean]) { 
    override def apply[T](r: RequestParam[T]): Boolean = r.propagate 
} 

object unbind extends (RequestParam ~> Const[Seq[(String, String)]]) { 
    override def apply[T](r: RequestParam[T]): Seq[(String, String)] = r.toQueryParams 
} 

// Reserialize a query string for options that should be propagated 
val propagatedParams = boundOptions.values.filter(propagateFilter).map(unbind).toList 
// (followed by conventional collections methods) 

मैं मुझे लगता है कि पहले .map काम करने के लिए कॉल प्राप्त करने का क्या करने की जरूरत नहीं पता है, और मुझे लगता है मैं चल रहा हो जाएगा अगले दो polymorphic कार्यों के साथ मुद्दों में।

+0

'options.values.map (बाइंड) 'काम करना चाहिए लेकिन नहीं। 'पॉली 1' के संदर्भ में 'बाइंड' को परिभाषित करने से चीजों को सीधे ठीक किया जाता है- मुझे यकीन नहीं है कि इस समय क्यों, लेकिन बाद में एक नज़र डालें। –

+0

क्या मुझे रिकॉर्ड पर खुद को मानचित्र करने में सक्षम नहीं होना चाहिए? मैं अभी भी चाबियों द्वारा बाध्य विकल्पों तक पहुंचने में सक्षम होना चाहता हूं। – acjay

+0

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

उत्तर

7

अद्यतन: FieldPoly सहायक वास्तव में यह सब बहुत काम आप के लिए यहाँ नहीं है, और आप इसे बिना इसी कार्य को पूरा कर सकते हैं (और बिना Witness निहित):

import shapeless.labelled.{ FieldType, field } 

object bind extends Poly1 { 
    implicit def rpb[T, K]: Case.Aux[ 
    FieldType[K, RequestParamBuilder[T]], 
    FieldType[K, RequestParam[T]] 
    ] = at[FieldType[K, RequestParamBuilder[T]]](b => field[K](b(requestParams))) 
} 

यह भी ध्यान देने योग्य है कि अगर आप खतरनाक तरीके से रहने वाले कोई आपत्ति नहीं है, तो आप वापसी प्रकार (दोनों कार्यान्वयन में) को छोड़ कर सकते हैं:

object bind extends Poly1 { 
    implicit def rpb[T, K] = at[FieldType[K, RequestParamBuilder[T]]](b => 
    field[K](b(requestParams)) 
) 
} 

लेकिन सामान्य रूप में किसी अनुमानित वापसी प्रकार के साथ एक अंतर्निहित विधि होने एक बुरा विचार है।


मैं ऊपर एक टिप्पणी में उल्लेख के रूप में, Case covariant नहीं है, जिसका अर्थ है कि आपके bind ही काम करता है, तो HList के तत्वों स्थिर (RequestParamBuilder के रूप में लिखे जाते हैं जिस स्थिति में आप एक न करना पड़े रिकॉर्ड)।

आप रिकॉर्ड से मूल्य प्राप्त करने के लिए .values का उपयोग कर सकते हैं, और फिर आप परिणाम पर मानचित्र कर सकते हैं, लेकिन (जैसा कि आप नोट करते हैं) इसका मतलब यह होगा कि आप चाबियाँ खो देते हैं। आप कुंजी को संरक्षित करना चाहते हैं, तो आप निराकार के FieldPoly, जो बनाया गया है स्थिति इस तरह का में मदद करने के लिए उपयोग कर सकते हैं: के रूप में उम्मीद

import shapeless.labelled.FieldPoly 

object bind extends FieldPoly { 
    implicit def rpb[T, K](implicit witness: Witness.Aux[K]): Case.Aux[ 
    FieldType[K, RequestParamBuilder[T]], 
    FieldType[K, RequestParam[T]] 
    ] = atField(witness)(_(requestParams)) 
} 

अब options.map(bind) काम करेंगे।

मुझे नहीं लगता कि इस समय इसे लिखने का एक बेहतर तरीका है, लेकिन मैं सबसे हाल ही में बेकार विकास का पालन नहीं कर रहा हूं। किसी भी मामले में यह उचित रूप से स्पष्ट है, बहुत वर्बोज़ नहीं है, और यह वही करता है जो आप चाहते हैं।

अपनी टिप्पणी में अन्य प्रश्न का उत्तर देने के लिए: this previous question एक शुरुआती बिंदु है, लेकिन मुझे बेकार में पॉलिमॉर्फिक फ़ंक्शन मानों के कार्यान्वयन के यांत्रिकी के वास्तव में एक अच्छा अवलोकन के बारे में पता नहीं है। ब्लॉग पोस्ट के लिए यह एक अच्छा विचार है।

+0

बहुत बढ़िया, यह काम करता है वास्तव में अच्छी तरह से। असल में, ब्लॉग पोस्ट तक, मुझे याद आया http://www.chuusai.com/2012/04/27/shapeless-polymorphic-function-values-1/, हालांकि इसमें अभी भी 3 का लंबा इंतजार हिस्सा 3 की कमी है। मैं अभी भी 'प्रोपेगेटफिल्टर' और 'अनबिंड' के साथ थोड़ा सा संघर्ष कर रहा हूं, लेकिन मैं आगे बढ़ूंगा और इस जवाब को स्वीकार करूँगा क्योंकि यह वास्तव में अकेला खड़ा है, और एक और सवाल पूछता है। धन्यवाद!! – acjay

+0

इसके अलावा, वंशावली के लिए, किसी भी समय 'साक्षी' और 'एटफ़िल्ड' की व्याख्या करने के लिए यह वास्तव में सहायक हो सकता है, यदि कोई भी कभी भी इन विवरणों के साथ इस उत्तर को बढ़ाना चाहता है। – acjay

+0

@acjay वास्तव में दूसरी नज़र में गवाह के बिना ऐसा करना संभव होना चाहिए-मैं जितनी जल्दी हो सके अपडेट करूँगा। –

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