উদাহরণ কেন সংকলন করে না, ওরফে কীভাবে (সহ-, বিপরীতে- এবং ইন-) বৈকল্পিকতা কাজ করে?


147

এই প্রশ্নটি অনুসরণ করে , কেউ স্কালায় নিম্নলিখিতটি ব্যাখ্যা করতে পারেন:

class Slot[+T] (var some: T) { 
   //  DOES NOT COMPILE 
   //  "COVARIANT parameter in CONTRAVARIANT position"

}

আমি প্রকারের ঘোষণার মধ্যে +Tএবং এর Tমধ্যে পার্থক্য বুঝতে পারি (এটি যদি আমি ব্যবহার করি তবে এটি সংকলন করে T)। কিন্তু তারপরে কেউ কীভাবে এমন একটি ক্লাস লিখতে পারে যা তার প্রকারের প্যারামিটারে কোভারিয়েন্ট থাকে সেটিকে অপরিশোধিত জিনিসটি তৈরি না করে অবলম্বন না করে ? নিম্নলিখিতটি কেবলমাত্র একটি উদাহরণ দিয়ে তৈরি করা যেতে পারে তা আমি কীভাবে নিশ্চিত করতে পারি T?

class Slot[+T] (var some: Object){    
  def get() = { some.asInstanceOf[T] }
}

সম্পাদনা - এখন এটি নীচে নেমে গেছে:

abstract class _Slot[+T, V <: T] (var some: V) {
    def getT() = { some }
}

এটি সব ভাল, তবে আমার এখন দুটি ধরণের পরামিতি রয়েছে, যেখানে আমি কেবল একটি চাই। আমি আবার প্রশ্ন জিজ্ঞাসা করব:

আমি কীভাবে অপরিবর্তনীয় Slot শ্রেণি লিখতে পারি যা এর ধরণের সমবায় ?

সম্পাদনা 2 : দুহ! আমি ব্যবহার করেছি varএবং না val। নিম্নলিখিতটি আমি যা চেয়েছিলাম তা হল:

class Slot[+T] (val some: T) { 
}

6
কারণ varস্থিরযোগ্য যদিও valনেই। স্ক্যালালের অপরিবর্তনীয় সংগ্রহগুলি সমবায় এবং একইভাবে পরিবর্তনীয় সংগ্রহগুলি একই কারণ নয়।
অক্সবো_লাক্স

এটি এই প্রসঙ্গে আকর্ষণীয় হতে পারে: scala-lang.org/old/node/129
ব্যবহারকারী573215

উত্তর:


302

সাধারণভাবে, একটি কোভেরিয়েন্ট টাইপ প্যারামিটার এমন একটি যা ক্লাসটি সাব-টাইপ করা হয়েছে (বিকল্পভাবে, সাব টাইপিংয়ের সাথে পৃথক হয়, তাই "সহ-উপসর্গ) থাকে down আরও দৃ concrete়ভাবে:

trait List[+A]

List[Int]এর একটি উপপ্রকার List[AnyVal]কারণ Intএটি একটি উপপ্রকার AnyVal। এর অর্থ হ'ল আপনি List[Int]যখন কোনও ধরণের মান List[AnyVal]আশা করা যায় তার একটি উদাহরণ সরবরাহ করতে পারেন । জেনেরিকদের কাজ করার জন্য এটি সত্যই একটি স্বজ্ঞাত উপায়, তবে এটি মিলে যায় এমন ডেটা উপস্থিতি ব্যবহার করার সময় এটি নিরবচ্ছিন্ন (টাইপ সিস্টেমটি বিরতি দেয়) হয়ে যায়। এজন্য জেনারিকস জাভাতে আক্রমণকারী। জাভা অ্যারেগুলি ব্যবহার করে অস্পষ্টতার সংক্ষিপ্ত উদাহরণ (যা ভ্রান্তভাবে সহকর্মী হয়):

Object[] arr = new Integer[1];
arr[0] = "Hello, there!";

আমরা সবেমাত্র টাইপের Stringএকটি অ্যারে টাইপের মান নির্ধারণ করেছি Integer[]। যে কারণে স্পষ্ট হওয়া উচিত, এটি খারাপ সংবাদ। জাভা টাইপ সিস্টেম আসলে এটি সংকলন সময়ে অনুমতি দেয়। জেভিএম ArrayStoreExceptionরানটাইমটিতে "সহায়তা করে" একটি নিক্ষেপ করবে । স্কালার টাইপ সিস্টেমটি এই সমস্যাটিকে প্রতিরোধ করে কারণ Arrayক্লাসে টাইপ পরামিতি অবিস্মরণীয় (ঘোষণার [A]পরিবর্তে হয় [+A])।

নোট করুন যে আরও একটি ধরণের বৈকল্পিক রয়েছে যা বিপরীত হিসাবে পরিচিত । এটি অত্যন্ত গুরুত্বপূর্ণ কারণ এটি ব্যাখ্যা করে যে কেন সম্প্রদায় কিছু সমস্যা সৃষ্টি করতে পারে। Contravariance আক্ষরিক সহভেদাংক বিপরীত: পরামিতি পরিবর্তন হওয়ার সম্ভাবনা রয়েছে উর্ধ্বগামী subtyping সঙ্গে। এটি আংশিকভাবে অনেক কম সাধারণ কারণ এটি এতটা আন্তঃজ্ঞানযুক্ত, যদিও এটির একটি খুব গুরুত্বপূর্ণ প্রয়োগ রয়েছে: ফাংশন functions

trait Function1[-P, +R] {
  def apply(p: P): R
}

টাইপ প্যারামিটারে " - " ভেরিয়েন্ট টীকাটি লক্ষ্য করুন P। একটি সম্পূর্ণ উপায়ে যে হিসাবে এই ঘোষণা Function1মধ্যে contravariant হয় Pএবং covariant R। সুতরাং, আমরা নিম্নলিখিত অক্ষরেখা পেতে পারি:

T1' <: T1
T2 <: T2'
---------------------------------------- S-Fun
Function1[T1, T2] <: Function1[T1', T2']

লক্ষ করুন যে, T1'একটি উপপ্রকার (অথবা একই ধরনের) হবে T1, যেহেতু এটি জন্য বিপরীত T2এবং T2'। ইংরাজীতে, এটি নিম্নলিখিত হিসাবে পড়া যেতে পারে:

একটি ফাংশন একটি অন্য ফাংশন একটি উপপ্রকার হয় বি যদি এর প্যারামিটার প্রকার একজন এর প্যারামিটার প্রকার একটি supertype হয় বি যখন এর রিটার্ন টাইপ একজন ফেরত ধরনের উপপ্রকার হয় বি

এই নিয়মের কারণ পাঠকের কাছে অনুশীলন হিসাবে ছেড়ে দেওয়া হয়েছে (ইঙ্গিত: ফাংশনগুলি সাব টাইপ করা হওয়ায় বিভিন্ন বিষয় সম্পর্কে চিন্তা করুন, যেমন উপরে থেকে আমার অ্যারের উদাহরণ হিসাবে)।

আপনার নতুন-সহ-ধারণা ও বৈপরীত্য সম্পর্কিত জ্ঞানের সাথে, নীচের উদাহরণটি কেন সংকলন করবে না তা আপনি দেখতে সক্ষম হবেন:

trait List[+A] {
  def cons(hd: A): List[A]
}

সমস্যাটি হ'ল Aকোভেরিয়েন্ট, যখন consফাংশনটি প্রত্যাশা করে যে এর ধরণের প্যারামিটারটি অদম্য । সুতরাং, Aভুল দিক বিভিন্ন হয়। আকর্ষণীয়ভাবে যথেষ্ট, আমরা Listবৈষম্য তৈরি করে এই সমস্যাটি সমাধান করতে পারতাম A, তবে ফাংশনটি তার রিটার্নের ধরনটি সমবায়িকList[A] হিসাবে consপ্রত্যাশা করে বলে প্রত্যাবর্তনের ধরণটি অবৈধ হবে ।

আমাদের এখানে কেবল দুটি অপশন Aহ'ল: কমনীয়তার দুর্দান্ত, স্বজ্ঞাত উপ-টাইপিং বৈশিষ্ট্যগুলি হারাতে, বা বিভ্রান্তি তৈরি করা, বা খ) consপদ্ধতিতে একটি স্থানীয় টাইপ প্যারামিটার যুক্ত করা যা Aনিম্ন সীমা হিসাবে সংজ্ঞায়িত করে :

def cons[B >: A](v: B): List[B]

এটি এখন বৈধ। আপনি কল্পনা করতে পারেন যে Aনীচের দিকে পরিবর্তিত হয়, তবে Bএটি নিম্ন-সীমাবদ্ধ Aথেকে সম্মানের সাথে wardর্ধ্বমুখী পরিবর্তিত করতে সক্ষম A। এই পদ্ধতির ঘোষণার সাথে আমরা Aসমবায় হতে পারি এবং সমস্ত কিছু কার্যকর হয়।

লক্ষ্য করুন এই কৌতুক শুধুমাত্র কাজ করে যদি আমরা একটি দৃষ্টান্ত ফেরত Listযা কম-নির্দিষ্ট ধরনের উপর বিশেষজ্ঞ B। আপনি যদি Listপরিবর্তনীয় করার চেষ্টা করেন তবে জিনিসগুলি ভেঙে যায় যেহেতু আপনি টাইপের মানকে টাইপের Bএকটি ভেরিয়েবলের জন্য নির্ধারণের চেষ্টা শেষ করেন A, যা সংকলক দ্বারা অনুমোদিত নয়। আপনি যখনই পরিবর্তন আনতে পারেন, আপনার কোনও ধরণের মিউটেটর থাকা দরকার, যার জন্য নির্দিষ্ট ধরণের একটি পদ্ধতি প্যারামিটার প্রয়োজন হয়, যা (একসেসরের সাথে একসাথে) অদম্যতা বোঝায়। কোভারিয়েন্স অপরিবর্তনীয় ডেটা নিয়ে কাজ করে যেহেতু একমাত্র সম্ভাব্য অপারেশনটি একজন অ্যাক্সেসর, যা একটি সমবায় রিটার্ন টাইপ দেওয়া যেতে পারে।


4
এটি কি সরল ইংরেজী হিসাবে বলা যেতে পারে - আপনি প্যারামিটার হিসাবে আরও সহজ কিছু নিতে পারেন এবং আপনি আরও জটিল কিছু ফিরিয়ে দিতে পারেন?
ফিল

1
জাভা সংকলক (1.7.0) "অবজেক্ট [] আরার = নতুন ইনট [1] সংকলন করে না;" বরং ত্রুটি বার্তাটি দেয়: "জাভা: বেমানান প্রকারগুলি প্রয়োজন: java.lang.Object [] পাওয়া গেছে: int []"। আমি মনে করি আপনি "অবজেক্ট [] আরার = নতুন পূর্ণসংখ্যার [1];" বোঝানো হয়েছে।
এমেরে সেভিনç

2
যখন আপনি উল্লেখ করেছিলেন, "এই নিয়মের কারণটি পাঠকের কাছে অনুশীলন হিসাবে ছেড়ে গেছে (ইঙ্গিত: ফাংশনগুলি সাব টাইপ করা হওয়ায় বিভিন্ন ক্ষেত্রে চিন্তা করুন, যেমন উপরে থেকে আমার অ্যারের উদাহরণ হিসাবে)" " আপনি আসলে একটি দম্পতি উদাহরণ দিতে পারেন?
পেরিঝেং

2
প্রতি @perryzheng এই গ্রহণ trait Animal, trait Cow extends Animal, def iNeedACowHerder(herder: Cow => Unit, c: Cow) = herder(c)এবং def iNeedAnAnimalHerder(herder: Animal => Unit, a: Animal) = herder(a)। তারপরে, iNeedACowHerder({ a: Animal => println("I can herd any animal, including cows") }, new Cow {})ঠিক আছে, আমাদের পশুর পাল যেমন গরু পাল করতে পারে iNeedAnAnimalHerder({ c: Cow => println("I can herd only cows, not any animal") }, new Animal {})তবে একটি সংকলন ত্রুটি দেয় কারণ আমাদের গরু পালক সমস্ত প্রাণীকে পশুপাল করতে পারে না।
লাসফ

এটি সম্পর্কিত এবং আমাকে বৈকল্পিক সাহায্য করেছে: typlevel.org/blog/2016/02/04/variance- এবং-functors.html
পিটার

27

@ ড্যানিয়েল এটি খুব ভালভাবে ব্যাখ্যা করেছেন। তবে সংক্ষেপে এটি ব্যাখ্যা করার জন্য, যদি এটি অনুমোদিত হয়:

  class Slot[+T](var some: T) {
    def get: T = some   
  }

  val slot: Slot[Dog] = new Slot[Dog](new Dog)   
  val slot2: Slot[Animal] = slot  //because of co-variance 
  slot2.some = new Animal   //legal as some is a var
  slot.get ??

slot.getতারপরে রানটাইমের সময় একটি ত্রুটি ফেলে দেবে কারণ এটি কোনও (দুহ!) রূপান্তর Animalকরতে ব্যর্থ হয়েছিল Dog

সাধারণ পরিবর্তনের সাথে সহ-প্রকরণ এবং বিপরীতে বৈকল্পিকতা ভাল হয় না। এই কারণেই সমস্ত জাভা সংগ্রহগুলি অবিচ্ছিন্ন।


7

এর সম্পূর্ণ আলোচনার জন্য উদাহরণস্বরূপ স্কেলা দেখুন , পৃষ্ঠা 57+।

আমি যদি আপনার মন্তব্যটি সঠিকভাবে বুঝতে পারি তবে আপনার 56 পৃষ্ঠার নীচে থেকে প্যাসেজটি পুনরায় পাঠ করা দরকার (মূলত, আমার মনে হয় আপনি যা চান তা রান টাইম চেক ছাড়া টাইপ-নিরাপদ নয়, যা স্কেলা করে না, সুতরাং আপনার ভাগ্যের বাইরে)। আপনার নির্মাণ ব্যবহার করার জন্য তাদের উদাহরণ অনুবাদ করা:

val x = new Slot[String]("test") // Make a slot
val y: Slot[Any] = x             // Ok, 'cause String is a subtype of Any
y.set(new Rational(1, 2))        // Works, but now x.get() will blow up 

আপনি যদি মনে করেন আমি আপনার প্রশ্নটি বুঝতে পারছি না (একটি স্বতন্ত্র সম্ভাবনা), সমস্যা বর্ণনায় আরও ব্যাখ্যা / প্রসঙ্গ যুক্ত করার চেষ্টা করুন এবং আমি আবার চেষ্টা করব।

আপনার সম্পাদনার প্রতিক্রিয়া: অপরিবর্তনীয় স্লট সম্পূর্ণ ভিন্ন পরিস্থিতি ... * হাসি * আশা করি উপরের উদাহরণটি সাহায্য করেছে।


আমি তা পড়েছি; দুর্ভাগ্যক্রমে আমি (এখনও) বুঝতে পারি না আমি উপরে যা জিজ্ঞাসা করব তা কীভাবে করতে পারি (উদাহরণস্বরূপ টি তে একটি
প্যারামাইট্রাইজড

আমি বুঝতে পারছিলাম যে এটি কিছুটা কঠোর। আমার প্রশ্ন (গুলি) এ পরিষ্কার হওয়া উচিত ছিল যে আমি উদাহরণস্বরূপ স্কালার বিটগুলি পড়েছি; আমি কেবল এটি "স্বল্প-আনুষ্ঠানিক" পদ্ধতিতে ব্যাখ্যা করতে চেয়েছিলাম
অক্সবো_লাক

@oxbow_lakes হাসা আমি আশঙ্কা Scala উদাহরণস্বরুপ হয় কম প্রথাগত ব্যাখ্যা।
সর্বোপরি,

দুঃখিত - আমার স্লটটি পরিবর্তনযোগ্য হতে চাই না। আমি ঠিক বুঝতে পেরেছি যে সমস্যাটি আমি ভ্যার হিসাবে ঘোষণা করেছি এবং ভ্যাল নয়
অক্সবো_লাক

3

প্যারামিটারে আপনাকে নিম্ন সীমা প্রয়োগ করতে হবে। সিনট্যাক্সটি মনে করতে আমার খুব কষ্ট হচ্ছে, তবে আমি মনে করি এটি দেখতে এরকম কিছু হবে:

class Slot[+T, V <: T](var some: V) {
  //blah
}

স্কাল-বাই-উদাহরণটি বোঝা কিছুটা কঠিন, কয়েকটি দৃ concrete় উদাহরণ সাহায্য করবে।

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