2014-06-10 7 views
11

में नि: शुल्क इकाई कुछ सप्ताह पहले Dragisa Krsmanovic पूछा a question here कैसे Scalaz 7 में मुक्त इकाई का उपयोग करने के इस स्थिति में ढेर अतिप्रवाह से बचने के लिए (मैं अपने कोड थोड़ा अनुकूल नहीं बनाया है) के बारे में:अनुप्रयोगी बनाम monadic combinators और Scalaz

import scalaz._, Scalaz._ 

def setS(i: Int): State[List[Int], Unit] = modify(i :: _) 

val s = (1 to 100000).foldLeft(state[List[Int], Unit](())) { 
    case (st, i) => st.flatMap(_ => setS(i)) 
} 

s(Nil) 

मैंने सोचा था कि that just lifting a trampoline into StateT काम करना चाहिए:

import Free.Trampoline 

val s = (1 to 100000).foldLeft(state[List[Int], Unit](()).lift[Trampoline]) { 
    case (st, i) => st.flatMap(_ => setS(i).lift[Trampoline]) 
} 

s(Nil).run 

लेकिन यह अभी भी ढेर चल रही है, तो मैं बस एक टिप्पणी के रूप में यह पोस्ट।

Dave Stevens सिर्फ pointed out कि अनुप्रयोगी *> monadic flatMap वास्तव में के बजाय साथ उन्हें क्रमबद्ध बस ठीक काम करता है:

val s = (1 to 100000).foldLeft(state[List[Int], Unit](()).lift[Trampoline]) { 
    case (st, i) => st *> setS(i).lift[Trampoline] 
} 

s(Nil).run 

(ठीक है, यह निश्चित रूप से सुपर धीमी है, क्योंकि वह कीमत है जो आप कुछ भी की तरह दिलचस्प करने के लिए भुगतान करते हैं है यह स्कैला में है, लेकिन कम से कम कोई स्टैक ओवरफ़्लो नहीं है।)

यहां क्या हो रहा है? मुझे नहीं लगता कि इस अंतर के लिए एक सिद्धांतबद्ध कारण हो सकता है, लेकिन वास्तव में मुझे नहीं पता कि कार्यान्वयन में क्या हो रहा है और इस समय आस-पास खोदने का समय नहीं है। लेकिन मैं उत्सुक हूं और अगर कोई और जानता है तो यह अच्छा होगा।

उत्तर

5

इस अंतर के लिए एक सिद्धांतबद्ध अंतर्ज्ञान है।

आवेदक ऑपरेटर *> केवल इसके दुष्प्रभावों के लिए बाएं तर्क का मूल्यांकन करता है, और हमेशा परिणाम को अनदेखा करता है। यह मोनैड के लिए हास्केल के >> फ़ंक्शन के समान (कुछ मामलों में समतुल्य) है।

/** Combine `self` and `fb` according to `Apply[F]` with a function that discards the `A`s */ 
final def *>[B](fb: F[B]): F[B] = F.apply2(self,fb)((_,b) => b) 

और Apply#apply2:

def apply2[A, B, C](fa: => F[A], fb: => F[B])(f: (A, B) => C): F[C] = 
    ap(fb)(map(fa)(f.curried)) 

सामान्य तौर पर, flatMap, बाईं तर्क का परिणाम पर निर्भर करता है (यह चाहिए के रूप में यह सही में समारोह के लिए इनपुट है यहाँ *> के लिए स्रोत है तर्क)। भले ही इस विशिष्ट मामले में आप बाएं परिणाम को अनदेखा कर रहे हैं, flatMap उसे नहीं पता है।

ऐसा लगता है कि आपके परिणाम दिए गए हैं, *> के लिए कार्यान्वयन उस मामले के लिए अनुकूलित किया गया है जहां बाएं तर्क का परिणाम अनियंत्रित है। हालांकि flatMap इस अनुकूलन को निष्पादित नहीं कर सकता है और इसलिए प्रत्येक कॉल अप्रयुक्त बाएं परिणाम को बनाए रखकर ढेर बढ़ता है।

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

+0

+1 और धन्यवाद, लेकिन मेरी समझ गया था जिस तरह से ट्रैम्पोलिन के 'flatMap' ढेर पर बाँध reifies इसका मतलब है कि हम सुरक्षित यहाँ भी होना चाहते हैं अगर हम उस परिणाम को फेंक नहीं रहे थे? –

+1

@cdk मुझे नहीं लगता कि यह जवाब है। एक और ऑपरेटर चुनें जो बाएं * के परिणाम पर निर्भर करता है और * को 'बाइंड' के बजाय 'लागू करें' की आवश्यकता होती है। जैसे '| @ |' https://gist.github.com/drstevens/3ea464446ee59463af1e – drstevens

3

बस चर्चा में जोड़ने के लिए ...

StateT में, तुम हो: अगले राज्य में

def flatMap[S3, B](f: A => IndexedStateT[F, S2, S3, B])(implicit F: Bind[F]): IndexedStateT[F, S1, S3, B] = 
    IndexedStateT(s => F.bind(apply(s)) { 
    case (s1, a) => f(a)(s1) 
    }) 

apply(s) फिक्स वर्तमान स्थिति संदर्भ।

bind परिभाषा बेसब्री से इसके मापदंडों संदर्भ पकड़ने interpretes क्योंकि यह आवश्यकता है:

def ap[A, B](fa: => F[A])(f: => F[A => B]): F[B] 

इस के साथ:

def bind[A, B](fa: F[A])(f: A => F[B]): F[B] 

ap का अंतर है जो अपने मानकों में से एक interprete करने की आवश्यकता नहीं हो सकता है पर कोड, TrampolineStateTflatMap (और map) के लिए भी मदद नहीं कर सकता ...

6

मंडुबियन सही है, स्टेटटी का फ्लैटमैप आपको लपेटा हुआ मोनैड बाइंड (जो आपके मामले में एक नि: शुल्क [फंक्शन 0] होगा) को कॉल करने से पहले नए स्टेटटी के निर्माण के कारण स्टैक संचय को बाईपास करने की अनुमति नहीं देता है।

तो ट्रैम्पोलिन मदद नहीं कर सकता है, लेकिन राज्य के लिए मज़दूर पर नि: शुल्क मोनाड स्टैक सुरक्षा सुनिश्चित करने का एक तरीका है।

हम राज्य [सूची [Int], यूनिट] से मुक्त [ए [राज्य [सूची [Int], ए], यूनिट] में जाना चाहते हैं और हमारा फ्लैटमैप कॉल फ्री के फ्लैटमैप (जो नहीं करता है नि: शुल्क डेटा संरचना बनाने के अलावा कुछ भी)।

s.foldRun(List[Int]())((a,b) => b(a)) 

कॉलिंग liftF काफी बदसूरत है तो मैं एक पीआर है में यह राज्य के लिए आसान बनाने के लिए:

val s = (1 to 100000).foldLeft( 
    Free.liftF[({ type l[a] = State[List[Int],a]})#l,Unit](state[List[Int], Unit](()))) { 
     case (st, i) => st.flatMap(_ => 
      Free.liftF[({ type l[a] = State[List[Int],a]})#l,Unit](setS(i))) 
    } 

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

संपादित करें: पीआर तो स्वीकार किए जाते हैं अब हम

val s = (1 to 100000).foldLeft(state[List[Int], Unit](()).liftF) { 
     case (st, i) => st.flatMap(_ => setS(i).liftF) 
} 
संबंधित मुद्दे