স্কালায় টাইপ ল্যাম্বডাস কী এবং সেগুলির সুবিধা কী?


152

মাঝে মাঝে আমি আধা রহস্যজনক স্বরলিপিতে হোঁচট খেয়ে যাই

def f[T](..) = new T[({type l[A]=SomeType[A,..]})#l] {..} 

স্কাল ব্লগ পোস্টগুলিতে, যা এটিকে "আমরা সেই টাইপ-ল্যাম্বদা ট্রিক ব্যবহার করেছি" হ্যান্ডওয়েভ দেয়।

যদিও আমি এই সম্পর্কে কিছু স্বীকৃতি পেয়েছি (আমরা এর Aসাথে সংজ্ঞাটি দূষিত না করে একটি বেনামে টাইপ প্যারামিটার অর্জন করি?), ল্যাম্বডা কৌশলটি টাইপটি কী এবং এর কী কী সুবিধা রয়েছে তা বর্ণনা করার জন্য আমি কোনও সুস্পষ্ট উত্স পাইনি। এটি কি কেবল সিনট্যাকটিক চিনি, বা এটি কিছু নতুন মাত্রা খুলবে?


উত্তর:


148

আপনি যখন উচ্চতর ধরণের সাথে কাজ করছেন তখন টাইপ ল্যাম্বডাস বেশ খানিকটা সময় গুরুত্বপূর্ণ vital

হয় [এ, বি] এর সঠিক প্রক্ষেপণের জন্য একটি মোনাডকে সংজ্ঞায়নের একটি সাধারণ উদাহরণ বিবেচনা করুন। মোনাড টাইপক্লাসটি দেখতে এমন দেখাচ্ছে:

trait Monad[M[_]] {
  def point[A](a: A): M[A]
  def bind[A, B](m: M[A])(f: A => M[B]): M[B]
}

এখন হয় হয় দুটি আর্গুমেন্টের টাইপ কনস্ট্রাক্টর তবে মোনাডকে বাস্তবায়িত করার জন্য আপনাকে এটিকে একটি যুক্তির টাইপ কনস্ট্রাক্টর দেওয়া দরকার। এর সমাধান হ'ল এক প্রকার ল্যাম্বদা ব্যবহার করা:

class EitherMonad[A] extends Monad[({type λ[α] = Either[A, α]})#λ] {
  def point[B](b: B): Either[A, B]
  def bind[B, C](m: Either[A, B])(f: B => Either[A, C]): Either[A, C]
}

এটি টাইপ সিস্টেমে কারিঙের উদাহরণ - আপনি উভয়ের প্রকারটি ত্যাগ করেছেন, যেমন আপনি যখন ইথারমোনাদের উদাহরণ তৈরি করতে চান, তখন আপনাকে একটি প্রকার নির্দিষ্ট করতে হবে; অন্যটি অবশ্যই সরবরাহ করা হয় যখন আপনি পয়েন্ট বা বাঁধাই কল করেন।

টাইপ ল্যাম্বদা ট্রিকটি এই ধরণের অবস্থানটি খালি ব্লক একটি বেনামে কাঠামোগত ধরণের তৈরি করে তোলে। তারপরে আমরা একটি টাইপ সদস্য পেতে # সিনট্যাক্স ব্যবহার করি।

কিছু ক্ষেত্রে, আপনার আরও পরিশীলিত টাইপ ল্যাম্বডাসের প্রয়োজন হতে পারে যা ইনলাইনটি লেখার জন্য ব্যথা। আজ থেকে আমার কোডের একটি উদাহরণ এখানে:

// types X and E are defined in an enclosing scope
private[iteratee] class FG[F[_[_], _], G[_]] {
  type FGA[A] = F[G, A]
  type IterateeM[A] = IterateeT[X, E, FGA, A] 
}

এই শ্রেণিটি একচেটিয়াভাবে উপস্থিত রয়েছে যাতে আমি FG [F, G] #IterateeM এর মতো একটি নাম ব্যবহার করতে পারি যাতে দ্বিতীয় মনডের কিছু ট্রান্সফরমার সংস্করণে বিশেষীকৃত IterateeT মনাদের ধরণটি উল্লেখ করা যেতে পারে যা কিছু তৃতীয় মনাদকে বিশেষীকরণ করা হয়। আপনি যখন স্ট্যাক করা শুরু করেন, এই ধরণের কনস্ট্রাক্টগুলি খুব প্রয়োজনীয় হয়ে ওঠে। আমি কখনই কোনও এফজি ইনস্ট্যান্ট করি না, অবশ্যই; টাইপ সিস্টেমে আমি কী চাই তা প্রকাশ করার জন্য এটি কেবল একটি হ্যাক হিসাবে রয়েছে।


3
মজার বিষয় লক্ষণীয় যে হাস্কেল সরাসরি টাইপ-লেভেল ল্যাম্বডাসকে সমর্থন করে না , যদিও কিছু নতুন টাইপ হ্যাকারি (যেমন টাইপকম্পোজ লাইব্রেরি) এর কাছাকাছি যাওয়ার উপায় রয়েছে।
ড্যান বার্টন

1
আমি bindআপনার EitherMonadক্লাসের জন্য পদ্ধতিটি সংজ্ঞায়িত করতে আগ্রহী হতে চাই । :-) এদিকে, যদি আমি এখানে এক সেকেন্ডের জন্য অ্যাড্রিয়ান চ্যানেল করতে পারি, আপনি সেই উদাহরণে উচ্চতর ধরণের ব্যবহার করছেন না। আপনি ভিতরে আছেন FG, কিন্তু ভিতরে নেই EitherMonad। বরং, আপনি টাইপ কনস্ট্রাক্টর ব্যবহার করছেন , যা দয়ালু * => *। এই ধরণের অর্ডার -১ এর যা "উচ্চতর" নয়।
ড্যানিয়েল স্পিওক

2
আমি ভেবেছিলাম এই ধরণের *অর্ডার -1 ছিল তবে কোনও অবস্থাতেই মোনাদ সদয় ছিল (* => *) => *। এছাড়াও, আপনি নোট করবেন যে আমি "সঠিক প্রক্ষেপণ" নির্দিষ্ট করেছি Either[A, B]- বাস্তবায়নটি তুচ্ছ (যদি আপনি এটি আগে না করেন তবে একটি ভাল অনুশীলন!)
ক্রিস নুটিকম্বে

আমি *=>*অনুমান করি যে ড্যানিয়েলের বক্তব্য উচ্চতর না বলে ন্যায়সঙ্গত যে আমরা কোনও সাধারণ ফাংশন বলি না (যেটি নন ফাংশনগুলিকে মেনু করে না, অন্য কথায়, সরল মান থেকে সরল মানকে উচ্চতর অর্ডার ফাংশন দেয়)।
ঝেগেদুস

1
পিয়ার্সের টিএপএল বই, পৃষ্ঠা 442:Type expressions with kinds like (*⇒*)⇒* are called higher-order typeoperators.
heেজেডাস

52

বেনিফিটগুলি হ'ল বেনাম ফাংশনগুলির দ্বারা প্রদত্ত সমান।

def inc(a: Int) = a + 1; List(1, 2, 3).map(inc)

List(1, 2, 3).map(a => a + 1)

স্কালাজ with সহ একটি উদাহরণের ব্যবহার We আমরা একটি এমনটি ব্যবহার করতে চাই Functorযা একটি এ এর ​​দ্বিতীয় উপাদানের উপরে কোনও ফাংশন মানচিত্র করতে পারে Tuple2

type IntTuple[+A]=(Int, A)
Functor[IntTuple].map((1, 2))(a => a + 1)) // (1, 3)

Functor[({type l[a] = (Int, a)})#l].map((1, 2))(a => a + 1)) // (1, 3)

স্কালাজ এমন কিছু অন্তর্নিহিত রূপান্তর সরবরাহ করে যা প্রকারের যুক্তিটিকে অনুমান করতে পারে Functor, তাই আমরা প্রায়শই এগুলি সম্পূর্ণরূপে লেখা এড়িয়ে চলি। পূর্ববর্তী লাইনটি আবার লিখিত হতে পারে:

(1, 2).map(a => a + 1) // (1, 3)

আপনি যদি ইন্টেলিজ ব্যবহার করেন তবে আপনি সেটিংস, কোড স্টাইল, স্কালা, ফোল্ডিং, টাইপ লম্বডাস সক্ষম করতে পারেন। এরপরে এটি সিনট্যাক্সের ক্রুফটি অংশগুলি লুকিয়ে রাখে এবং আরও স্পষ্টকর উপস্থাপন করে:

Functor[[a]=(Int, a)].map((1, 2))(a => a + 1)) // (1, 3)

ভবিষ্যতের স্কালার সংস্করণ সরাসরি এ জাতীয় বাক্য গঠন সমর্থন করে।


শেষ স্নিপেটটি দেখতে দুর্দান্ত লাগছে। ইন্টেলিজিজ স্কেল প্লাগইনটি অবশ্যই দুর্দান্ত!
AndreasSchainert

1
ধন্যবাদ! ল্যাম্বদা শেষ উদাহরণে নিখোঁজ হতে পারে। পাশাপাশি, কেন টুপল ফান্ট্যাকাররা সর্বশেষ মানটি রূপান্তর করতে বেছে নিয়েছে? এটি কি কনভেনশন / ব্যবহারিক ডিফল্ট?
রন

1
আমি নিকার জন্য রাত্রে ছুটে যাচ্ছি এবং আমার কাছে আইডিইএ বিকল্প বর্ণিত নেই। যথেষ্ট উত্সাহের ব্যাপার হল, সেখানে হয় একটি পরিদর্শন "ফলিত প্রকার ল্যামডা সরলীকৃত যেতে পারে।"
র‌্যান্ডাল স্কুল্জ

6
এটি সেটিংস -> সম্পাদক -> কোড ফোল্ডিং এ সরানো হয়েছে।
retronym

@ রিট্রোনিয়াম, রিপল চেষ্টা করার সময় আমি একটি ত্রুটি পেয়েছি (1, 2).map(a => a + 1): `<কনসোল>: 11: ত্রুটি: মানচিত্র মানচিত্র (ইনট, ইনট) (1, 2)। ম্যাপ (a => এ + 1) এর সদস্য নয় ^`
কেভিন মেরেডিথ

41

বিষয়গুলিকে প্রসঙ্গে রাখতে: এই উত্তরটি মূলত অন্য থ্রেডে পোস্ট করা হয়েছিল। আপনি এটি এখানে দেখছেন কারণ দুটি থ্রেড একত্রিত করা হয়েছে। উল্লিখিত থ্রেডে প্রশ্ন বিবৃতিটি নিম্নরূপ ছিল:

এই ধরণের সংজ্ঞা কীভাবে সমাধান করবেন: খাঁটি [({প্রকার? [ক] = (আর, ক)}) #?]?)?

এই ধরনের নির্মাণ ব্যবহার করার কারণগুলি কী কী?

স্নিপড স্ক্যালজ লাইব্রেরি থেকে আসে:

trait Pure[P[_]] {
  def pure[A](a: => A): P[A]
}

object Pure {
  import Scalaz._
//...
  implicit def Tuple2Pure[R: Zero]: Pure[({type ?[a]=(R, a)})#?] = new Pure[({type ?[a]=(R, a)})#?] {
  def pure[A](a: => A) = (Ø, a)
  }

//...
}

উত্তর:

trait Pure[P[_]] {
  def pure[A](a: => A): P[A]
}

বাক্সগুলির মধ্যে একটি আন্ডারস্কোরের পরে Pবোঝায় যে এটি একটি প্রকারের কনস্ট্রাক্টর এক প্রকার নেয় এবং অন্য প্রকারটি প্রদান করে। এই জাতীয় ধরণের কনস্ট্রাক্টরগুলির উদাহরণ: List, Option

Listএকটি Int, একটি কংক্রিট টাইপ দিন এবং এটি আপনাকে দেয় List[Int], অন্য কংক্রিটের ধরণ। Listএকটি দিন Stringএবং এটি আপনাকে দেয় List[String]। প্রভৃতি

সুতরাং, List, Option1. আনুষ্ঠানিকভাবে আমরা বলতে arity ধরণ স্তর ফাংশন হিসেবে বিবেচনা করা যেতে পারে, তারা একটি ধরনের আছে * -> *। নক্ষত্র একটি প্রকারকে বোঝায়।

এখন Tuple2[_, _]ধরণের একটি টাইপ কনস্ট্রাক্টর (*, *) -> *অর্থাৎ নতুন টাইপ পাওয়ার জন্য আপনাকে এটি দুটি প্রকারের দেওয়া দরকার।

যেহেতু তাদের স্বাক্ষর মিলছে না, আপনি প্রতিস্থাপন করতে পারবেন না Tuple2জন্য P। আপনার যা করা দরকার তা আংশিকভাবে এর একটি Tuple2 যুক্তিতে প্রয়োগ করা হবে যা আমাদের সাথে ধরণের একটি নির্মাতা দেবে * -> *এবং আমরা এটির জন্য প্রতিস্থাপন করতে পারি P

দুর্ভাগ্যক্রমে স্কালার টাইপ কন্সট্রাক্টরদের আংশিক প্রয়োগের জন্য কোনও বিশেষ সিনট্যাক্স নেই এবং তাই আমাদের টাইপ ল্যাম্বডাস নামক মনট্রোটি অবলম্বন করতে হবে। (আপনার উদাহরণে যা আছে।) তাদের এগুলি বলা হয় কারণ এগুলি মান স্তরে বিদ্যমান ল্যাম্বডা অভিব্যক্তির সাথে সাদৃশ্যপূর্ণ।

নিম্নলিখিত উদাহরণ সাহায্য করতে পারে:

// VALUE LEVEL

// foo has signature: (String, String) => String
scala> def foo(x: String, y: String): String = x + " " + y
foo: (x: String, y: String)String

// world wants a parameter of type String => String    
scala> def world(f: String => String): String = f("world")
world: (f: String => String)String

// So we use a lambda expression that partially applies foo on one parameter
// to yield a value of type String => String
scala> world(x => foo("hello", x))
res0: String = hello world


// TYPE LEVEL

// Foo has a kind (*, *) -> *
scala> type Foo[A, B] = Map[A, B]
defined type alias Foo

// World wants a parameter of kind * -> *
scala> type World[M[_]] = M[Int]
defined type alias World

// So we use a lambda lambda that partially applies Foo on one parameter
// to yield a type of kind * -> *
scala> type X[A] = World[({ type M[A] = Foo[String, A] })#M]
defined type alias X

// Test the equality of two types. (If this compiles, it means they're equal.)
scala> implicitly[X[Int] =:= Foo[String, Int]]
res2: =:=[X[Int],Foo[String,Int]] = <function1>

সম্পাদনা:

আরও মান স্তর এবং ধরণের স্তরের সমান্তরাল।

// VALUE LEVEL

// Instead of a lambda, you can define a named function beforehand...
scala> val g: String => String = x => foo("hello", x)
g: String => String = <function1>

// ...and use it.
scala> world(g)
res3: String = hello world

// TYPE LEVEL

// Same applies at type level too.
scala> type G[A] = Foo[String, A]
defined type alias G

scala> implicitly[X =:= Foo[String, Int]]
res5: =:=[X,Foo[String,Int]] = <function1>

scala> type T = World[G]
defined type alias T

scala> implicitly[T =:= Foo[String, Int]]
res6: =:=[T,Foo[String,Int]] = <function1>

আপনি যে ক্ষেত্রে উপস্থাপন করেছেন সে ক্ষেত্রে, টাইপ প্যারামিটারটি Rস্থানীয়ভাবে কাজ করতে পারে Tuple2Pureএবং তাই আপনি কেবল সংজ্ঞা দিতে পারবেন না type PartialTuple2[A] = Tuple2[R, A], কারণ এমন কোনও স্থান নেই যেখানে আপনি এই প্রতিশব্দটি রাখতে পারেন।

এই জাতীয় কেস মোকাবেলার জন্য, আমি নিম্নলিখিত কৌশলগুলি ব্যবহার করি যা টাইপ সদস্যদের ব্যবহার করে। (আশা করি উদাহরণটি স্ব-ব্যাখ্যামূলক)

scala> type Partial2[F[_, _], A] = {
     |   type Get[B] = F[A, B]
     | }
defined type alias Partial2

scala> implicit def Tuple2Pure[R]: Pure[Partial2[Tuple2, R]#Get] = sys.error("")
Tuple2Pure: [R]=> Pure[[B](R, B)]

0

type World[M[_]] = M[Int]যে যাই হোক না কেন আমরা রাখা ঘটায় Aমধ্যে সবসময় সত্য আমি অনুমান করা হয়।X[A]implicitly[X[A] =:= Foo[String,Int]]

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.