فض النزاع في أنواع الأنواع! نظرية لغات البرمجة!

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

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

لغات البرمجة تعتبر من أكثر الفروع التي تم بحثها, في العقول نظرياً و في أرض الواقع عملياً. إن فرع لغات البرمجة, يعتبر إن لم يكن أكثر الفرو ع الحاسوبية أصالة في البحث العلمي, مثل أنظمة التشغيل و قواعد البيانات...إلخ. كثير من المبرمجين, لا يدركون أن لغة البرمجة, هي عبارة عن حاسوب افتراضي, و كلمة Abstraction, لم تأتي من فراغ, و إنما من مصطلح Abstract Machine أو Abstract Computer و الأول أصح, لأن لغات البرمجة تمثل حاسوباً مع أجزاء من العالم الخارجي كالذاكرة و العديد من المفاهيم الأخرى التي لا يمكن حصرها هنا, و ليست مجرد "حواسيب" فقط. و مبحث علماء البرمجة, ينقسم إلى قسمين رئيسيين. الأول, هو الـ Computations و الثاني هو الـ Types. فالـ Computation يمثل كيفية تمثيلنا للحسابات, من الدوال, و عملية ندائها, و المباحث المتقدمة في هذا المجال تذهب للـ Functional Languages و المفاهيم التي تقدمها. بينما علم "الأنواع" فهو يهتم بتمثيل الأنواع التي نقوم بحسابها, و هذا لب موضوعنا. فنحن نستطيع أن نمثل الأنواع التي تتوفر في حاسوبنا على أنها اوعية بدائية جداً, كـ Registers كما في الحواسيب الحقيقية, و في هذه الحالة سنفرض أسلوباً معيناً على الـ Computational Model, و هو سيفرض بالمقابل أسلوباً معيناً على الأنواع و على كيفية مزجها, و على مباحث أخرى كثيرة. و نصل في أخر الطيف, إلى الـ Type Models المتقدمة جداً, كـ OOP و Generic Programming و Abstract Data Types و الأخير في الحقيقة الأساس الذي تستند عليه البرمجة الكائنية و البرمجة الـ Generic. إن أي لغة برمجة, يجب أن تحدد الطريقة التي يتم التعامل فيها بين الأنواع التي تحتويها. بمعنى آخر إن مصمم أي لغة برمجة, يجب أن يحدد العلاقات بين أجزاء الـ Type Model الذي سيضيفه إلى لغته. أحد أبرز القرارات التي يجب أن يتخذها هي كالتالي:

أولاً: هل اللغة التي سوف أصنعها Type Safe؟
معنى أن تكون اللغة Type Safe, هو ضمان عدم حصول أي عملية على نوع غير النوع الذي صممت من أجله العملية, مما يؤدي إلى تطبيق عملية خاطئة على النوع. دعك من هذا التعريف المخيف, و انظر معي مالذي سيحصل في المثال التالي. عندما ظهرت ما يسمى K&R C أي لغة C الشهيرة في مهدها, كانت اللغة تسمح بانتهاك الـ Type Safety بشكل مباشر, و دون أدنى عناء من المبرمج, فمثلاً كان من الممكن أن نقوم بالتالي, بالمناسبة هذا الـ Syntax قد يبدو غريباً حتى على مبرمجي C الحاليين لأن هذا الـ Syntax مات منذ عشرات السنين.

add(first, second) int first, int second;
{
        int result = first+second;
        return result;
}

إن لم تفهم وظيفة الدالة فرجاءً لا تكمل المقال :)
المهم هو أن تفهم, أننا عرفنا دالة تقوم باستقبال متغيرين من نوع int و تعيد int رغم أن النوع المعاد غير مكتوب إلى أنه في C القديمة Implicit int. ثم قمنا بجمع العددين ووضعناهما في متغير من نوع int. و من ثم قمنا بإعادة الناتج على شكل int, تصور أننا قمنا باستدعاء الدالة بالطريقة التالية:
float first  = 5.0, second = 1.0;
float result = add(first, second);

أنا متأكد أنني ربما أجد رداً يقول, و لكن هذا بكل تأكيد خطا لغوي في C! بالتأكيد C الحديثة ليست C القديمة. و لكن في C القديمة كان سيحصل أمر مثير. الذي سيحصل هو أن المتغيرات من النوع float سوف يتم تمريرها إلى الدالة, مع افتراض أن النوع الممرر هو int و هذا يعني ان الدالة سوف تعمل على تلك المتغيرات و التي ربما تكون من نفس الحجم و تقوم بتطبيق عملية الجمع عليها, و لكن على اعتبار أن الـ Bits التي تمثل تلك المتغيرات عبارة عن int! ثم سنعيد ذلك الناتج و نضعه في متغير من نوع float. رغم أن الناتج الذي وضع في الحقيقة, هو ناتج تطبيق عملية الجمع الصحيح على تلك الـ bits الموجودة في تلك المتغيرات من النوع float و التي تختلف في طريقة تمثيلها في الذاكرة عن الأنواع الصحيحة!

هذا مثال مبسط للـ Type Safety. و لكن لا تقلق فأنا أضمن لك بأن أي لغة تستعملها تعتبر Type Safe بشكل كامل كما في Java, أو يمكن "اختراق" تلك الـ Type Safety كما في لغات أكثر تقدماً كما في الـ Unsafe Mode في #C. بالنسبة لأشهر اللغات التي يمكنك اختراق الـ Type Safety فيها هي C و ++C بدرجة أقل, و السبب هو أنها لغات للـ Systems Programming, و جميع ما تقوم به هاتان اللغتان هو تنظيم العملية و ليس منعها كما يعتقد البعض. بالطبع تحتاج إلى اختراق الـ Type Safety في أحيان نادرة في اللغات التي ننتج بها التطبيقات, بينما في أماكن كأنظمة التشغيل و معظم الـ System Programs فالأمر حاجة حقيقية و ليس Bad Style! الفرق بين C و ++C في هذا الموضوع, أن ++C أوجدت حلول للـ Systems Programming مع تقليل عمليات الاختراق دون خسارة الكفائة, و لكن للأسف تبقى تلك الأدوات حكراً على المحترفين في الغالب, و هي أمور الـ Generic Programming و الـ Compile-Time Metaprogramming و الأخيرة أثبتت تفوقها بشكل كبير على أسلوب الـ C في هذا المجال. نكون بهذا انتهينا من هذا الموضوع المعقد, و الذي لا تعتقد أنك لفهمك لهذا المثال قد فهمت الموضوع بشكل عام, لأنه بالفعل أحد أصعب المواضيع و يعتبر الـ tricky part في عملية تصميم اللغات.

نأتي إلى الأمور الأكثر إثارة الآن :cool:

إذا قام مصمم اللغة بجعل لغته Type Safe بشكل كبير حتى لو أوجد بعض طرق اختراقه في اللغة, يبقى عليه أمور كثيرة يجب أن يحددها. من أكثرها إثارة و صعوبة في نفس الوقت, هو جعل اللغة إما تميل للـ Weak Typing أو للـ Strong Typing! الـ Weak Typing يعني أن اللغة ستحاول إيجاد وسيلة لتطبيق عملية معينة على نوعين مختلفين و ذلك يرجع إلى مصمم اللغة بإيجاد الطريقة المناسبة, و الـ Strong Typing بكل بساطة يمنع العمليات بين الأنواع المختلفة إلا أن صرح المبرمج بذلك صراحة بواسطة أدوات توفرها اللغة! فمثلاً في Python التي تعتبر Strongly Typed:
>>> 1 + '10'

سوف تصدر خطأ! و لكن إذا أرد المبرمج يمكن أن يقوم بتحويل النص صراحة بالطريقة التالية:
>>> 1 + int('10')
11

لن يكون هناك خطأ! بينما لو ذهبت إلى لغة مثل Perl, فسترحب بذلك دون تفكير:
print(1 + '10');
11

مالذي حصل؟! مصمم اللغة قرر بأنه عند تلاقي نص و عدد فإن النص يتم محاولة تحويله لعدد! ماذا لو كان هناك نص لا يمكن تحويله كأن يكون مكوناً من الحروف مثلاً! في هذه الحالة هناك قرار آخر يقرره مصمم اللغة.

بالطبع العملية ليست "يا أبيض يا أسود"! فلغة Weakly Typed حتى النخاع تصبح معقدة أشد تعقيد, و حتى أعتى المبرمجين لابد أن يقع في أخطاء لا يمكن تصورها! و لغة Strongly Typed مئة بالمئة هي لغة لا تطاق و تصبح حتى أسهل العمليات فيها تحتاج تصريح من المبرمج. و لكن هناك إجماع أنه كلما كانت اللغة أقرب للـ Strong Typing كلما كان أفضل, و لمجاراة النقص في مرونة اللغة, فإنه يتم استحداث ما يسمى الـ Type Coercion. ببساطة لو أننا قمنا بجمع 1 + 1.0 فإن الطرف الأول من النوع int يتم تحويله إلى النوع double و إكمال العملية, لأننا نضمن أنه لن تكون هناك مفاجآت في هذا النوع من التحويل بين هذين النوعين, و هكذا بعض اللمسات البسيطة الأخرى.


بقي فقرة أخرى تتحدث عن الـ Static Typing Vs Dynamic Typing, و لن أكملها في هذا الموضوع و لكن في الموضوع التالي بإذن الله. السبب هو أن هناك خلطاً رهيباً بين هذين المفهومين و اللذان يعتبران من خصائص الـ Implementation أو المترجم, المفسر سمه ما شئت, و بين الـ Weak Typing Vs Strong Typing و اللذان يتعبران من خصائص اللغة نفسها. و سنضرب بعض الأمثلة بإذن الله, عن لغات Static/Strongly Typed و عن لغات أخرى تعتبر Static/Weakly Typed و لغات أخرى Dynamic/Strongly Typed و لغات أخرى Dynamic/Weakly Typed!!!!!

أتمنى أن يكون الموضوع ممتعاً,

تحياتي...