الإيجاز قوة
مايو 2002
| "إن كمية المعنى المضغوطة في مساحة صغيرة بواسطة الرموز الجبرية هي ظرف آخر يسهل الاستدلالات التي اعتدنا على القيام بها بمساعدتها."
- تشارلز بابيج، نقلاً عن محاضرة آيفيرسون الحائزة على جائزة تورينغ
في المناقشة حول القضايا التي أثارها انتقام المهووسين على قائمة بريد LL1، كتب بول بريسكود شيئًا ظل عالقًا في ذهني.
هدف بايثون هو الانتظام وسهولة القراءة، وليس الإيجاز.
على ما يبدو، هذا شيء يدعو للذنب حقًا للمطالبة بلغة برمجة. بقدر ما أستطيع أن أقول، الإيجاز = القوة. إذا كان الأمر كذلك، فبالتعويض نحصل على
هدف بايثون هو الانتظام وسهولة القراءة، وليس القوة.
وهذا لا يبدو مقايضة (إذا كانت مقايضة بالفعل) ترغب في القيام بها. إنه ليس بعيدًا عن القول بأن هدف بايثون ليس أن يكون فعالاً كلغة برمجة.
هل الإيجاز = القوة؟ هذا يبدو لي سؤالاً مهمًا، ربما أهم سؤال لأي شخص مهتم بتصميم اللغات، وسؤال سيكون من المفيد مواجهته مباشرة. لست متأكدًا بعد أن الإجابة هي نعم بسيطة، لكنها تبدو فرضية جيدة للبدء بها.
الفرضية
فرضيتي هي أن الإيجاز هو القوة، أو قريب بما يكفي بحيث يمكنك معاملتهما على أنهما متطابقان باستثناء الأمثلة المرضية.
يبدو لي أن الإيجاز هو ما تُستخدم لغات البرمجة من أجله. ستكون أجهزة الكمبيوتر سعيدة بنفس القدر بإخبارها بما يجب فعله مباشرة بلغة الآلة. أعتقد أن السبب الرئيسي الذي يدفعنا إلى تحمل عناء تطوير لغات عالية المستوى هو الحصول على نفوذ، حتى نتمكن من القول (والأهم من ذلك، التفكير) في 10 أسطر من لغة عالية المستوى ما يتطلب 1000 سطر من لغة الآلة. بعبارة أخرى، النقطة الرئيسية للغات عالية المستوى هي جعل الكود المصدري أصغر.
إذا كان الكود المصدري الأصغر هو الغرض من اللغات عالية المستوى، وقوة شيء ما هي مدى جودة تحقيقه لغرضه، فإن مقياس قوة لغة البرمجة هو مدى صغر البرامج التي تجعلها.
وعلى العكس من ذلك، فإن اللغة التي لا تجعل برامجك صغيرة تقوم بعمل سيء فيما يفترض أن تفعله لغات البرمجة، مثل سكين لا تقطع جيدًا، أو طباعة غير مقروءة.
المقاييس
صغير بأي معنى؟ المقياس الأكثر شيوعًا لحجم الكود هو عدد الأسطر. لكنني أعتقد أن هذا المقياس هو الأكثر شيوعًا لأنه الأسهل في القياس. لا أعتقد أن أي شخص يعتقد حقًا أنه الاختبار الحقيقي لطول البرنامج. اللغات المختلفة لها اصطلاحات مختلفة حول مقدار ما يجب وضعه في سطر؛ في C، تحتوي العديد من الأسطر على لا شيء سوى فاصل أو اثنين.
اختبار سهل آخر هو عدد الأحرف في البرنامج، لكن هذا ليس جيدًا أيضًا؛ بعض اللغات (مثل Perl) تستخدم معرفات أقصر من غيرها.
أعتقد أن مقياسًا أفضل لحجم البرنامج سيكون عدد العناصر، حيث يكون العنصر أي شيء سيكون عقدة مميزة إذا رسمت شجرة تمثل الكود المصدري. اسم متغير أو دالة هو عنصر؛ عدد صحيح أو رقم عشري هو عنصر؛ جزء من نص حرفي هو عنصر؛ عنصر في نمط، أو توجيه تنسيق، هو عنصر؛ كتلة جديدة هي عنصر. هناك حالات حدودية (هل -5 عنصران أم واحد؟) لكنني أعتقد أن معظمها متشابه لكل لغة، لذلك لا تؤثر على المقارنات كثيرًا.
هذا المقياس يحتاج إلى تفصيل، وقد يتطلب تفسيرًا في حالة لغات معينة، لكنني أعتقد أنه يحاول قياس الشيء الصحيح، وهو عدد الأجزاء التي يمتلكها البرنامج. أعتقد أن الشجرة التي سترسمها في هذا التمرين هي ما عليك تكوينه في رأسك لتصور البرنامج، وبالتالي فإن حجمه يتناسب مع مقدار العمل الذي يتعين عليك القيام به لكتابته أو قراءته.
التصميم
هذا النوع من المقاييس سيسمح لنا بمقارنة لغات مختلفة، لكن هذا ليس، على الأقل بالنسبة لي، قيمته الرئيسية. القيمة الرئيسية لاختبار الإيجاز هي كدليل في تصميم اللغات. المقارنة الأكثر فائدة بين اللغات هي بين متغيرين محتملين لنفس اللغة. ماذا يمكنني أن أفعل في اللغة لجعل البرامج أقصر؟
إذا كان الحمل المفاهيمي للبرنامج يتناسب مع تعقيده، ويمكن لمبرمج معين تحمل حمولة مفاهيمية ثابتة، فهذا هو نفس السؤال، ماذا يمكنني أن أفعل لتمكين المبرمجين من إنجاز أكبر قدر ممكن؟ وهذا يبدو لي مطابقًا للسؤال، كيف يمكنني تصميم لغة جيدة؟
(بالمناسبة، لا شيء يجعل الأمر أكثر وضوحًا من أن الخرافة القديمة "كل اللغات متكافئة" خاطئة أكثر من تصميم اللغات. عندما تقوم بتصميم لغة جديدة، فأنت تقارن باستمرار بين لغتين - اللغة إذا فعلت كذا، وإذا لم أفعل - لتحديد أيهما أفضل. إذا كان هذا سؤالاً لا معنى له حقًا، فيمكنك بنفس القدر رمي عملة معدة.)
الاستهداف للإيجاز يبدو طريقة جيدة للعثور على أفكار جديدة. إذا كان بإمكانك فعل شيء يجعل العديد من البرامج المختلفة أقصر، فمن المحتمل أنه ليس مصادفة: ربما اكتشفت تجريدًا جديدًا مفيدًا. قد تتمكن حتى من كتابة برنامج للمساعدة عن طريق البحث في الكود المصدري عن أنماط متكررة. من بين اللغات الأخرى، تلك التي تتمتع بسمعة طيبة في الإيجاز ستكون هي التي تبحث فيها عن أفكار جديدة: Forth، Joy، Icon.
المقارنة
أول شخص كتب عن هذه القضايا، على حد علمي، كان فريد بروكس في كتاب The Mythical Man Month. كتب أن المبرمجين ينتجون نفس القدر من الكود يوميًا بغض النظر عن اللغة. عندما قرأت هذا لأول مرة في أوائل العشرينات من عمري، كان مفاجأة كبيرة لي وبدا أن له آثارًا هائلة. هذا يعني أن (أ) الطريقة الوحيدة لكتابة البرامج بشكل أسرع هي استخدام لغة أكثر إيجازًا، و (ب) شخصًا ما تحمل عناء القيام بذلك يمكنه ترك المنافسين الذين لم يفعلوا ذلك في الغبار.
فرضية بروكس، إذا كانت صحيحة، تبدو في صميم القرصنة. في السنوات التي تلت ذلك، انتبهت عن كثب لأي دليل يمكنني الحصول عليه حول هذه المسألة، من الدراسات الرسمية إلى القصص عن المشاريع الفردية. لم أر شيئًا يتعارض معه.
لم أر بعد دليلًا بدا لي قاطعًا، ولا أتوقع ذلك. الدراسات مثل مقارنة لوتز بريشلت للغات البرمجة، بينما تولد النتائج التي توقعتها، تميل إلى استخدام مشاكل قصيرة جدًا لتكون اختبارات ذات مغزى. اختبار أفضل للغة هو ما يحدث في البرامج التي تستغرق شهرًا لكتابتها. والاختبار الحقيقي الوحيد، إذا كنت تعتقد كما أعتقد أن الغرض الرئيسي من اللغة هو أن تكون جيدة للتفكير بها (بدلاً من مجرد إخبار الكمبيوتر بما يجب فعله بمجرد التفكير فيه) هو الأشياء الجديدة التي يمكنك كتابتها بها. لذلك أي مقارنة لغة حيث يتعين عليك تلبية مواصفات محددة مسبقًا تختبر شيئًا خاطئًا قليلاً.
الاختبار الحقيقي للغة هو مدى جودة اكتشافك وحلك للمشاكل الجديدة، وليس مدى جودة استخدامك لحل مشكلة صاغها شخص آخر بالفعل. هذان معياران مختلفان تمامًا. في الفن، الوسائط مثل التطريز والفسيفساء تعمل بشكل جيد إذا كنت تعرف مسبقًا ما تريد صنعه، ولكنها سيئة للغاية إذا لم تفعل. عندما تريد اكتشاف الصورة أثناء صنعها - كما عليك أن تفعل مع أي شيء معقد مثل صورة شخص، على سبيل المثال - تحتاج إلى استخدام وسيط أكثر مرونة مثل قلم رصاص أو حبر أو طلاء زيتي. وبالفعل، فإن الطريقة التي تُصنع بها المنسوجات والفسيفساء في الممارسة العملية هي صنع لوحة أولاً، ثم نسخها. (كلمة "كرتون" استخدمت في الأصل لوصف لوحة مخصصة لهذا الغرض).
ما يعنيه هذا هو أننا لن نحصل أبدًا على مقارنات دقيقة للقوة النسبية للغات البرمجة. سنحصل على مقارنات دقيقة، ولكن ليس دقيقة. على وجه الخصوص، الدراسات الصريحة لغرض مقارنة اللغات، لأنها ستستخدم مشاكل صغيرة على الأرجح، وستستخدم بالضرورة مشاكل محددة مسبقًا، ستميل إلى التقليل من شأن قوة اللغات الأكثر قوة.
التقارير من الميدان، على الرغم من أنها ستكون بالضرورة أقل دقة من الدراسات "العلمية"، من المرجح أن تكون أكثر فائدة. على سبيل المثال، أجرى Ulf Wiger من Ericsson دراسة دراسة خلصت إلى أن Erlang كانت أكثر إيجازًا بـ 4-10 مرات من C++، وأسرع بشكل متناسب في تطوير البرامج:
تشير المقارنات بين مشاريع التطوير الداخلية في Ericsson إلى إنتاجية مماثلة للأسطر/الساعة، بما في ذلك جميع مراحل تطوير البرامج، بشكل مستقل تقريبًا عن أي لغة (Erlang، PLEX، C، C++، أو Java) تم استخدامها. ما يميز اللغات المختلفة إذن هو حجم الكود المصدري.
تتعامل الدراسة أيضًا بشكل صريح مع نقطة كانت ضمنية فقط في كتاب بروكس (حيث قام بقياس أسطر الكود المصحح): تميل البرامج المكتوبة بلغات أقوى إلى أن تحتوي على عدد أقل من الأخطاء. وهذا يصبح غاية في حد ذاته، ربما أكثر أهمية من إنتاجية المبرمج، في تطبيقات مثل محولات الشبكة.
اختبار الذوق
في النهاية، أعتقد أن عليك أن تذهب مع حدسك. كيف تشعر بالبرمجة في اللغة؟ أعتقد أن الطريقة للعثور على (أو تصميم) أفضل لغة هي أن تصبح حساسًا للغاية لمدى جودة السماح للغة بالتفكير، ثم اختيار/تصميم اللغة التي تبدو أفضل. إذا كانت أي ميزة في اللغة غير ملائمة أو مقيدة، فلا تقلق، ستعرف بها.
مثل هذه الحساسية المفرطة ستأتي بتكلفة. ستجد أنك لا تستطيع تحمل البرمجة في لغات غبية. أجد أنه من المقيد بشكل لا يطاق العودة إلى البرمجة بلغة تحتاج فيها إلى الإعلان عن نوع كل متغير، ولا يمكنك إنشاء قائمة بكائنات من أنواع مختلفة، كما هو الحال بالنسبة لشخص اعتاد على الكتابة الديناميكية.
أنا لست الوحيد. أعرف العديد من مبرمجي Lisp الذين حدث لهم هذا. في الواقع، قد يكون المقياس الأكثر دقة للقوة النسبية للغات البرمجة هو النسبة المئوية للأشخاص الذين يعرفون اللغة والذين سيقبلون أي وظيفة يستخدمون فيها تلك اللغة، بغض النظر عن مجال التطبيق.
التقييد
أعتقد أن معظم المبرمجين يعرفون ما يعنيه أن تشعر اللغة بأنها مقيدة. ماذا يحدث عندما تشعر بذلك؟ أعتقد أنها نفس الشعور الذي تشعر به عندما يكون الشارع الذي تريد أن تسلكه مسدودًا، وعليك أن تسلك طريقًا التفافيًا طويلاً للوصول إلى المكان الذي كنت تريده. هناك شيء تريد قوله، واللغة لا تسمح لك بذلك.
ما يحدث حقًا هنا، أعتقد، هو أن اللغة المقيدة هي لغة ليست موجزة بما فيه الكفاية. المشكلة ليست ببساطة أنك لا تستطيع قول ما خططت له. إنها أن الطريق الالتفافي الذي تجبرك اللغة على اتخاذه أطول. جرب هذه التجربة الفكرية. افترض أن هناك برنامجًا تريد كتابته، واللغة لا تسمح لك بالتعبير عنه بالطريقة التي خططت لها، بل تجبرك على كتابة البرنامج بطريقة أخرى أقصر. بالنسبة لي على الأقل، لن يبدو ذلك مقيدًا للغاية. سيكون الأمر أشبه بسد الطريق الذي أردت أن تسلكه، والشرطي عند التقاطع يوجهك إلى طريق مختصر بدلاً من طريق التفاف. رائع!
أعتقد أن معظم (تسعين بالمائة؟) من الشعور بالتقييد يأتي من الاضطرار إلى جعل البرنامج الذي تكتبه باللغة أطول من البرنامج الذي لديك في رأسك. التقييد هو في الغالب نقص في الإيجاز. لذلك عندما تشعر اللغة بأنها مقيدة، فإن ما يعنيه ذلك (في الغالب) هو أنها ليست موجزة بما فيه الكفاية، وعندما تكون اللغة غير موجزة، فإنها ستبدو مقيدة.
سهولة القراءة
الاقتباس الذي بدأت به يذكر صفتين أخريين، الانتظام وسهولة القراءة. لست متأكدًا مماهية الانتظام، أو ما هي الميزة، إن وجدت، التي يتمتع بها الكود المنتظم والمقروء مقارنة بالكود المقروء ببساطة. لكنني أعتقد أنني أعرف ما يعنيه سهولة القراءة، وأعتقد أنها مرتبطة أيضًا بالإيجاز.
علينا أن نكون حذرين هنا للتمييز بين سهولة قراءة سطر كود فردي وسهولة قراءة البرنامج بأكمله. هذا هو ما يهم. أتفق على أن سطرًا من Basic من المحتمل أن يكون أكثر قابلية للقراءة من سطر من Lisp. لكن برنامجًا مكتوبًا بلغة Basic سيكون به أسطر أكثر من نفس البرنامج المكتوب بلغة Lisp (خاصة بمجرد عبورك إلى Greenspunland). الجهد الإجمالي لقراءة برنامج Basic سيكون بالتأكيد أكبر.
الجهد الإجمالي = الجهد لكل سطر × عدد الأسطر
لست متأكدًا من أن سهولة القراءة تتناسب طرديًا مع الإيجاز كما أنا متأكد من أن القوة كذلك، لكن بالتأكيد الإيجاز عامل (بالمعنى الرياضي؛ انظر المعادلة أعلاه) في سهولة القراءة. لذلك قد لا يكون من المنطقي حتى القول بأن هدف اللغة هو سهولة القراءة، وليس الإيجاز؛ قد يكون مثل القول بأن الهدف هو سهولة القراءة، وليس سهولة القراءة.
ما تعنيه سهولة القراءة لكل سطر، للمستخدم الذي يواجه اللغة لأول مرة، هو أن الكود المصدري سيبدو غير مخيف. لذلك يمكن أن تكون سهولة القراءة لكل سطر قرار تسويقي جيد، حتى لو كان قرار تصميم سيئ. إنه متطابق مع تقنية ناجحة جدًا تتمثل في السماح للناس بالدفع على أقساط: بدلاً من تخويفهم بسعر مرتفع مقدمًا، يخبرونك بالدفعة الشهرية المنخفضة. خطط التقسيط هي خسارة صافية للمشتري، على الرغم من ذلك، كما هو الحال مع سهولة القراءة لكل سطر على الأرجح للمبرمج. سيقوم المشتري بسداد الكثير من هذه الدفعات المنخفضة جدًا؛ وسيقرأ المبرمج الكثير من هذه الأسطر المقروءة بشكل فردي.
هذه المقايضة تسبق لغات البرمجة. إذا كنت معتادًا على قراءة الروايات والمقالات الصحفية، فإن تجربتك الأولى في قراءة ورقة رياضية قد تكون محبطة. قد يستغرق الأمر نصف ساعة لقراءة صفحة واحدة. ومع ذلك، أنا متأكد تمامًا من أن الترميز ليس هو المشكلة، على الرغم من أنه قد يبدو كذلك. الورقة الرياضية صعبة القراءة لأن الأفكار صعبة. إذا عبرت عن نفس الأفكار بالنثر (كما اضطر الرياضيون إلى القيام به قبل أن يطوروا ترميزات موجزة)، فلن تكون أسهل في القراءة، لأن الورقة ستنمو إلى حجم كتاب.
إلى أي مدى؟
رفض عدد من الأشخاص فكرة أن الإيجاز = القوة. أعتقد أنه سيكون أكثر فائدة، بدلاً من مجادلين ببساطة بأنهم متماثلون أو ليسوا كذلك، أن نسأل: إلى أي مدى الإيجاز = القوة؟ لأن الإيجاز من الواضح أنه جزء كبير مما تُستخدم من أجله اللغات عالية المستوى. إذا لم يكن هذا كل ما تُستخدم من أجله، فما هي وظائفها الأخرى، ومدى أهميتها النسبية؟
أنا لا أقترح هذا فقط لجعل النقاش أكثر تحضرًا. أريد حقًا معرفة الإجابة. متى، إن حدث ذلك على الإطلاق، تكون اللغة موجزة جدًا لدرجة تضر بها؟
الفرضية التي بدأت بها كانت أنه باستثناء الأمثلة المرضية، اعتقدت أن الإيجاز يمكن اعتباره متطابقًا مع القوة. ما قصدته هو أنه في أي لغة يصممها أي شخص، ستكون متطابقة، ولكن إذا أراد شخص ما تصميم لغة لدحض هذه الفرضية صراحة، فيمكنه القيام بذلك على الأرجح. أنا لست متأكدًا من ذلك حتى، في الواقع.
لغات، وليس برامج
يجب أن نكون واضحين أننا نتحدث عن إيجاز اللغات، وليس البرامج الفردية. من الممكن بالتأكيد كتابة برامج فردية بكثافة مفرطة.
كتبت عن هذا في On Lisp. قد يحتاج الماكرو المعقد إلى توفير أضعاف طوله لتبريره. إذا كان كتابة ماكرو معقد يمكن أن يوفر لك عشرة أسطر من الكود في كل مرة تستخدمه فيها، والماكرو نفسه هو عشرة أسطر من الكود، فستحصل على توفير صافٍ في الأسطر إذا استخدمته أكثر من مرة. ولكن هذا لا يزال يمكن أن يكون خطوة سيئة، لأن تعريفات الماكرو أصعب في القراءة من الكود العادي. قد تحتاج إلى استخدام الماكرو عشر أو عشرين مرة قبل أن يحقق تحسنًا صافيًا في سهولة القراءة.
أنا متأكد من أن كل لغة لديها مثل هذه المقايضات (على الرغم من أنني أشك في أن المخاطر تزداد مع قوة اللغة). يجب أن يكون كل مبرمج قد رأى كودًا قام شخص ذكي بجعله أقصر بشكل هامشي باستخدام حيل برمجة مشكوك فيها.
لذلك لا يوجد جدال حول ذلك - على الأقل، ليس مني. يمكن بالتأكيد أن تكون البرامج الفردية موجزة جدًا لدرجة تضر بها. السؤال هو، هل يمكن للغة أن تكون كذلك؟ هل يمكن للغة أن تجبر المبرمجين على كتابة كود قصير (بالعناصر) على حساب سهولة القراءة الشاملة؟
أحد أسباب صعوبة تخيل لغة موجزة جدًا هو أنه إذا كانت هناك طريقة مدمجة بشكل مفرط للتعبير عن شيء ما، فمن المحتمل أن تكون هناك أيضًا طريقة أطول. على سبيل المثال، إذا شعرت أن برامج Lisp التي تستخدم الكثير من وحدات الماكرو أو الدوال عالية المستوى كثيفة جدًا، يمكنك، إذا كنت تفضل ذلك، كتابة كود متطابق مع Pascal. إذا كنت لا ترغب في التعبير عن المضروب في Arc كاستدعاء لدالة عالية المستوى (rec zero 1 * 1-) يمكنك أيضًا كتابة تعريف تكراري: (rfn fact (x) (if (zero x) 1 (* x (fact (1- x))))) على الرغم من أنني لا أستطيع التفكير في أمثلة من الذاكرة، إلا أنني مهتم بمسألة ما إذا كانت اللغة يمكن أن تكون موجزة جدًا. هل هناك لغات تجبرك على كتابة كود بطريقة متشنجة وغير مفهومة؟ إذا كان لدى أي شخص أمثلة، فسأكون مهتمًا جدًا برؤيتها.
(تذكير: ما أبحث عنه هو البرامج الكثيفة جدًا وفقًا لمقياس "العناصر" الموضح أعلاه، وليس مجرد البرامج القصيرة لأن الفواصل يمكن حذفها وكل شيء له اسم مكون من حرف واحد.)