সংক্ষিপ্ত বিবরণ
প্রথাগত, মান-স্তরের প্রোগ্রামিংয়ের সাথে টাইপ-লেভেল প্রোগ্রামিংয়ের অনেক মিল রয়েছে। যাইহোক, মান-স্তরের প্রোগ্রামিংয়ের বিপরীতে, যেখানে টাইপ-লেভেল প্রোগ্রামিংয়ে রানটাইমের সময় গণনা ঘটে, সংকলনের সময় গণনাটি ঘটে। আমি মান-স্তরে প্রোগ্রামিং এবং টাইপ-স্তরে প্রোগ্রামিংয়ের মধ্যে সমান্তরাল আঁকতে চেষ্টা করব।
paradigms
টাইপ-লেভেল প্রোগ্রামিংয়ে দুটি প্রধান দৃষ্টান্ত রয়েছে: "অবজেক্ট-ওরিয়েন্টেড" এবং "ফাংশনাল"। এখান থেকে যুক্ত বেশিরভাগ উদাহরণগুলি অবজেক্ট-ভিত্তিক দৃষ্টান্ত অনুসরণ করে।
অবজেক্ট-ভিত্তিক দৃষ্টান্তে টাইপ-লেভেল প্রোগ্রামিংয়ের একটি ভাল, মোটামুটি সহজ উদাহরণটি লম্বা ক্যালকুলাসের অ্যাপোক্যালিস্পের প্রয়োগে এখানে প্রতিলিপি পাওয়া যায়:
// Abstract trait
trait Lambda {
type subst[U <: Lambda] <: Lambda
type apply[U <: Lambda] <: Lambda
type eval <: Lambda
}
// Implementations
trait App[S <: Lambda, T <: Lambda] extends Lambda {
type subst[U <: Lambda] = App[S#subst[U], T#subst[U]]
type apply[U] = Nothing
type eval = S#eval#apply[T]
}
trait Lam[T <: Lambda] extends Lambda {
type subst[U <: Lambda] = Lam[T]
type apply[U <: Lambda] = T#subst[U]#eval
type eval = Lam[T]
}
trait X extends Lambda {
type subst[U <: Lambda] = U
type apply[U] = Lambda
type eval = X
}
উদাহরণে দেখা যাবে, টাইপ-লেভেল প্রোগ্রামিংয়ের জন্য অবজেক্ট-ওরিয়েন্টেড দৃষ্টান্তটি নিম্নরূপ:
- প্রথম: বিভিন্ন বিমূর্ত প্রকার ক্ষেত্রের সাথে একটি বিমূর্ত বৈশিষ্ট্য সংজ্ঞায়িত করুন (বিমূর্ত ক্ষেত্রটি কী তা জন্য নীচে দেখুন)। এটি গ্যারান্টিযুক্ত করার জন্য একটি টেমপ্লেট যা নির্দিষ্টকরণের ক্ষেত্রগুলি বাস্তবায়নের জন্য জোর না করে সমস্ত বাস্তবায়নে উপস্থিত রয়েছে। ল্যামডা ক্যালকুলাস উদাহরণে, এই অনুরূপ
trait Lambdaযে নিশ্চয়তা যে নিম্নলিখিত প্রকারের অস্তিত্ব: subst, apply, এবং eval।
- পরবর্তী: সাবট্রাইটগুলি সংজ্ঞায়িত করুন যা বিমূর্ত বৈশিষ্ট্য প্রসারিত করে এবং বিভিন্ন বিমূর্ত প্রকার ক্ষেত্র প্রয়োগ করে
- প্রায়শই, এই সাবট্রাইটগুলি আর্গুমেন্টগুলির সাথে পরামিতি করা হবে। ল্যাম্বডা ক্যালকুলাস উদাহরণে, উপ-প্রকারগুলি
trait App extends Lambdaদুটি ধরণের ( Sএবং Tউভয়েরই অবশ্যই সাবাইটাইপস Lambda), trait Lam extends Lambdaএক প্রকার ( T) এবং trait X extends Lambda(যা প্যারামিটারাইজড নয়) দিয়ে প্যারামিটারাইজড হয়।
- প্রকার ক্ষেত্রগুলি প্রায়শই সাবট্রাইটের ধরণের পরামিতিগুলি উল্লেখ করে এবং কখনও কখনও হ্যাশ অপারেটরের মাধ্যমে তাদের ধরণের ক্ষেত্রগুলি উল্লেখ করে প্রয়োগ করা হয়:
#(যা বিন্দু অপারেটরের সাথে খুব মিল: .মানগুলির জন্য)। বৈশিষ্ট্য সালে Appল্যামডা ক্যালকুলাস উদাহরণস্বরূপ, টাইপ evalনিম্নরূপ বাস্তবায়িত হয়: type eval = S#eval#apply[T]। এটি মূলত evalবৈশিষ্ট্যের প্যারামিটারের প্রকারটি Sকল করে এবং ফলাফলের applyসাথে প্যারামিটারের সাথে কল Tকরে। দ্রষ্টব্য, Sকোনও evalপ্রকারের গ্যারান্টিযুক্ত কারণ প্যারামিটারটি এটির একটি সাব টাইপ হিসাবে নির্দিষ্ট করে Lambda। একইভাবে, ফলাফলটির evalঅবশ্যই একটি applyপ্রকার থাকতে হবে , যেহেতু এটি Lambdaবিমূর্ত বৈশিষ্ট্যে উল্লিখিত হিসাবে এর একটি উপ-টাইপ হিসাবে নির্দিষ্ট করা হয়েছে Lambda।
কার্যকরী দৃষ্টান্তে বৈশিষ্ট্যগুলিতে একত্রে গোষ্ঠীভুক্ত নয় এমন অনেকগুলি প্যারামিটারাইজড ধরণের নির্মাণকারীর সংজ্ঞা দেওয়া থাকে।
মান-স্তর প্রোগ্রামিং এবং টাইপ-লেভেল প্রোগ্রামিংয়ের মধ্যে তুলনা
- বিমূর্ত ক্লাস
- মান-স্তর:
abstract class C { val x }
- টাইপ-স্তর:
trait C { type X }
- পথ নির্ভর প্রকার
C.x (অবজেক্ট সিতে ক্ষেত্রের মান / ফাংশন x উল্লেখ করা)
C#x (বৈশিষ্ট্য সিতে ক্ষেত্রের প্রকারের রেফারেন্সিং)
- ফাংশন স্বাক্ষর (বাস্তবায়ন নেই)
- মান-স্তর:
def f(x:X) : Y
- টাইপ-লেভেল:
type f[x <: X] <: Y(এটিকে "টাইপ কনস্ট্রাক্টর" বলা হয় এবং এটি সাধারণত বিমূর্ত বৈশিষ্ট্যে দেখা যায়)
- ফাংশন বাস্তবায়ন
- মান-স্তর:
def f(x:X) : Y = x
- টাইপ-স্তর:
type f[x <: X] = x
- কন্ডিশন
- সমতা পরীক্ষা করা হচ্ছে
- মান-স্তর:
a:A == b:B
- টাইপ-স্তর:
implicitly[A =:= B]
- মান-স্তর: রানটাইমে (যেমন কোনও রানটাইম ত্রুটি নেই) জেভিএম-এ ইউনিট পরীক্ষার মাধ্যমে ঘটে:
- প্রয়োজনীয়তা একটি জোর দেওয়া হয়:
assert(a == b)
- টাইপ-লেভেল: টাইপেকের মাধ্যমে সংকলকটিতে ঘটে (অর্থাত্ কোনও সংকলক ত্রুটি নেই):
- সংক্ষেপে একটি প্রকারের তুলনা: যেমন
implicitly[A =:= B]
A <:< B, কেবলমাত্র Aএকটি সাব টাইপ হলে সংকলন করেB
A =:= B, কেবল তখনই সংকলন করে যদি Aএকটি উপপ্রকার Bএবং Bএর একটি উপপ্রকার হয়A
A <%< B, ("হিসাবে দেখতে পঠনযোগ্য") কেবল তখনই Aসংকলিত হয় B(যেমন Aএকটি উপ-প্রকারের মধ্যে অন্তর্ভুক্ত রূপান্তর রয়েছে B)
- একটি উদাহরণ
- আরও তুলনা অপারেটর
প্রকার এবং মানগুলির মধ্যে রূপান্তর করা
অনেক উদাহরণে, বৈশিষ্ট্যের মাধ্যমে সংজ্ঞায়িত প্রকারগুলি প্রায়শই বিমূর্ত এবং সীলমোহরযুক্ত হয় এবং তাই সরাসরি বা বেনামে সাবক্লাসের মাধ্যমে ইনস্ট্যান্ট করা যায় না। সুতরাং nullকিছু ধরণের আগ্রহ ব্যবহার করে কোনও মান-স্তরের গণনা করার সময় স্থানধারক মান হিসাবে ব্যবহার করা সাধারণ :
- যেমন
val x:A = null, যেখানে Aটাইপ আপনার পছন্দের হয়
টাইপ-ইরেজরের কারণে, প্যারামিটারাইজড টাইপগুলি একই রকম দেখাচ্ছে। তদ্ব্যতীত, (উপরে উল্লিখিত হিসাবে) আপনি যে মানগুলির সাথে কাজ করছেন সেগুলি সকলেরই প্রবণতা null, এবং সুতরাং অবজেক্টের ধরণের (যেমন ম্যাচের বিবরণের মাধ্যমে) কন্ডিশনিং অকার্যকর।
কৌশলটি অন্তর্ভুক্ত ফাংশন এবং মানগুলি ব্যবহার করা। বেস কেসটি সাধারণত একটি অন্তর্নিহিত মান এবং পুনরাবৃত্তির ক্ষেত্রে সাধারণত একটি অন্তর্ভুক্ত ফাংশন হয়। প্রকৃতপক্ষে, টাইপ-লেভেল প্রোগ্রামিং জটিলতার ভারী ব্যবহার করে।
এই উদাহরণটি বিবেচনা করুন ( মেটাসকালা এবং অ্যাপোক্যালিস্প থেকে নেওয়া ):
sealed trait Nat
sealed trait _0 extends Nat
sealed trait Succ[N <: Nat] extends Nat
এখানে আপনার কাছে প্রাকৃতিক সংখ্যাগুলির একটি পেরো এনকোডিং রয়েছে। এটি হ'ল, আপনার প্রতিটি অ-নেতিবাচক পূর্ণসংখ্যার জন্য একটি টাইপ রয়েছে: 0 এর জন্য একটি বিশেষ প্রকার, যথা _0; এবং শূন্যের চেয়ে বড় প্রতিটি পূর্ণসংখ্যার ফর্মের একটি প্রকার থাকে Succ[A], যেখানে Aটাইপটি একটি ছোট পূর্ণসংখ্যা উপস্থাপন করে। উদাহরণস্বরূপ, 2 প্রতিনিধিত্বকারী টাইপটি Succ[Succ[_0]]হ'ল : (শূন্যটি উপস্থাপনকারী ধরণের ক্ষেত্রে উত্তরসূরি দুইবার প্রয়োগ করা)।
আরও সুবিধাজনক রেফারেন্সের জন্য আমরা বিভিন্ন প্রাকৃতিক সংখ্যা উপস্থাপন করতে পারি। উদাহরণ:
type _3 = Succ[Succ[Succ[_0]]]
(এটি অনেকটা valকোনও ফাংশনের ফলাফল হিসাবে সংজ্ঞায়িত করার মতো is )
এখন ধরুন, আমরা একটি মান-স্তরের ফাংশনটি সংজ্ঞায়িত করতে চাই def toInt[T <: Nat](v : T)যা একটি আর্গুমেন্টের মানটি গ্রহণ করে v, যা সেই ধরণের এনকোডড Natপ্রাকৃতিক সংখ্যার প্রতিনিধিত্ব করে এমন একটি পূর্ণসংখ্যার সাথে মানিয়ে যায় এবং প্রদান করে v। উদাহরণস্বরূপ, যদি আমাদের মান val x:_3 = null( nullটাইপ Succ[Succ[Succ[_0]]]) থাকে তবে আমরা toInt(x)ফিরে আসতে চাই 3।
বাস্তবায়নের জন্য toInt, আমরা নিম্নলিখিত ক্লাসটি ব্যবহার করতে যাচ্ছি:
class TypeToValue[T, VT](value : VT) { def getValue() = value }
আমরা নীচে দেখতে হবে, সেখানে একটি বস্তু ক্লাস থেকে নির্মাণকাজ শেষ হবে TypeToValueপ্রত্যেকের জন্য Natথেকে _0আপ (যেমন) এর _3, এবং প্রতিটি সংশ্লিষ্ট টাইপ (অর্থাত মান প্রতিনিধিত্ব সংরক্ষণ করবে TypeToValue[_0, Int]মান সংরক্ষণ করবে 0, TypeToValue[Succ[_0], Int]মান সংরক্ষণ করবে 1, ইত্যাদি)। দ্রষ্টব্য, TypeToValueদুটি ধরণের দ্বারা পরামিতি করা হয়: Tএবং VT। Tআমরা যে ধরণের মান নির্ধারণের চেষ্টা করছি তার সাথে সামঞ্জস্যপূর্ণ (আমাদের উদাহরণস্বরূপ Nat) এবং VTআমরা এটির জন্য যে মূল্য নির্ধারণ করেছি তার সাথে সামঞ্জস্য করে (আমাদের উদাহরণে, Int)।
এখন আমরা নিম্নলিখিত দুটি অন্তর্নিহিত সংজ্ঞা দিচ্ছি:
implicit val _0ToInt = new TypeToValue[_0, Int](0)
implicit def succToInt[P <: Nat](implicit v : TypeToValue[P, Int]) =
new TypeToValue[Succ[P], Int](1 + v.getValue())
এবং আমরা toIntনিম্নলিখিত হিসাবে বাস্তবায়ন :
def toInt[T <: Nat](v : T)(implicit ttv : TypeToValue[T, Int]) : Int = ttv.getValue()
কীভাবে toIntকাজ করে তা বোঝার জন্য আসুন এটি বিবেচনা করুন যে এটি কয়েকটা ইনপুটগুলিতে কী করে:
val z:_0 = null
val y:Succ[_0] = null
যখন আমরা কল করি toInt(z), সংকলক ttvপ্রকারের একটি অন্তর্নিহিত যুক্তি সন্ধান করে TypeToValue[_0, Int](যেহেতু zটাইপের হয় _0)। এটি বস্তুটি সন্ধান করে _0ToInt, এটি getValueএই বস্তুর পদ্ধতিটি কল করে এবং ফিরে আসে 0। গুরুত্বপূর্ণ লক্ষণীয় বিষয়টি হ'ল আমরা যে প্রোগ্রামটি ব্যবহার করব তা নির্দিষ্ট করেছিলাম না, সংকলকটি এটিকে স্পষ্টভাবে খুঁজে পেয়েছে।
এখন বিবেচনা করা যাক toInt(y)। এবার, সংকলক ttvপ্রকারের একটি অন্তর্নিহিত যুক্তি সন্ধান করে TypeToValue[Succ[_0], Int](যেহেতু yটাইপের হয় Succ[_0])। এটি ফাংশনটি সন্ধান করে succToInt, যা উপযুক্ত টাইপের কোনও বস্তু ( TypeToValue[Succ[_0], Int]) ফেরত দিতে পারে এবং মূল্যায়ন করতে পারে। এই ফাংশনটি নিজেই vপ্রকারের একটি অন্তর্নিহিত যুক্তি ( ) গ্রহণ করে TypeToValue[_0, Int](এটি TypeToValueযেখানে প্রথম ধরণের প্যারামিটারের পরিমাণ কম থাকে Succ[_])। সংকলক সরবরাহ করে _0ToInt( toInt(z)উপরের মূল্যায়নে যেমন হয়েছিল ), এবং মান সহ succToIntএকটি নতুন TypeToValueঅবজেক্ট তৈরি করে 1। আবার এটিও লক্ষ করা জরুরী যে সংকলকটি এই সমস্ত মানকে স্পষ্টতই সরবরাহ করছে, যেহেতু তাদের কাছে স্পষ্টভাবে আমাদের অ্যাক্সেস নেই।
আপনার কাজ চেক করা হচ্ছে
আপনার টাইপ-লেভেল গণনাগুলি আপনার প্রত্যাশা অনুযায়ী করছে তা যাচাই করার বিভিন্ন উপায় রয়েছে। এখানে কয়েকটি উপায় আছে। দুটি প্রকার তৈরি করুন Aএবং B, যা আপনি যাচাই করতে চান তা সমান। তারপরে নীচের সংকলনটি পরীক্ষা করুন:
Equal[A, B]
implicitly[A =:= B]
বিকল্পভাবে, আপনি টাইপটিকে কোনও মানের (যেমন উপরে দেখানো হয়েছে) রূপান্তর করতে পারেন এবং মানগুলির একটি রানটাইম চেক করতে পারেন। যেমন assert(toInt(a) == toInt(b)), যেখানে aধরনের হয় Aএবং bধরনের হয় B।
অতিরিক্ত সম্পদ
স্কেল রেফারেন্স ম্যানুয়াল (পিডিএফ) এর প্রকার বিভাগে উপলভ্য কনস্ট্রাক্টসের সম্পূর্ণ সেটটি পাওয়া যাবে ।
টাইপ কন্সট্রাক্টর এবং সম্পর্কিত বিষয় সম্পর্কিত অ্যাড্রিয়ান মুরসের বেশ কয়েকটি একাডেমিক পেপার রয়েছে যার সাথে স্কেলার উদাহরণ রয়েছে:
অ্যাপোক্যালিস্প এমন একটি ব্লগ যা স্কালায় টাইপ-লেভেল প্রোগ্রামিংয়ের অনেক উদাহরণ রয়েছে।
স্কাল্যাজ একটি অত্যন্ত সক্রিয় প্রকল্প যা কার্যকারিতা সরবরাহ করে যা বিভিন্ন ধরণের-স্তরের প্রোগ্রামিং বৈশিষ্ট্যগুলি ব্যবহার করে স্কালা এপিআই প্রসারিত করে। এটি একটি খুব আকর্ষণীয় প্রকল্প যার একটি বৃহত নিম্নলিখিত রয়েছে।
মেটাস্কালা স্কালার জন্য একটি টাইপ-স্তরের গ্রন্থাগার, যার মধ্যে প্রাকৃতিক সংখ্যা, বুলিয়ানস, ইউনিট, এইচলিস্ট ইত্যাদির জন্য মেটা প্রকার রয়েছে Jes এটি জেস্পার নর্ডেনবার্গের (তার ব্লগ) একটি প্রকল্প ।
Michid (ব্লগ) কিছু সন্ত্রস্ত (অন্যান্য উত্তর থেকে) Scala টাইপ স্তরের প্রোগ্রামিং উদাহরণ রয়েছে:
দেবাশীষ ঘোষের (ব্লগ) কিছু প্রাসঙ্গিক পোস্টও রয়েছে:
(আমি এই বিষয় নিয়ে কিছু গবেষণা করে চলেছি এবং আমি যা শিখেছি তা এখানে। আমি এখনও এটির জন্য নতুন so