تقديمٌ ومنظومة
بسم الله الرحمن الرحيم
الحمدُ لله الذي علَّم بالقلم، علَّم الإنسانَ ما لم يعلم، فتح على عباده خزائنَ المعارف، وهداهم إلى صوغِ اللغاتِ والحروف، حتى أنشؤوا من العجائب آلاتٍ تفهمُ عن أهلها أمرَهم فتتمثَّل، وتنفِّذُ مرامَهم فتفعل.
وأشهدُ أن لا إلهَ إلا اللهُ وحدَه لا شريكَ له، علَّم آدمَ الأسماءَ كلَّها، وأشهدُ أنَّ سيدَنا ومولانا محمدًا عبدُه ورسولُه، أُوتيَ جوامعَ الكلم، صلَّى اللهُ عليه وعلى آلهِ وصحبِه وسلَّمَ تسليمًا كثيرًا.
أمَّا بعدُ: فقد وَقَعَتْ إليَّ منظومةٌ موجَزةُ المبنى، بديعةُ المعنى، نَظَمَتْها الأديبةُ الفاضلةُ آمنة بنتُ أحمدَ بنِ ميرغني - أدامَ اللهُ عليها نعمةَ البيان - سمَّتْها «نَظمَ تقسيمِ لغاتِ البرمجةِ وفقَ ترتيبِ الفَهمِ والمعالجة»، جَمَعَتْ فيها بين جَزالةِ العبارةِ العربيَّةِ ولُطفِ الاصطلاحِ الفنيِّ الحديثِ، فعرَّبَتْ بعضَ أسماءِ اللغاتِ بما يُوافِقُ معناها في لسانِها الأصليّ، وأبقَتْ بعضَها على لفظِه. فأحبَبْتُ أن أُقيِّدَ عليها تعليقةً مختصرةً تَكشفُ عن معانيها، وتُفصِحُ عن مراميها، وتُذلِّلُ على الطالبِ سُبُلَ فَهمِها، مع إشاراتٍ إلى ما تخلَّلَها من النُّكَتِ والفوائد.
المنظومةُ من بحرِ الرَّجَزِ الذي يُسمَّى «حمارَ الشعراء» لسهولةِ الرُّكوبِ عليه؛ وقد جرى اصطلاحُ أهلِ العلمِ على اختيارِه لمتونِ الفنونِ تقريبًا للحفظِ على الطلَّاب. ورتَّبَتْها الناظمةُ على مقدمةٍ يسيرةٍ، وثلاثةِ أبوابٍ: بابِ اللغاتِ المترجَمةِ، ثم المفسَّرةِ، ثم الهجينةِ، وخَتَمَتْ بخاتمةٍ ضمَّنَتْها البيتَ الأخير.
المنظومة كاملةً
شرحُ الديباجة
قولُها - رحمَها اللهُ ونفعَ بها -:
أقول: افتتحَتْ منظومَتَها بالتبرُّكِ باسمِ اللهِ تعالى، جَرْيًا على سُنَّةِ أهلِ العلمِ في افتتاحِ مصنَّفاتِهم، اقتداءً بفاتحةِ الكتابِ العزيز. واختصَّتْ من أسماءِ اللهِ الحسنى اسمَين: «المؤمنَ» و«المُهيمنَ»، وهما الواردانِ في قولِه تعالى: ﴿الْمُؤْمِنُ الْمُهَيْمِنُ﴾ ١. فالمؤمنُ هو الذي يُؤمِنُ عبادَه من عذابِه ويُصدِّقُ رُسُلَه بما أظهرَ لهم من الآيات، والمُهيمنُ هو الرقيبُ الحافظُ لكلِّ شيءٍ، الشاهدُ على خلقِه بأعمالِهم.
وفي تخصيصِ هذين الاسمَين مع هذا الفنِّ بالذاتِ نكتةٌ حسنة؛ فصنعةُ البرمجةِ تَحتاجُ إلى «أمانةٍ» في بناءِ ما ينفعُ الخلقَ لا ما يَضُرُّهم، وإلى «هَيمنةٍ» على ما يُصنَع فلا يَسرَحُ ولا يَمرَح. وفيه إشارةٌ حسنةٌ إلى مجانبةِ ما سُمِّيَ بـ«البرمجةِ بالإحساس» (vibe coding)، حيث يكتبُ المرءُ شِفرةً لا يَفهمُها ولا يُهيمنُ عليها. واللهُ أعلم.
وقولُها: «في نَظمِ تقسيمِ لغاتِ البرمجةْ» بيانٌ لموضوعِ المنظومةِ، وهو تقسيمُ اللغاتِ البرمجيةِ باعتبارٍ مخصوص. وقولُها: «وفقَ ترتيبِ الفَهمِ والمعالجةْ» بيانٌ للمسلكِ الذي سَلَكَتْه في التقسيم؛ إذ ليس التقسيمُ عندها باعتبارِ القِدَمِ والحداثة، ولا باعتبارِ المجالاتِ التي تُستخدَمُ فيها، وإنما باعتبارِ كيفيَّةِ فَهمِ الحاسوبِ للشِّفرةِ المكتوبةِ ومعالجتِه إيَّاها؛ فـ«الفَهمُ» هو ما يُعبَّرُ عنه بـ(parsing / compilation)، و«المعالجةُ» هي التنفيذُ (execution).
بابُ اللغاتِ المترجَمة
قولُها:
شَرَعَتْ في ذكرِ الأقسامِ فبدأتْ بالقسمِ الأولِ، وهو «اللغاتُ المترجَمة» (compiled languages). والترجمةُ في الأصلِ اللغويِّ نَقْلُ الكلامِ من لسانٍ إلى لسان، ثم استُعمِلَتْ في الاصطلاحِ البرمجيِّ في نَقْلِ الشِّفرةِ المصدريَّةِ (source code) المفهومةِ للإنسانِ إلى لُغةِ الآلةِ (machine code) المفهومةِ للحاسوب، بوساطةِ برنامجٍ يُسمَّى «المترجِمَ» (compiler).
وبيانُ عملِه ما قالَتْه في البيتِ الثاني: أنه يَمُرُّ على جميعِ السطورِ فيفحصُها سطرًا سطرًا، فإن وَجَدَ في أحدِها خطأً وقفَ ولم يُكمِل، ولم يَنتُج عنه برنامجٌ يُشغَّل. أما إن صحَّ الكلُّ فإنَّه يُنتِجُ ملفًّا تنفيذيًّا (executable) يُشغَّلُ دَفعةً واحدةً، لا سطرًا بعدَ سطر.
الأولى: أنَّ الخطأَ في اللغاتِ المترجَمةِ يُكتَشَفُ «قبلَ» التشغيلِ لا أثناءَه، وهذه من مزاياها الكبرى؛ إذ لا يَقِفُ البرنامجُ في وجهِ المستخدِمِ وهو يعمل، بل إن كانت فيه عِلَّةٌ ظَهَرَت في طَورِ التطويرِ قبلَ أن تَصِلَ إلى يدِ المستخدِم.
الثانية: أنَّ اللغاتِ المترجَمةَ أسرعُ في التنفيذِ غالبًا، إذ لا يُحتاجُ حين التشغيلِ إلى فَهمٍ جديد؛ فقد فُرِغَ منه قبلَه. وكذلك الشِّفرةُ المفهومةُ من صاحبِها أسهلُ في الاعتناءِ، ولا تَحتاجُ إلى الاستذكاءِ في فهمِها كلَّ مرَّة.
ثم مَثَّلَتْ لهذا القسمِ بخمسٍ من اللغاتِ الشهيرة:
- سي: وهي لغةُ C المعروفةُ، وَضَعَها دِنِيس رِيتشي في مختبراتِ «بِل» سنةَ اثنتَين وسبعينَ وتسعِ مئةٍ وألفٍ من الميلاد، وعليها قامَتْ أنظمةُ التشغيلِ كـ«يونكس» و«لينكس».
- أختُها: أي ++C التي طوَّرها بْيارِني سترُوسترُوب على أساسِ C، فكانَتْ بمنزلةِ الأختِ الشقيقةِ لها. ووصفُ علامةِ ++ بديعٌ كما يظهرُ للطالب، ويظهرُ لي أنَّ لفظَ «بنت» يكونُ حسنًا أيضًا، لأنَّ الابنَ - كما هو معلوم - امتدادُ أبيه.
- هَسكل: وهي Haskell، لغةٌ وظيفيةٌ خالصة (purely functional)، سُمِّيَتْ على اسمِ المنطقيِّ هَسكل كاري.
- انطَلِقْ: تعريبٌ للغةِ Go، إذ «go» في الإنجليزيَّةِ بمعنى اذهَبْ وانطلِقْ، وهي من وَضعِ مهندسي شركةِ «جوجل».
- الصَّدا: تعريبٌ للغةِ Rust، إذ «rust» في الإنجليزيَّةِ هو الصَّدَأُ الذي يعتري الحديد.
بابُ اللغاتِ المفسَّرة
قولُها:
القسمُ الثاني هو «اللغاتُ المفسَّرة». وتسميتُها بـ«المفسَّرة» على معنى أنَّ «المفسِّرَ» (interpreter) يأخذُ السطرَ فيَفهمُه ثم يُنفِّذُه على الفور، ثم ينتقلُ إلى ما بعدَه؛ فهو يَجمَعُ بين الفَهمِ والتنفيذِ معًا، بخلافِ المترجِمِ الذي يَفصِلُ بينهما. وقولُها «متعاقبات» حالٌ تدلُّ على أنَّ الفَهمَ والتنفيذَ يأتيانِ متلاحقَين سطرًا إثْرَ سطر.
ظاهرُ كلامِ الناظمةِ أنَّ المترجَمَ إمَّا أن يعملَ كاملًا أو لا يعملَ أصلًا، وأنَّ المفسَّرَ يَقِفُ حيثُ يجدُ الخطأَ دونَ أن يَضُرَّ ما مضى؛ وهذا الفارقُ إنَّما يَصِحُّ في نوعٍ مخصوصٍ من الأخطاءِ، لا في كلِّ خطأٍ مطلقًا. وبيانُه أنَّ الأخطاءَ على ضَربَين:
الضربُ الأوَّل: أخطاءُ الفَهمِ السابقةُ على التشغيل (compile-time errors)، كخطأٍ في تركيبِ الجملةِ (syntax error)، أو في تطابُقِ الأنواعِ (type mismatch)، أو في اسمِ دالَّةٍ لا وجودَ لها.
الضربُ الثاني: أخطاءُ التشغيلِ الطارئةُ وقتَ العمل (run-time errors)، كقِسمةٍ على صفر، أو تجاوزٍ لحدودِ مَصفوفة، أو محاولةِ فتحِ ملفٍّ غيرِ موجود، أو انقطاعٍ في الشبكة، أو نَفادِ الذاكرة، أو إشارةٍ إلى عُنوانٍ خاوٍ (null pointer).
فالفارقُ الذي بيَّنَتْه الناظمةُ إنَّما يَصِحُّ في الضربِ الأولِ؛ فالمترجِمُ يَكشِفُه دَفعةً واحدةً قبلَ التشغيل، والمفسِّرُ لا يَكشِفُه إلَّا حين يَبلُغَ السطرَ المعتلَّ. أمَّا الضربُ الثاني فيَقَعُ في القسمَين سواءً بسواء؛ إذ لو كان برنامجُك المترجَمُ بـ«سي» قد فَتَحَ ملفًّا وكتبَ فيه في السطرِ الأوَّل، ثم قَسَمَ على صفرٍ في السطرِ العاشر، فالملفُّ قد كُتِبَ ولا يَرتدُّ، وحصلَ الوقوفُ في موضعِ الخطأِ تمامًا كما يَحصُلُ في المفسَّر. وقد يُفضي مثلُ هذا في بعضِ اللغاتِ إلى ما يُسمَّى (segmentation fault) وهو أسوأُ من خطأِ المفسَّرِ في كثيرٍ من الأحوال.
فاحفَظْ هذا التحقيقَ فإنَّه موضعُ إشكالٍ على كثيرٍ من المبتدئين، يظنُّون أنَّ المترجَمَ إذا نجحَ في ترجمتِه سَلِمَ البرنامجُ من كلِّ خطأٍ، وليس الأمرُ كذلك.
فصلٌ في تقسيمِ اللغاتِ باعتبارِ الأنواع
ومِن المناسبِ هنا ذِكرُ تقسيمٍ آخرَ مُستقلٍّ عن تقسيمِ الناظمةِ، وهو تقسيمُها باعتبارِ «الأنواع» (types). واعلَمْ أنَّ كلَّ قيمةٍ في البرمجةِ لها نوعٌ تَنتَمي إليه: إمَّا عددٌ صحيحٌ (integer)، أو عددٌ كسريٌّ (float)، أو نصٌّ (string)، أو قيمةٌ منطقيَّةٌ (boolean)، أو غيرُ ذلك من الأنواعِ المركَّبة. واللغاتُ في معامَلَتِها للأنواعِ على قسمَين:
| محقِّقةٌ للأنواعِ وقتَ الترجمة (static) | محقِّقةٌ للأنواعِ وقتَ التشغيل (dynamic) |
|---|---|
| يُصرِّحُ المبرمِجُ بنوعِ كلِّ متغيِّر، ويَكشِفُ المترجِمُ خطأَ النَّوعِ قبلَ التشغيل. | لا يُصرَّحُ فيها بالنوع، ولا يظهرُ خطأُ النوعِ إلَّا حين يبلغَ المفسِّرُ السطرَ المعتلَّ. |
| سي، وأختُها، وهَسكل، وانطَلِق، والصَّدا، وجافا، وسي الموسومة، وكوتلِن. | الياقوتُ، والثعبانُ، والجافاسكريبت. |
في تمثيلِ الناظمةِ للمفسَّرةِ بـ«الثعبان» (Python) مسامحةٌ لطيفةٌ؛ فالثعبانُ ليس مفسَّرًا محضًا على الحقيقة، بل يَسلُكُ مسلكًا قريبًا من مسلكِ الهجينةِ في بعضِ وجوهه. وذلك أنَّ مفسِّرَ الثعبانِ الرسميَّ (CPython) إذا مَرَّ على ملفِّ الشِّفرةِ لا يُفسِّرُ السطورَ رأسًا من نصِّها كما يَتوهَّمُ كثيرٌ من الناس، بل يُترجِمُها أولًا إلى صيغةٍ وسيطةٍ تُسمَّى «البايتكود» (bytecode)، ثم يُنفِّذُ هذا البايتكود في آلةٍ افتراضيةٍ داخلَه، شأنَ الهجينةِ. والفارقُ بينه وبين «جافا» أنَّ هذه الترجمةَ في الثعبانِ تَقَعُ عند كلِّ تشغيلٍ تلقائيًّا، لا في خطوةٍ سابقةٍ يأمُرُ بها المبرمِج.
ثم إنَّ الثعبانَ يَحفَظُ هذا البايتكودَ في مُجلَّدٍ يُسمَّى «__pycache__» (أي المَخبَأ)، تَجِدُه ينشأُ تلقائيًّا بجانبِ ملفَّاتِك بصيغةِ (.pyc). وفائدةُ هذا الحَفظِ أنَّه إذا أُعيدَ تشغيلُ البرنامجِ ولم يُعَدَّلِ الملفُّ الأصليُّ، أخذَ المفسِّرُ البايتكودَ الجاهزَ مباشرةً دونَ إعادةِ الترجمة، فوُفِّرَ من الزمنِ ما وُفِّر. فإذا رأيتَ هذا المجلَّدَ ينشأُ بجانبِ ملفَّاتِك فاعلَمْ أنَّه أثرُ هذا الصنيعِ لا عِلَّةٌ يجبُ إزالتُها، ويَصلُحُ استثناؤُه من أنظمةِ الإصدار (git).
وهذا المسلكُ نفسُه تَسلُكُه «الياقوتُ» في آلتها الافتراضيةِ المسمَّاةِ YARV منذ الإصدارِ التاسعِ من المئةِ الثالثةِ بعد الألفَين، و«الجافاسكريبت» في محرِّكاتِها الحديثةِ كمحرِّكِ V8 المستخدَمِ في متصفِّحِ كروم. بل تَتَجاوَزُ الجافاسكريبتُ ذلك إلى ما يُسمَّى «الترجمةَ في الطَّيَران» (JIT - Just-In-Time)، وهي ترجمةُ البايتكودِ الحارِّ المتكرِّرِ تنفيذُه كثيرًا إلى لغةِ الآلةِ مباشرةً وقتَ التشغيلِ طلبًا للسُّرعةِ القُصوى.
ثم مثَّلَتْ لهذا القسمِ بثلاثٍ:
- الياقوت: تعريبٌ للغةِ Ruby، وهي في الأصلِ حجرٌ كريمٌ أحمرُ، وَضَعَها يوكيهيرو ماتسوموتو.
- الثعبان: تعريبٌ للغةِ Python، وَضَعَها خيدو فان روسُّم وسمَّاها على اسمِ فرقةٍ كوميديَّةٍ لا على اسمِ الحيَّةِ، لكنَّ الاسمَ صار عَلَمًا على الحيَّةِ في الأذهان، فأخذَتْ هي بذلك.
- الجافاسكريبت: وأبقَتْها على لفظِها لشهرتِها به، وهي لغةُ المتصفِّحاتِ، وَضَعَها برَندان آيك.
وقولُها: «فاستَمِعْ بياني» تَشويقٌ لما بعدُ، من بابِ حُسنِ التخلُّصِ من بابٍ إلى باب، وهو من محاسنِ النَّظم.
بابُ اللغاتِ الهجينة
قولُها:
هذا هو القسمُ الثالثُ، وهو الذي جَمَعَ بين خَصيصَتَي القسمَين السابقَين، فسُمِّيَ «الهجينَ» (hybrid) لأنه مُركَّبٌ من أصلَين. والهجينُ في اللغةِ العربيةِ هو المولَّدُ من أبوَينِ مختلفَي الأصل. وقولُها: «قسمٌ افترَقْ» أي انفصَلَ عن القسمَين السابقَين وتَميَّزَ بخاصِّيَّةٍ لا تجتمعُ فيهما، وقولُها: «وما له من موضعٍ فيما سبَقْ» تأكيدٌ لهذا التمايز.
ثم بيَّنَتْ آليَّتَه: أنَّ الشِّفرةَ تُترجَمُ أولًا إلى صِيغةٍ وسيطةٍ - لا إلى لغةِ الآلةِ المحضةِ - تُسمَّى «بايتكود» (bytecode)، ثم يأتي دورُ الآلةِ الافتراضيةِ (virtual machine) فتَقرأ هذا البايتكود سطرًا سطرًا، كما يَصنعُ المفسِّرُ في المفسَّرةِ تمامًا.
فصلٌ في حقيقةِ الآلةِ الافتراضيةِ وأقسامِها
اعلَمْ - أرشَدَكَ اللهُ - أنَّ لفظَ «الآلةِ الافتراضيَّةِ» (virtual machine) يُطلَقُ في الصناعةِ على معنيَيْنِ متغايرَيْنِ، وقد يَلتبِسُ أحدُهما بالآخرِ على غيرِ المتخصِّص:
- آلاتُ النظامِ (system VMs): وهي برامجُ تُحاكي جهازًا كاملًا بأعتُدِه من معالِجٍ وذاكرةٍ وأقراصٍ، حتى يُمكِنَ تنصيبُ نظامِ تشغيلٍ بتمامِه داخلَها، ومنها VMware وVirtualBox وKVM وHyper-V. وليس مرادُ الناظمةِ هذا القسمَ.
- آلاتُ العمَليَّةِ (process VMs): وهي التي يَدورُ عليها كلامُ الناظمةِ، ووَظيفتُها تشغيلُ «برنامجٍ واحدٍ» مكتوبٍ بصيغةِ البايتكودِ، دونَ محاكاةِ جهازٍ كامل، ومثالُها JVM وCLR وBEAM.
وآلاتُ العمَليَّةِ تَنقَسِمُ باعتبارِ بِنيَتِها الداخليَّةِ إلى ضربَين:
(أ) الآلاتُ المَكدَسيَّةُ (stack-based): تَستعمِلُ «مَكدَسًا» (stack) لإجراءِ العمليَّاتِ، فتَدفَعُ إليه المُعامَلاتِ ثم تَسحبُها لإجراءِ الحساب. فإذا أردتَ جمعَ عددَين دَفَعتَهما إلى المكدَسِ ثم أمَرتَ بالجمعِ، فيَسحَبُهما ويَدفَعُ ناتجَهما. ومنها JVM في أشهرِ تنفيذاتِها، وCPython.
(ب) الآلاتُ السِّجِلِّيَّةُ (register-based): تُحاكي المعالِجاتِ الحقيقيَّةَ في استعمالِ «السجلَّاتِ» (registers) بدلَ المكدَس، فتكونُ أوامرُها أقلَّ عددًا وأقربَ إلى الآلةِ الفِعليَّة. ومنها Dalvik قديمًا، وART حديثًا في الأندرويد، وآلةُ لغةِ Lua.
فصلٌ في أشهرِ هذه الآلاتِ وأعظمِها شأنًا
- آلةُ الجافا (JVM): أُمُّ البابِ وأصلُه، وَضَعَتْها شركةُ «صَن» سنةَ خمسٍ وتسعينَ من الميلاد، ولها تنفيذاتٌ متعدِّدة: HotSpot (تنفيذُ أوراكل المرجعيُّ، يرصُدُ «النقاطَ الحارَّةَ» فيُترجِمُها بتقنيَّةِ JIT)، وOpenJ9 (من IBM، يَمتازُ بخِفَّةِ الذاكرة)، وGraalVM التي تَجمَعُ بين JIT والترجمةِ المُسْبَقةِ التامَّةِ (AOT)، فتستطيعُ إخراجَ برنامجِك ملفًّا تنفيذيًّا مستقلًّا عن JVM بالكُليَّة.
- زمنُ التشغيلِ العامُّ (CLR): آلةُ مايكروسوفت الافتراضيَّةُ لبيئةِ NET.، أُطلِقَتْ سنةَ ألفَينِ من الميلاد، مُصمَّمةٌ لِتَستَقبِلَ بايتكودَ «أكثرَ من لغةٍ» في وقتٍ واحد (C# وVB.NET وF#)، وتَستَعمِلُ صيغةً وسيطةً تُسمَّى CIL.
- آلةُ بِيم (BEAM): تَعمَلُ عليها لغتا Erlang وElixir، وامتازَتْ بِدَعمٍ فائقٍ لـ«العمَليَّاتِ المتوازيَةِ»؛ إذ تَستَطيعُ حَملَ ملايينِ العمَليَّاتِ الخفيفةِ في الوقتِ الواحد، وعليها قامَتْ أنظمةُ الاتصالاتِ الضخمة.
- دالفيك ثم ART: آلتانِ وَضَعَتْهما جوجل لتشغيلِ تطبيقاتِ الأندرويد، كانت دالفيك سجِلِّيَّةً، ثم جاءَتْ ART فَغَلَّبَتِ الترجمةَ المسبقةَ توفيرًا للطاقة.
- آلةُ ويبأسمبلي (WebAssembly): آلةٌ مَكدَسيَّةٌ تَعمَلُ في المتصفِّحات، تستقبلُ بايتكودًا مُنتَجًا من Rust أو ++C أو Go، فيَعمَلُ بسرعةٍ قريبةٍ من سرعةِ الآلةِ الأصليَّة.
في كلامِ الناظمةِ موضعُ نَظَرٍ يَحسُنُ التنبيهُ عليه، وهو زَعمُها أنَّ هذا القسمَ «افترَقَ عمَّا سَبَق، وما له من موضعٍ فيما سبَق». والتحقيقُ أنَّ الصناعةَ ليست كذلك على الإطلاق، من وجوه:
الأول: أنَّ الثعبانَ والياقوتَ وسائرَ ما جُعِلَ من المفسَّرةِ تَستَعمِلُ في حقيقتِها آلاتٍ افتراضيَّةً وبايتكودًا، فهي هجينةٌ بالحقيقة.
الثاني: أنَّ كثيرًا مما عُدَّ من الهجينةِ - كـJava وKotlin وC# - يُمكِنُ ترجمتُه اليومَ ترجمةً مُسبَقةً تامَّةً (AOT) فيَصيرُ مترجَمًا صِرفًا، كما في GraalVM Native Image وKotlin/Native وNET. Native AOT؛ وفي هذا نقضٌ لطيفٌ لحدِّ الناظمةِ فتأمَّل.
الثالث: أنَّ بعضَ ما عُدَّ من المترجَمةِ - كـHaskell - له تنفيذٌ هجينٌ أيضًا (GHCi) يُفسِّرُه سطرًا سطرًا.
فَظَهَرَ أنَّ التقسيمَ الثلاثيَّ هو باعتبارِ «الحالةِ الغالبةِ والتنفيذِ الأشهرِ» لا باعتبارِ حقيقةِ اللغةِ نفسِها. ولا تَثريبَ على الناظمةِ في ذلك، إذ الأنظامُ التعليميَّةُ تُبنى على الغالبِ لا على النوادر؛ وإنما هذا تنبيهٌ للمتقدِّمِ في الفنِّ حتى لا يَعتَقِدَ التقسيمَ حقيقةً ثابتةً على الإطلاق.
يَجمعُ هذا المسلكُ بين مَزِيَّاتٍ جليلة: قابليَّةُ النقلِ (portability) بين الأجهزةِ المختلفةِ، فالبايتكودُ واحدٌ والأجهزةُ متعدِّدة، وهي التي عبَّرَتْ عنها جافا بشعارِها «اكتُبْ مرَّةً تَعمَلْ في كلِّ مكان»؛ وكَشْفُ كثيرٍ من الأخطاءِ قبلَ التشغيلِ كما في المترجَمة؛ وإدارةُ الذاكرةِ التلقائيَّةُ (garbage collection) فلا يَحتاجُ المبرمجُ إلى تحريرِ الذاكرةِ بيدِه كما في C؛ والأمانُ (sandboxing) إذ تُقيِّدُ الآلةُ ما يَفعلُه البرنامجُ فلا يَنفُذُ إلى مواردِ الجهازِ إلا بإذن.
ثم مثَّلَتْ له بثلاث:
- جافا (Java): وَضَعَها جيمس جوسلِنغ في شركةِ «صَن مايكروسيستمز»، وهي أشهرُ لغاتِ هذا القسمِ، تَعمَلُ على JVM، واستحوذَتْ عليها شركةُ أوراكل سنةَ عشرٍ من الألفيَّةِ الثانية.
- سي الموسومة (#C): سُمِّيت بـ«الموسومةِ» لأنَّ اسمَها الأصليَّ C مع علامةِ (#) التي تُنطَقُ «شارب»، والوَسمُ هو العلامة، وَضَعَتْها مايكروسوفت بقيادةِ أندرس هيلسبرغ، تعملُ على CLR.
- كوتلن (Kotlin): وَضَعَتْها شركةُ «JetBrains»، وتَعمَلُ على JVM أصالةً، وقد صارَتْ من أشهرِ لغاتِ تطبيقاتِ الأندرويدِ بإعلانِ جوجل تبنِّيَها سنةَ سبعَ عشرةَ من الألفيَّة، ولها تنفيذاتٌ أُخَرُ تَنقلُها إلى JavaScript وإلى شِفرةِ الآلةِ المحضَّةِ (Kotlin/Native).
الخاتمةُ وخلاصة
قولُها:
وقولُها: «وهذا خَتمُ ذي المنظومةْ» إعلانٌ منها بانتهاءِ النَّظمِ على عادةِ الناظِمين، وهو من قبيلِ «حُسنِ الخِتام»، كما اعتَنَوا بـ«حُسنِ الابتداء».
فالتقسيمُ الثلاثيُّ الذي اختارَتْه الناظمةُ - مترجَمةٌ، ومفسَّرةٌ، وهجينةٌ - تقسيمٌ تعليميٌّ حَسَنٌ يُقرِّبُ المعنى على الطالب، والحقُّ أنَّ كلَّ اللغاتِ الحديثةِ تَسلُكُ مسالكَ مركَّبةً بين الترجمةِ والتفسيرِ والتحسينِ في الطَّيَران؛ وإنَّما تُنسَبُ كلُّ لغةٍ إلى القسمِ الذي يَغلِبُ على ظاهرِ تعامُلِ المبرمِجِ معها. واللهُ أعلمُ بالصواب.
هذا آخرُ ما تَيَسَّرَ من شرحِ هذه المنظومةِ اللطيفةِ، جاهدتُ فيه أن أكونَ مع الناظمةِ لا عليها، ومع المرادِ لا بعيدًا عنه؛ فما كان فيه من صوابٍ فمن اللهِ وحدَه، وما كان من خطأٍ أو قصورٍ فمن نفسي والشيطانِ، واللهُ ورسولُه منه بريئان.
معجم المصطلحات
| بالعربية | بالإنجليزية | المعنى باختصار |
|---|---|---|
| اللغات المترجَمة | Compiled Languages | لغات تُترجَم شفرتها كاملةً إلى لغة الآلة قبل التشغيل، فإن صحّ الكل أُنتج ملف تنفيذي. مثل C وRust. |
| اللغات المفسَّرة | Interpreted Languages | لغات يأخذ مفسِّرها السطر فيفهمه ثم ينفذه على الفور سطرًا إثر سطر. مثل Ruby وPython. |
| اللغات الهجينة | Hybrid Languages | لغات تُترجَم أولًا إلى بايتكود ثم تنفّذه آلة افتراضية سطرًا سطرًا. مثل Java وC#. |
| المترجِم | Compiler | برنامج ينقل الشفرة المصدرية إلى لغة الآلة دفعةً واحدة، ويكشف أخطاء الفهم قبل التشغيل. |
| المفسِّر | Interpreter | برنامج يجمع الفهم والتنفيذ معًا، يأخذ السطر فيفهمه فينفذه ثم ينتقل لما بعده. |
| الشفرة المصدرية | Source Code | النص الذي يكتبه المبرمج بلغة مفهومة للإنسان قبل ترجمته أو تفسيره. |
| لغة الآلة | Machine Code | التعليمات الثنائية التي يفهمها المعالج مباشرة وينفذها. |
| الملف التنفيذي | Executable | ناتج الترجمة في اللغات المترجَمة، يُشغَّل دفعةً واحدة لا سطرًا بعد سطر. |
| البايتكود | Bytecode | صيغة وسيطة بين الشفرة المصدرية ولغة الآلة، تقرأها الآلة الافتراضية. تستعملها Java وPython. |
| الآلة الافتراضية | Virtual Machine | برنامج ينفّذ البايتكود. منها آلات النظام (تحاكي جهازًا كاملًا) وآلات العملية (تشغّل برنامجًا واحدًا) كـJVM. |
| الترجمة في الطيران | JIT (Just-In-Time) | ترجمة البايتكود الحارّ المتكرّر تنفيذه إلى لغة الآلة وقت التشغيل طلبًا للسرعة. |
| الترجمة المسبقة | AOT (Ahead-Of-Time) | ترجمة تامة قبل التشغيل تُحوّل اللغة الهجينة إلى مترجَمة صرفة، كما في GraalVM Native Image. |
| الأنواع الساكنة | Static Typing | التصريح بنوع كل متغير وكشف خطأ النوع قبل التشغيل. كما في C وHaskell وJava. |
| الأنواع المتحركة | Dynamic Typing | لا يُصرَّح فيها بالنوع، ولا يظهر خطأ النوع إلا وقت بلوغ السطر المعتل. كما في Python. |
| خطأ وقت الترجمة | Compile-time Error | خطأ الفهم السابق على التشغيل، كخطأ التركيب (syntax) أو تطابق الأنواع. |
| خطأ وقت التشغيل | Run-time Error | خطأ طارئ أثناء العمل، كقسمة على صفر أو تجاوز حدود مصفوفة أو إشارة خاوية. |
| قوقعة الحوار | REPL | بيئة تفاعلية تتيح تجربة التعبيرات سطرًا سطرًا دون ترجمة الكل، من ثمرات التفسير. |
| كنس المهملات | Garbage Collection | إدارة الذاكرة تلقائيًا فلا يحتاج المبرمج إلى تحريرها بيده، من مزايا الآلة الافتراضية. |
| المكدَس | Stack-based VM | آلة افتراضية تجري العمليات عبر مكدس تدفع إليه المعاملات وتسحبها. كـJVM وCPython. |
| السِّجِلّات | Register-based VM | آلة افتراضية تحاكي المعالجات في استعمال السجلات، أوامرها أقل وأقرب للآلة. كـDalvik وLua. |
| عطل التجزئة | Segmentation Fault | عطل وقت تشغيل في بعض اللغات عند الوصول لذاكرة غير مسموحة، من أسوأ أخطاء التشغيل. |