بتـــــاريخ : 1/24/2011 12:29:05 AM
الفــــــــئة
  • الحـــــــــــاسب
  • التعليقات المشاهدات التقييمات
    0 1129 0


    ال Dynamic Typing و ال Static Typing في سطور! من النظرية إلى التطبيق!

    الناقل : elmasry | العمر :42 | الكاتب الأصلى : Khaled.Alshaya | المصدر : www.arabteam2000-forum.com

    كلمات مفتاحية  :

    بسم الله الرحمن الرحيم,

    في الجزء الأول من هذا الموضوع, بدأنا حديثاً موجزاً عن الـ Type Safety و الـ Strong Typing في مقابل الـ Weak Typing.و حديثنا في هذا الموضوع عن الـ Static Typing في مقابل الـ Dynamic Typing. الغريب أنك إن لم تكن قرأت من قبل حول هذا الموضوع في مصادر "حقيقية" فسيصبك الإحباط, لتناقض المعلومات حوله. إذاً ماذا نعني عندما نقول أن هذه اللغة Statically Typed؟ و ماذا نعني عندما نقول أن هذه اللغة Dynamically Typed؟ لربما كانت الإجابة على "طرف لسانك" لو أنك قرأت عن الموضوع من قبل, و لكن: هل الإجابة هي أن كل لغة مترجمة هي لغة Statically Typed و كل لغة مفسرة هي Dynamically Typed؟! ماذا عن اللغات المترجمة وقت الشغيل Jitted!!!

    الـ Dynamic Typing عندما نتكلم عن أقصى معنى لها, تعني أن أي متغير في البرنامج النهائي أو بمعنى أصح at run-time يحمل جميع المعلومات الأخرى غير القيمة التي يحملها أساساً, مثل اسمه "التمثيل النصي لاسمه", نوعه, حجمه, الأنواع التي يرثها كما في OOP, و هكذا و عندما تكون اللغة Dynamically حتى النخاع فإن جميع المعلومات التي يمكن أن تستقيها من الكود يمكن أن تحصل عليها أو تعدلها بطريقة أو بأخرى عند تشغيل البرنامج at run-time. فالمتغير ليس فحسب, مكاناً توضع فيه قيمة و حسب و إنما data structure متكاملة تحمل جميع المعلومات التي تحددها اللغة إضافة إلى القيمة التي يفترض أن يحملها. هذا الأمر يوفر ميزات كثيرة و عيوب كثيرة أيضاً, فالعيب واضح, فلن يعود بإمكاننا استخدام الذاكرة بشكل كفؤ لأن المتغير يملأ حيزاً كبيرة, و ربما يكون حجم تلك المعلومات الإضافية أضعافاً مضاعفة من الحجم اللازم لحمل قيمة عدد صحيح int مثلاً. هذا الأمر يظهر عندما يكون البرنامج يعالج كمية كبيرة نسبياً من البيانات. و لكن الميزات كثيرة! فمثلاً تصور أننا نستخدم لغة تشبه ++C في الـ Syntax و لكنها Dynamic و أردنا كتابة دالة تقوم باستدعاء دالة تسمى print لكائن ما:
    expand | plain text

    void call_print(object)
    { object.print(); }


    هل ترى! الـ Generic Programming في أوج عظمتها :)
    سيتم البحث عن دالة اسمها print في معلومات الكائن المسمى object, ثم سيتم إحضار عنوانها و يتم ندائها, و في الغالب العمليات أكثر تعقيداً و اللغة تصبح مرنة إلى أبعد الحدود. لكن هناك سؤال, هل تستطيع دفع تكلفة ظهور الأخطاء وقت تشغيل البرنامج؟ في المقابل ستحصل على مرونة إلى حد أن هناك مبرمجون, يفضلون الـ Prototype Inheritance على الـ Classic Inheritance! الأولى لـ SmallTalk و هي لغة Dynamic و الثانية لـ Simula و هي المعتادة في اللغات الـ C-Like. الـ prototype inheritance هي مثال حقيقي على الـ Dynamic Typing, فأنت تأخذ كائن و لنقل أنه person, و تضيف عليه متغيراً اسمه student_number و تضيف دالتان get و set و أصبح لديك كائن جديد, و لا تنسى كل هذا تم عند تشغيل البرنامج!

    الـ Static Typing, على النقيض تعني أن المتغير يحمل معلومات أقل عن نفسه وقت التشغيل. قد يكون هذا التعريف غامضاً قليلاً, و لكن في أول الطيف هناك C و ++C حيث لا يحمل أي متغير عن نفسه أي شيء في الذاكرة على الإطلاق. و لا يوجد في اللغتان أصلاً طريقة لمعرفة وجود متغير معين من عدمه في الناتج النهائي عند الحديث عن الـ Optimized Code للمترجمات الشهيرة. هذا هو السبب الحقيقي لكون C و ++C لغات Efficient أكثر من اللغات الحية الأخرى لمهمات كصناعة المترجمات و الـ RDBMS حيث كميات البيانات خيالية. لربما كان من الغريب لمعظم دارسي C معرفة أن المصفوفات لاتحمل حتى حجمها معها في الذاكرة! هذا مصطلح أطلق عليه الأستاذ Stroustrup مايسمى بالـ Zero Overhead Principle :) في مترجمات اللغات الأكثر حداثة, كـ Java و #C و غيرهم يتم إضافة معلومات إلى الأنواع و لكنها بسيطة مقارنة بالمعلومات التي يتم إضافتها للمتغيرات في لغة Dynamic حتى النخاع كـ Python. ميزات الـ Static Typing واضحة, و عيوبه واضحة, و لكن بشكل عام الـ Static Typing تظهر محاسنها كلما كان حجم المشروع أكبر. فالمترجم بجانب المبرمج لاكتشاف أخطاء ربما تكون تافهة و لكنها غير ظاهرة كجمع نص مع عدد في حالة كون اللغة strongly typed مما يؤدي إلى خطأ وقت التشغيل بدلاً من إعلامنا بذلك مبكراً. الجميل في الأمر, أن هناك اتجاهاً نحو ما يسمى بالـ Reflection في اللغات الـ Statically Typed, و هذا بكل بساطة يعني أننا نستطيع الاستعلام عن بعض المعلومات المتوفرة عن المتغير و لكن لا يسمح لنا بتعديلها كما في اللغات الـ Dynamic الخالصة.

    ربما كنت قد أصبت بالإحباط عند وصولك إلى هذا السطر لأنني قلت: "فمثلاً تصور أننا نستخدم لغة تشبه ++C و لكنها Dynamic"
    فأنا قد أكدت في الموضوع السابق أن هذا الأمر لا علاقة له بلغة البرمجة نفسه و إنما بالـ Implementation المستخدم!
    في الحقيقة أن الجملة السابقة صحيحة و لكنها تحتاج إلى توضيح...

    عندما نستخدم مفسراً, فإن اللغة تصبح Dynamic لأن المفسر بطبعه يحمل جميع المعلومات عن الكود وقت التشغيل. بينما لو استخدمنا مترجماً, سيكون لدينا حالتان: الأولى هي أن يقوم المترجم باستخدام المعلومات التي استخلصها في الكود لترجمة البرنامج إلى منصة معينة دون إلحاق معلومات المتغير, و الحالة الثانية هي أن يقوم بإلحاقها! و في هذه الحالة تصبح اللغة Dynamic حتى باستخدام مترجم! لربما كان أوضح مثال هو عند بناء برنامج بلغة C مع تفعيل خواص الـ Debugging, في حينها ستصبح جميع المعلومات متوفرة في الناتج النهائي, و عند تفعيل خواص الـ Optimization فإن جميع المعلومات كأنها لم تكن أصلاً! هذا المثال ليس مثالاً محبذاً لدى الكثيرين عند الحديث عن الـ Dynamic Typing لأن اللغة في الحقيقة لاتوفر طرقاً للاستعلام عن تلك المعلومات من داخل اللغة نفسها وليس عن طريق برنامج خارجي على سبيل المثال. هذا يقودنا إلى الفقرة القادمة...

    دائماً عند وضع تعريف لشيء ما, فلابد أن تجد له ثغرات, و خصوصاً عند الحديث عن موضوع شائك كهذا! لابد أن تعي أن اللغات عندما تبنى فإن صاحبها على الأغلب يضع في الحسبان كونها ستصبح لغة مفسرة أو مترجمة, و بالتالي فإن هناك لغات ملائمة أكثر بطبعها للـ Dynamic Typing و هناك لغات ملائمة أكثر للـ Static Typing. ما نريد أن نصل إليه هو أن هناك لغات توفر مرونة في أنواعها, يصبح إنتاج مترجم لها معدوم الفائدة, لأن المترجم عليه أن يضع معلومات ضرورية لتلك المتغيرات للتأكد من صحة العمليات. فمثلاً, هناك تجارب لإنتاج مترجمات لـ Python, و لكن طبيعة اللغة نفسها و مرونتها لا تسمحان للمترجم بافتراض شيء حول المتغيرات, و يصبح من شبه المحتم أن يلحق معلومات المتغير وقت التشغيل. بينما في لغات أخرى, فإن هذا الأمر ليس صحيحاً بالضرورة, فمثلاً هناك مفسرات للغة C و لغة ++C, و هناك مترجمات, و نفس الأمر ينطبق على Haskell و Ocaml على سبيل المثال لا الحصر. إذاً, الأمر يتعلق بالافتراضات التي وضعها صاحب اللغة. و لكن أتمنى عند وصولنا لهذه النقطة, أن نكون قد وصنا إلى اتفاق أن استخدام مترجم لايعني أن اللغة Statically Typed. و لكن عند استخدام مفسر فإن الأمر شبه محسوم, و لكن تظهر لنا حالة شاذة و هي كون المفسر المستخدم يقوم بإنتاج كود Jitter يتم تنفيذه بعد ذلك. في هذه الحالة المعلومات قد تكون موجودة قبل الترجمة الفورية و ربما يقوم بالتخلص منها في الكود النهائي. يمكن أن تضع "سيناريوهات" عن لغات تقع في الوسط! لا نريد أن نعقد الموضوع أكثر من ذلك فأنا قد تهت! هناك لغات اختارت أن يكون هذا الأمر صريحاً في اللغة نفسها, بمعنى أن اللغة تفرض وجود الأمرين معاً في نفس الوقت, و لكن هذا توجه حديث نسبياً, و هو أحد شواذ التعريف الذي وضعناه. فمثلاً, في لغة #C النسخة الرابعة, استحدث صانعوها ما يسمى بالـ Dynamic Type رغم أن لغة #C لغة Statically Typed عند النظر إلى مترجماتها الشهيرة. فلو نظرت إلى المثال التالي:
    expand | plain text

    dynamic dynamic_int = 10;
    int static_int = 10;


    فمن المؤكد أن dynamic_int يحمل معلومات أكثر عن نفسه, و فائدة هذه الميزة لا تظهر إلا لو تخيلنا مثالاً أكثر تعقيداً كأن تقوم بتحميل مكتبة وقت التشغيل و من ثم تنادي دالة من أحد كائناتها دون حتى أن تعرف ماهو الكائن, و لا تخبر المترجم بماهية الكائن, و هل لديه تلك الدالة أصلاً؟ كل ذلك يتم وقت تشغيل البرنامج. بالطبع يمكنك القيام بذلك بدونها, و لكن المشكلة في الـ Static Typing أن المترجم لابد أن يعلم نوع الشيء الذي تريد استخدامه, و هذا الأمر قد يكون صعباً جداً في حالة كون الكود الذي تريد استخدامه بل ببساطة مكتوب بلغة أخرى كـ ++C مثلاً, و ربما تتضارب بعض الأنواع فيها مع #C. ببساطة هناك شخص قام بكتابة glue دون أن تراه كطبقة بين العالم الخارجي و بين متغيرك الـ Dynamic و سيحاول أن يبحث في المعلومات التي حصل من ذلك الكائن في العالم الخارجي لتطبيق ما أردته دون تعقيد أو حتى تفكير بماهية ذلك الكائن و هل من الممكن تمثيله بسهولة في اللغة أم لا. ضربت مثالاً آخر منذ مدة ليست بالقصيرة حول الـ Static Scope و الـ Dynamic Scope و كيف أن الـ Dynamic Scope يحتاج إلى Dynamic Types حتى يعمل. المثال تجده على الرابط التالي حتى نختصر الموضوع:
    What is lexical scope?

     

    قبل أن أترككم في رعاية الله, يجب أن أقول لكم بأنه لايوجد أحد ادعى بأنه يملك تعريفاً شاملاً للمصطلحات التي تكلمنا عنها. و ما ذكرته كان من المعلومات البسيطة التي أملكها, و من الخبرة الأبسط التي اكتسبتها. لذلك فإني أرحب بأي تصحيح أو إضافة مفيدة إلى الموضوع. بقي جزء قادم بإذن الله, و هو عبارة عن معاينة عن قرب لبعض اللغات لعرض المفاهيم التي تكلمنا عنها في الموضوع الأول و الثاني.

    تحياتي,

    كلمات مفتاحية  :

    تعليقات الزوار ()