স্ক্যালাস টাইপ Dynamicআপনাকে অস্তিত্বগুলিতে পদ্ধতি কল করতে অনুমতি দেয় যা বিদ্যমান নেই বা অন্য কথায় এটি গতিশীল ভাষায় "পদ্ধতি অনুপস্থিত" এর একটি প্রতিরূপ।
এটি সঠিক, scala.Dynamicকোনও সদস্য নেই, এটি কেবল একটি মার্কার ইন্টারফেস - কংক্রিট বাস্তবায়ন সংকলক দ্বারা পূরণ করা হয়েছে। স্কেলাস স্ট্রিং ইন্টারপোলেশন বৈশিষ্ট্যটির জন্য উত্পন্ন বাস্তবায়ন বর্ণনা করার জন্য বেশ কয়েকটি সংজ্ঞায়িত নিয়ম রয়েছে। আসলে, চারটি বিভিন্ন পদ্ধতি প্রয়োগ করতে পারে:
selectDynamic - ফিল্ড অ্যাকসেসর লিখতে দেয়: foo.bar
updateDynamic - ফিল্ড আপডেট লিখতে দেয়: foo.bar = 0
applyDynamic - যুক্তি সহ পদ্ধতিতে কল করতে দেয়: foo.bar(0)
applyDynamicNamed - নামযুক্ত আর্গুমেন্ট সহ পদ্ধতি কল করতে অনুমতি দেয়: foo.bar(f = 0)
এই পদ্ধতির একটি ব্যবহার করার জন্য এটি বর্ধিত শ্রেণি লিখতে Dynamicএবং সেখানে পদ্ধতিগুলি বাস্তবায়নের জন্য যথেষ্ট :
class DynImpl extends Dynamic {
}
তবুও একটি যুক্ত করা প্রয়োজন
import scala.language.dynamics
বা সংকলক বিকল্প সেট করুন -language:dynamicsকারণ বৈশিষ্ট্যটি ডিফল্টরূপে লুকানো থাকে।
নির্বাচন করুন ডায়নামিক
selectDynamicবাস্তবায়ন সবচেয়ে সহজ এক। সংকলক একটি কল অনুবাদ foo.barকরে foo.selectDynamic("bar"), সুতরাং এটি প্রয়োজন যে এই পদ্ধতিতে একটি প্রত্যাশা যুক্তি তালিকা রয়েছে String:
class DynImpl extends Dynamic {
def selectDynamic(name: String) = name
}
scala> val d = new DynImpl
d: DynImpl = DynImpl@6040af64
scala> d.foo
res37: String = foo
scala> d.bar
res38: String = bar
scala> d.selectDynamic("foo")
res54: String = foo
যেহেতু একজন দেখতে পাচ্ছেন, গতিশীল পদ্ধতিগুলি স্পষ্টভাবে কল করাও সম্ভব।
আপডেটডায়নামিক
কারণ updateDynamicএই পদ্ধতিটি ফিরে আসতে হবে এমন একটি মান আপডেট করতে ব্যবহৃত হয় Unit। তদুপরি, আপডেট করার জন্য ক্ষেত্রের নাম এবং এর মান সংকলক দ্বারা বিভিন্ন যুক্তি তালিকায় দেওয়া হয়:
class DynImpl extends Dynamic {
var map = Map.empty[String, Any]
def selectDynamic(name: String) =
map get name getOrElse sys.error("method not found")
def updateDynamic(name: String)(value: Any) {
map += name -> value
}
}
scala> val d = new DynImpl
d: DynImpl = DynImpl@7711a38f
scala> d.foo
java.lang.RuntimeException: method not found
scala> d.foo = 10
d.foo: Any = 10
scala> d.foo
res56: Any = 10
কোডটি প্রত্যাশা মতো কাজ করে - কোডটিতে রানটাইম সময়ে পদ্ধতি যুক্ত করা সম্ভব। অন্যদিকে, কোডটি আর আর টাইপসেফ নয় এবং যদি এমন কোনও পদ্ধতি বলা হয় যা বিদ্যমান নেই তবে এটি রানটাইমেও পরিচালনা করা উচিত। গতিশীল ভাষাগুলির মতো এই কোডটি তেমন কার্যকর নয় কারণ রানটাইমটিতে যে পদ্ধতিগুলি কল করা উচিত তা তৈরি করা সম্ভব নয়। এর অর্থ হ'ল আমরা এর মতো কিছু করতে পারি না
val name = "foo"
d.$name
যেখানে রানটাইমে d.$nameরূপান্তরিত হবে d.foo। তবে এটি এতটা খারাপ নয় কারণ গতিশীল ভাষাগুলিতেও এটি একটি বিপজ্জনক বৈশিষ্ট্য।
এখানে আরেকটি বিষয় লক্ষণীয়, তা হচ্ছে updateDynamicএটির সাথে একত্রে বাস্তবায়ন করা দরকার selectDynamic। যদি আমরা এটি না করি তবে আমরা একটি সংকলন ত্রুটিটি পেয়ে যাব - এই নিয়মটি সেটার বাস্তবায়নের অনুরূপ, যা কেবল একই নামের সাথে গেটর থাকলেই কাজ করে।
প্রয়োগডায়নামিক
আর্গুমেন্টের সাথে পদ্ধতিগুলি কল করার ক্ষমতা সরবরাহ করেছেন এটি দ্বারা applyDynamic:
class DynImpl extends Dynamic {
def applyDynamic(name: String)(args: Any*) =
s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"
}
scala> val d = new DynImpl
d: DynImpl = DynImpl@766bd19d
scala> d.ints(1, 2, 3)
res68: String = method 'ints' called with arguments '1', '2', '3'
scala> d.foo()
res69: String = method 'foo' called with arguments ''
scala> d.foo
<console>:19: error: value selectDynamic is not a member of DynImpl
পদ্ধতির নাম এবং তার যুক্তিগুলি আবার বিভিন্ন প্যারামিটার তালিকায় পৃথক করা হয়। আমরা চাইলে একটি স্বেচ্ছাসেবী সংখ্যার সাথে সালিস পদ্ধতিতে কল করতে পারি তবে আমরা যদি কোনও বন্ধন ছাড়াই কোনও পদ্ধতিতে কল করতে চাই তবে আমাদের প্রয়োগ করতে হবে selectDynamic।
ইঙ্গিত: এটি দিয়ে প্রয়োগ-সিনট্যাক্স ব্যবহার করাও সম্ভব applyDynamic:
scala> d(5)
res1: String = method 'apply' called with arguments '5'
প্রয়োগডায়নামিক নাম
সর্বশেষ উপলভ্য পদ্ধতিটি আমাদের চাইলে আমাদের যুক্তিগুলির নাম দেওয়ার অনুমতি দেয়:
class DynImpl extends Dynamic {
def applyDynamicNamed(name: String)(args: (String, Any)*) =
s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"
}
scala> val d = new DynImpl
d: DynImpl = DynImpl@123810d1
scala> d.ints(i1 = 1, i2 = 2, 3)
res73: String = method 'ints' called with arguments '(i1,1)', '(i2,2)', '(,3)'
পদ্ধতি স্বাক্ষরের পার্থক্যটি হ'ল applyDynamicNamedফর্মের টিপলগুলি (String, A)যেখানে Aএকটি স্বেচ্ছাসেবী প্রকারের প্রত্যাশা করে ।
উপরোক্ত সমস্ত পদ্ধতির সাধারণ বিষয় রয়েছে যে তাদের প্যারামিটারগুলি প্যারামিটারাইজ করা যেতে পারে:
class DynImpl extends Dynamic {
import reflect.runtime.universe._
def applyDynamic[A : TypeTag](name: String)(args: A*): A = name match {
case "sum" if typeOf[A] =:= typeOf[Int] =>
args.asInstanceOf[Seq[Int]].sum.asInstanceOf[A]
case "concat" if typeOf[A] =:= typeOf[String] =>
args.mkString.asInstanceOf[A]
}
}
scala> val d = new DynImpl
d: DynImpl = DynImpl@5d98e533
scala> d.sum(1, 2, 3)
res0: Int = 6
scala> d.concat("a", "b", "c")
res1: String = abc
ভাগ্যক্রমে, অন্তর্নিহিত যুক্তি যুক্ত করাও সম্ভব - যদি আমরা একটি TypeTagপ্রসঙ্গের সীমাবদ্ধ যুক্ত করি তবে আমরা সহজেই যুক্তিগুলির প্রকারগুলি পরীক্ষা করতে পারি। এবং সর্বোত্তম বিষয়টি হ'ল এমনকি রিটার্নের ধরণটিও সঠিক - যদিও আমাদের কিছু ক্যাসেট যোগ করতে হয়েছিল।
যখন এই ধরণের ত্রুটিগুলি খুঁজে পাওয়ার কোনও উপায় না থাকে তখন স্কেলা স্কাল হবে না। আমাদের ক্ষেত্রে কাস্টগুলি এড়ানোর জন্য আমরা টাইপ ক্লাস ব্যবহার করতে পারি:
object DynTypes {
sealed abstract class DynType[A] {
def exec(as: A*): A
}
implicit object SumType extends DynType[Int] {
def exec(as: Int*): Int = as.sum
}
implicit object ConcatType extends DynType[String] {
def exec(as: String*): String = as.mkString
}
}
class DynImpl extends Dynamic {
import reflect.runtime.universe._
import DynTypes._
def applyDynamic[A : TypeTag : DynType](name: String)(args: A*): A = name match {
case "sum" if typeOf[A] =:= typeOf[Int] =>
implicitly[DynType[A]].exec(args: _*)
case "concat" if typeOf[A] =:= typeOf[String] =>
implicitly[DynType[A]].exec(args: _*)
}
}
বাস্তবায়নটি দেখতে দুর্দান্ত দেখাচ্ছে না, তবে এর শক্তি নিয়ে প্রশ্ন করা যায় না:
scala> val d = new DynImpl
d: DynImpl = DynImpl@24a519a2
scala> d.sum(1, 2, 3)
res89: Int = 6
scala> d.concat("a", "b", "c")
res90: String = abc
সর্বোপরি, Dynamicম্যাক্রোগুলির সাথে একত্রিত হওয়াও সম্ভব :
class DynImpl extends Dynamic {
import language.experimental.macros
def applyDynamic[A](name: String)(args: A*): A = macro DynImpl.applyDynamic[A]
}
object DynImpl {
import reflect.macros.Context
import DynTypes._
def applyDynamic[A : c.WeakTypeTag](c: Context)(name: c.Expr[String])(args: c.Expr[A]*) = {
import c.universe._
val Literal(Constant(defName: String)) = name.tree
val res = defName match {
case "sum" if weakTypeOf[A] =:= weakTypeOf[Int] =>
val seq = args map(_.tree) map { case Literal(Constant(c: Int)) => c }
implicitly[DynType[Int]].exec(seq: _*)
case "concat" if weakTypeOf[A] =:= weakTypeOf[String] =>
val seq = args map(_.tree) map { case Literal(Constant(c: String)) => c }
implicitly[DynType[String]].exec(seq: _*)
case _ =>
val seq = args map(_.tree) map { case Literal(Constant(c)) => c }
c.abort(c.enclosingPosition, s"method '$defName' with args ${seq.mkString("'", "', '", "'")} doesn't exist")
}
c.Expr(Literal(Constant(res)))
}
}
scala> val d = new DynImpl
d: DynImpl = DynImpl@c487600
scala> d.sum(1, 2, 3)
res0: Int = 6
scala> d.concat("a", "b", "c")
res1: String = abc
scala> d.noexist("a", "b", "c")
<console>:11: error: method 'noexist' with args 'a', 'b', 'c' doesn't exist
d.noexist("a", "b", "c")
^
ম্যাক্রোস আমাদের সমস্ত সংকলন সময়ের গ্যারান্টি ফিরিয়ে দেয় এবং উপরের ক্ষেত্রে এটি এতটা কার্যকর না হলেও সম্ভবত এটি কিছু স্কালাল ডিএসএল-এর পক্ষে খুব কার্যকর হতে পারে।
আপনি আরও কিছু তথ্য পেতে চাইলে আরও তথ্য পেতে চান Dynamic: