স্কালা বিড়াল / fs2 এ স্ট্যাক সুরক্ষা সম্পর্কে কীভাবে যুক্তিযুক্ত?


13

এখানে fs2 এর জন্য ডকুমেন্টেশন থেকে কোডের একটি অংশ রয়েছে । ফাংশন goপুনরাবৃত্তি। প্রশ্নটি হ'ল আমরা কীভাবে জানি যে এটি স্ট্যাক নিরাপদ কিনা এবং কোনও ফাংশন স্ট্যাক নিরাপদ থাকলে কীভাবে যুক্তিযুক্ত?

import fs2._
// import fs2._

def tk[F[_],O](n: Long): Pipe[F,O,O] = {
  def go(s: Stream[F,O], n: Long): Pull[F,O,Unit] = {
    s.pull.uncons.flatMap {
      case Some((hd,tl)) =>
        hd.size match {
          case m if m <= n => Pull.output(hd) >> go(tl, n - m)
          case m => Pull.output(hd.take(n.toInt)) >> Pull.done
        }
      case None => Pull.done
    }
  }
  in => go(in,n).stream
}
// tk: [F[_], O](n: Long)fs2.Pipe[F,O,O]

Stream(1,2,3,4).through(tk(2)).toList
// res33: List[Int] = List(1, 2)

আমরা যদি goঅন্য কোনও পদ্ধতি থেকে কল করি তবে কী এটি স্ট্যাক নিরাপদ হবে ?

def tk[F[_],O](n: Long): Pipe[F,O,O] = {
  def go(s: Stream[F,O], n: Long): Pull[F,O,Unit] = {
    s.pull.uncons.flatMap {
      case Some((hd,tl)) =>
        hd.size match {
          case m if m <= n => otherMethod(...)
          case m => Pull.output(hd.take(n.toInt)) >> Pull.done
        }
      case None => Pull.done
    }
  }

  def otherMethod(...) = {
    Pull.output(hd) >> go(tl, n - m)
  }

  in => go(in,n).stream
}

না, ঠিক না যদিও এটি লেজ পুনরাবৃত্তির ক্ষেত্রে হয় দয়া করে তাই বলুন, তবে মনে হয় এটি তা নয়। আমি যতদূর জানি বিড়ালরা স্ট্যাক সুরক্ষার জন্য ট্রাম্পোলাইনিং নামে কিছু জাদু করে। দুর্ভাগ্যক্রমে আমি কখনই বলতে পারি না কোনও ফাংশন ট্রাম্পলাইনড এবং কখন নয়।
লেভ ডেনিসভ

আপনি goযেমন Monad[F]টাইপক্লাস ব্যবহার করে পুনরায় লিখতে পারেন - tailRecMফাংশনটি স্ট্যাক নিরাপদ থাকবে তার গ্যারান্টি দেওয়ার জন্য আপনাকে স্পষ্টভাবে ট্রামপোলিন সঞ্চালনের অনুমতি দেওয়ার পদ্ধতি রয়েছে । আমি ভুল হতে পারি তবে এগুলি ব্যতীত আপনি নিজেরাই Fস্ট্যাক নিরাপদে থাকার উপর নির্ভর করছেন (উদাহরণস্বরূপ যদি এটি অভ্যন্তরীণভাবে ট্রাম্পোলাইন প্রয়োগ করে) তবে আপনি কখনই জানেন না কে আপনার সংজ্ঞা দেবে F, সুতরাং আপনার এটি করা উচিত নয়। যদি আপনার কোনও গ্যারান্টি নেই Fযা স্ট্যাক নিরাপদ, এটি একটি ধরণের শ্রেণি ব্যবহার করুন যা tailRecMএটি সরবরাহ করে কারণ এটি আইন অনুসারে স্ট্যাক-নিরাপদ।
ম্যাটিউজ কুবসজোক

1
@tailrecসংকলকটিকে লেজ রেক ফাংশনগুলির জন্য টীকা সহ প্রমাণ করতে দেওয়া সহজ easy অন্যান্য ক্ষেত্রে স্কাল এএফাইক-তে কোনও আনুষ্ঠানিক গ্যারান্টি নেই। এমনকি যদি ফাংশনটি নিজেই নিরাপদ থাকে তবে অন্যান্য ফাংশনগুলি যার কাছে এটি কল করছে তা নাও হতে পারে: /।
yǝsʞǝla

উত্তর:


17

আমার এখানে পূর্ববর্তী উত্তরটি কিছু ব্যাকগ্রাউন্ড তথ্য দেয় যা দরকারী হতে পারে। মূল ধারণাটি হ'ল কিছু এফেক্টের ধরণের flatMapপ্রয়োগগুলি রয়েছে যা স্ট্যাক-নিরাপদ পুনরাবৃত্তিকে সরাসরি সমর্থন করে — আপনি flatMapকলগুলি বাজেভাবে বা পুনরাবৃত্তির মাধ্যমে যতটা গভীরভাবে চান তত নীড় করতে পারেন এবং আপনি স্ট্যাকটিকে উপচে ফেলবেন না।

কিছু প্রভাবের ধরণের জন্য এটি flatMapস্ট্যাক-নিরাপদ হওয়া সম্ভব নয় , কারণ প্রভাবটির শব্দার্থকতা। অন্যান্য ক্ষেত্রে স্ট্যাক-সেফ লেখা সম্ভব হতে পারে flatMap, তবে প্রয়োগকারীরা পারফরম্যান্স বা অন্যান্য বিবেচনার কারণে না হওয়ার সিদ্ধান্ত নিয়েছিলেন।

দুর্ভাগ্যক্রমে কোনও flatMapনির্দিষ্ট ধরণের স্ট্যাক-নিরাপদ কিনা তা জানার কোনও মানক (বা এমনকি প্রচলিত) উপায় নেই । বিড়ালগুলির মধ্যে এমন একটি tailRecMঅপারেশন অন্তর্ভুক্ত রয়েছে যা কোনও আইনী মোনাডিক প্রভাবের ধরণের জন্য স্ট্যাক-নিরাপদ monadic পুনরাবৃত্তি সরবরাহ করা উচিত, এবং কখনও কখনও tailRecMবৈধ হিসাবে পরিচিত এমন একটি বাস্তবায়নের দিকে তাকালে flatMapস্ট্যাক-নিরাপদ কিনা তা সম্পর্কে কিছু ইঙ্গিত প্রদান করতে পারে । ক্ষেত্রে Pullএটা দেখে মনে হচ্ছে এই :

def tailRecM[A, B](a: A)(f: A => Pull[F, O, Either[A, B]]) =
  f(a).flatMap {
    case Left(a)  => tailRecM(a)(f)
    case Right(b) => Pull.pure(b)
  }

এই tailRecMশুধু মাধ্যমে recursing হয় flatMap, এবং আমরা জানি যে Pullএর Monadউদাহরণ হিসেবে বলা যায় হালাল , যা প্রশংসনীয় ভাল প্রমাণ যে Pullএর flatMapহয় স্ট্যাক-নিরাপদ। এখানে ফ্যাক্টর জটিল যে জন্য উদাহরণস্বরূপ হয় Pullএকটি হয়েছে ApplicativeErrorউপর বাধ্যতা Fযে Pullএর flatMapনা, কিন্তু এই ক্ষেত্রে যে কিছু পরিবর্তন করে না হবে।

সুতরাং এখানে tkবাস্তবায়ন স্ট্যাক-নিরাপদ কারণ flatMapএটি Pullস্ট্যাক-নিরাপদ, এবং আমরা জানি যে এটির tailRecMবাস্তবায়নের দিকে তাকানো থেকে । (আমরা যদি আরও গভীর খনন করি তবে আমরা বুঝতে পারি যে flatMapএটি স্ট্যাক-নিরাপদ কারণ Pullমূলত একটি মোড়ক FreeCযা ট্রামপোলিনযুক্ত ))

এটা সম্ভবত ভয়ঙ্কর পুনর্লিখন কঠিন হবে না tkপরিপ্রেক্ষিতে tailRecM, যদিও আমরা অন্যথায় অপ্রয়োজনীয় যোগ আছে চাই ApplicativeErrorবাধ্যতা। আমি ডকুমেন্টেশন লেখক অনুমান করছি না যে স্বচ্ছতার জন্য মনোনীত করেছি এবং কারণ তারা জানত Pull's flatMapজরিমানা।


আপডেট: এখানে মোটামুটি যান্ত্রিক tailRecMঅনুবাদ:

import cats.ApplicativeError
import fs2._

def tk[F[_], O](n: Long)(implicit F: ApplicativeError[F, Throwable]): Pipe[F, O, O] =
  in => Pull.syncInstance[F, O].tailRecM((in, n)) {
    case (s, n) => s.pull.uncons.flatMap {
      case Some((hd, tl)) =>
        hd.size match {
          case m if m <= n => Pull.output(hd).as(Left((tl, n - m)))
          case m => Pull.output(hd.take(n.toInt)).as(Right(()))
        }
      case None => Pull.pure(Right(()))
    }
  }.stream

মনে রাখবেন যে এখানে কোনও স্পষ্ট পুনরাবৃত্তি নেই।


আপনার দ্বিতীয় প্রশ্নের উত্তরটি অন্য পদ্ধতিটি কেমন দেখাচ্ছে তার উপর নির্ভর করে তবে আপনার নির্দিষ্ট উদাহরণের ক্ষেত্রে >>কেবল আরও flatMapস্তর তৈরি হবে, সুতরাং এটি ঠিক হওয়া উচিত।

আপনার প্রশ্নকে আরও সাধারণভাবে সমাধান করার জন্য, এই পুরো বিষয়টি স্কালায় একটি বিভ্রান্তিকর গোলযোগ। কোনও ধরণের স্ট্যাক-সেফ মোনাদিক পুনরাবৃত্তি সমর্থন করে কিনা তা আমরা কেবল উপরে জানার জন্য আপনাকে প্রয়োগের মতো খনন করতে হবে না। ডকুমেন্টেশনের চারপাশে আরও ভাল কনভেনশনগুলি এখানে সহায়তা করবে তবে দুর্ভাগ্যক্রমে আমরা এর খুব ভাল কাজটি করছি না। আপনি সর্বদা tailRecM"নিরাপদ" হিসাবে ব্যবহার করতে পারেন (যা F[_]জেনেরিক হওয়ার পরে আপনি যা করতে চাইবেন যাইহোক) তবে তারপরেও আপনি বিশ্বাস করছেন যে Monadবাস্তবায়ন বৈধ।

সংক্ষেপে: এটি চারদিকে একটি খারাপ পরিস্থিতি এবং সংবেদনশীল পরিস্থিতিতে আপনার অবশ্যই এই জাতীয় বাস্তবায়নগুলি স্ট্যাক-নিরাপদ কিনা তা যাচাই করার জন্য আপনার নিজের পরীক্ষা লিখতে হবে।


ব্যাখ্যা করার জন্য আপনাকে ধন্যবাদ. প্রশ্নটি সম্পর্কে যখন আমরা goঅন্য পদ্ধতি থেকে কল করি তখন কী এটিকে স্ট্যাকটিকে অনিরাপদ করে তুলতে পারে? আমরা কল করার আগে যদি আমরা কিছু অ-পুনরাবৃত্ত গণনা করি Pull.output(hd) >> go(tl, n - m)তবে তা কি ঠিক আছে?
লেভ ডেনিসভ

হ্যাঁ, এটি সূক্ষ্ম হওয়া উচিত (ধরে নিলাম গণনাটি অবশ্যই স্ট্যাকের উপচে পড়বে না)।
ট্র্যাভিস ব্রাউন

উদাহরণস্বরূপ কোন প্রভাবের ধরণটি মোনাডিক পুনরাবৃত্তির জন্য স্ট্যাক-নিরাপদ হবে না? ধারাবাহিকতা টাইপ?
বব

রাইট @bob, যদিও বিড়াল এর ContT'র flatMap হয় আসলে স্ট্যাক-নিরাপদ (ক মাধ্যমে Deferঅন্তর্নিহিত ধরনের উপর বাধ্যতা)। আমি এমন কিছু নিয়ে ভাবছিলাম List, যেখানে পুনরাবৃত্তি করা flatMapস্ট্যাক-নিরাপদ নয় ( tailRecMযদিও এটি আইনী আছে )।
ট্র্যাভিস ব্রাউন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.