انتقام المهووسين

هل تريد بدء شركة ناشئة؟ احصل على تمويل من Y Combinator.


مايو 2002

| "لقد كنا نستهدف مبرمجي C++. لقد نجحنا في سحب الكثير منهم إلى منتصف الطريق نحو Lisp."

  • جاي ستيل، المؤلف المشارك لمواصفات Java

في مجال البرمجيات، هناك صراع مستمر بين الأكاديميين ذوي الرؤوس المدببة، وقوة أخرى لا تقل عنها، وهم الرؤساء ذوو الشعر المدبب. الجميع يعرف من هو الرئيس ذو الشعر المدبب، أليس كذلك؟ أعتقد أن معظم الأشخاص في عالم التكنولوجيا لا يتعرفون فقط على هذه الشخصية الكرتونية، بل يعرفون الشخص الفعلي في شركتهم الذي تم تصويره بناءً عليه.

يجمع الرئيس ذو الشعر المدبب بشكل معجزتين صفتين شائعتين بحد ذاتهما، ولكنهما نادراً ما تريان معًا: (أ) أنه لا يعرف شيئًا على الإطلاق عن التكنولوجيا، و (ب) لديه آراء قوية جدًا بشأنها.

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

لماذا يعتقد ذلك؟ دعنا نلقي نظرة داخل عقل الرئيس ذو الشعر المدبب. ما يفكر فيه هو شيء من هذا القبيل. Java هي معيار. أعرف أنها يجب أن تكون كذلك، لأنني أقرأ عنها في الصحافة طوال الوقت. بما أنها معيار، فلن أتعرض للمشاكل لاستخدامها. وهذا يعني أيضًا أنه سيكون هناك دائمًا الكثير من مبرمجي Java، لذلك إذا استقال المبرمجون الذين يعملون لدي الآن، كما يفعل المبرمجون الذين يعملون لدي بشكل غامض دائمًا، يمكنني استبدالهم بسهولة.

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

لكن جميع اللغات ليست متكافئة، وأعتقد أنني أستطيع إثبات ذلك لك دون الدخول في الاختلافات بينها. إذا سألت الرئيس ذو الشعر المدبب في عام 1992 عن اللغة التي يجب أن تُكتب بها البرمجيات، لكان قد أجاب بنفس القدر من عدم التردد كما يفعل اليوم. يجب أن تُكتب البرمجيات بلغة C++. ولكن إذا كانت اللغات كلها متكافئة، فلماذا يجب أن يتغير رأي الرئيس ذو الشعر المدبب على الإطلاق؟ في الواقع، لماذا كان يجب على مطوري Java حتى عناء إنشاء لغة جديدة؟

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

إذن، من هو على حق؟ جيمس جوسلينج، أم الرئيس ذو الشعر المدبب؟ ليس من المستغرب أن جوسلينج على حق. بعض اللغات أفضل، لمشاكل معينة، من غيرها. وأنت تعلم، هذا يثير بعض الأسئلة المثيرة للاهتمام. تم تصميم Java لتكون أفضل، لمشاكل معينة، من C++. ما هي المشاكل؟ متى تكون Java أفضل ومتى تكون C++؟ هل هناك مواقف تكون فيها لغات أخرى أفضل من أي منهما؟

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

عيب الاعتقاد بأن جميع لغات البرمجة متكافئة هو أنها ليست صحيحة. لكن الميزة هي أنها تجعل حياتك أسهل بكثير. وأعتقد أن هذا هو السبب الرئيسي لانتشار الفكرة. إنها فكرة مريحة.

نعرف أن Java يجب أن تكون جيدة جدًا، لأنها لغة البرمجة الجديدة والرائجة. أم أنها كذلك؟ إذا نظرت إلى عالم لغات البرمجة عن بعد، يبدو أن Java هي أحدث شيء. (من مسافة كافية، كل ما يمكنك رؤيته هو اللوحة الإعلانية الكبيرة الوامضة التي دفعتها Sun.) ولكن إذا نظرت إلى هذا العالم عن قرب، ستجد أن هناك درجات من الروعة. ضمن ثقافة الهاكرز الفرعية، هناك لغة أخرى تسمى Perl تعتبر أكثر روعة بكثير من Java. Slashdot، على سبيل المثال، يتم إنشاؤه بواسطة Perl. لا أعتقد أنك ستجد هؤلاء الأشخاص يستخدمون Java Server Pages. ولكن هناك لغة أخرى أحدث، تسمى Python، يميل مستخدموها إلى الاستعلاء على Perl، و المزيد ينتظر في الكواليس.

إذا نظرت إلى هذه اللغات بالترتيب، Java، Perl، Python، ستلاحظ نمطًا مثيرًا للاهتمام. على الأقل، ستلاحظ هذا النمط إذا كنت من مستخدمي Lisp. كل واحدة منها تشبه Lisp بشكل تدريجي. Python تنسخ حتى الميزات التي يعتبرها العديد من مستخدمي Lisp أخطاء. يمكنك ترجمة برامج Lisp البسيطة إلى Python سطرًا بسطر. إنها عام 2002، ولغات البرمجة قد لحقت تقريبًا بأفكار عام 1958.

اللحاق بالرياضيات

ما أعنيه هو أن Lisp اكتشفها جون مكارثي لأول مرة في عام 1958، ولغات البرمجة الشائعة تلحق الآن بالأفكار التي طورها في ذلك الوقت.

الآن، كيف يمكن أن يكون ذلك صحيحًا؟ أليست تكنولوجيا الكمبيوتر شيئًا يتغير بسرعة كبيرة؟ أعني، في عام 1958، كانت أجهزة الكمبيوتر عمالقة بحجم الثلاجة بقوة معالجة ساعة اليد. كيف يمكن لأي تكنولوجيا قديمة أن تكون ذات صلة، ناهيك عن تفوقها على أحدث التطورات؟

سأخبرك كيف. ذلك لأن Lisp لم تُصمم حقًا لتكون لغة برمجة، على الأقل بالمعنى الذي نقصده اليوم. ما نعنيه بلغة البرمجة هو شيء نستخدمه لإخبار الكمبيوتر بما يجب فعله. مكارثي قصد في النهاية تطوير لغة برمجة بهذا المعنى، لكن Lisp التي انتهى بنا الأمر بها بالفعل كانت تستند إلى شيء منفصل قام به كـ تمرين نظري -- جهد لتحديد بديل أكثر ملاءمة لآلة تورينج. كما قال مكارثي لاحقًا:

طريقة أخرى لإظهار أن Lisp كانت ألطف من آلات تورينج هي كتابة دالة Lisp عالمية وإظهار أنها أقصر وأكثر قابلية للفهم من وصف آلة تورينج عالمية. كانت هذه دالة Lisp eval...

، التي تحسب قيمة تعبير Lisp.... تطلب كتابة eval اختراع تدوين يمثل دوال Lisp كبيانات Lisp، وتم وضع مثل هذا التدوين لأغراض الورقة دون أي تفكير في استخدامه للتعبير عن برامج Lisp عمليًا.

ما حدث بعد ذلك هو أنه في وقت ما في أواخر عام 1958، نظر ستيف راسل، أحد طلاب مكارثي، إلى هذا التعريف لـ eval وأدرك أنه إذا قام بترجمته إلى لغة الآلة، فإن النتيجة ستكون مترجم Lisp.

كان هذا مفاجأة كبيرة في ذلك الوقت. إليك ما قاله مكارثي عن ذلك لاحقًا في مقابلة:

قال ستيف راسل، انظر، لماذا لا أبرمج هذا eval...

، وقلت له، هو هو، أنت تخلط بين النظرية والممارسة، هذا eval مخصص للقراءة، وليس للحساب. لكنه مضى وقام بذلك. أي أنه قام بتجميع eval في ورقتي إلى [IBM] 704 لغة الآلة، وإصلاح الأخطاء، ثم أعلن عن ذلك كمترجم Lisp، وهو بالتأكيد كان كذلك. لذلك في تلك المرحلة كان لدى Lisp الشكل الذي لديها اليوم تقريبًا....

فجأة، في غضون أسابيع أعتقد، وجد مكارثي تمرينه النظري يتحول إلى لغة برمجة فعلية-- وأكثر قوة مما كان ينوي.

لذلك الشرح المختصر لسبب عدم تقادم لغة الخمسينيات هذه هو أنها لم تكن تكنولوجيا بل رياضيات، والرياضيات لا تتقادم. الشيء الصحيح لمقارنة Lisp به ليس أجهزة الخمسينيات، بل، لنقل، خوارزمية Quicksort، التي اكتشفت في عام 1960 ولا تزال أسرع فرز للأغراض العامة.

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

Fortran I، اللغة التي تم تطويرها في عام 1956، كانت حيوانًا مختلفًا تمامًا عن Fortran الحالي. كان Fortran I تقريبًا لغة تجميع مع الرياضيات. من بعض النواحي كانت أقل قوة من لغات التجميع الأحدث؛ لم تكن هناك روتينات فرعية، على سبيل المثال، فقط فروع. Fortran الحالي هو الآن أقرب إلى Lisp منه إلى Fortran I.

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

ما الذي جعل Lisp مختلفة

عندما تم تطويرها لأول مرة، جسدت Lisp تسع أفكار جديدة. بعض هذه الأفكار نأخذها كأمر مسلم به الآن، والبعض الآخر يُرى فقط في اللغات الأكثر تقدمًا، واثنان لا يزالان فريدين لـ Lisp. الأفكار التسع هي، بترتيب تبنيها من قبل التيار الرئيسي:

  1. الشروط. الشرط هو بناء if-then-else. نأخذ هذه كأمر مسلم به الآن، لكن Fortran I لم يكن لديها. كان لديها فقط goto شرطي يعتمد بشكل وثيق على تعليمات الآلة الأساسية.

  2. نوع الدالة. في Lisp، الدوال هي نوع بيانات مثل الأعداد الصحيحة أو السلاسل النصية. لها تمثيل حرفي، يمكن تخزينها في متغيرات، يمكن تمريرها كوسائط، وما إلى ذلك.

  3. العودية. كانت Lisp أول لغة برمجة تدعمها.

  4. الكتابة الديناميكية. في Lisp، جميع المتغيرات هي مؤشرات فعليًا. القيم هي التي لها أنواع، وليس المتغيرات، وتعيين أو ربط المتغيرات يعني نسخ المؤشرات، وليس ما تشير إليه.

  5. جمع القمامة.

  6. البرامج تتكون من تعبيرات. برامج Lisp هي أشجار من التعبيرات، كل منها يعيد قيمة. هذا على عكس Fortran ومعظم اللغات اللاحقة، التي تميز بين التعبيرات والجمل.

كان من الطبيعي وجود هذا التمييز في Fortran I لأنه لم يكن بإمكانك تداخل الجمل. ولذلك بينما كنت بحاجة إلى تعبيرات لعمل الرياضيات، لم يكن هناك جدوى من جعل أي شيء آخر يعيد قيمة، لأنه لم يكن هناك شيء ينتظرها.

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

  1. نوع الرمز. الرموز هي مؤشرات فعليًا للسلاسل النصية المخزنة في جدول تجزئة. لذلك يمكنك اختبار المساواة عن طريق مقارنة مؤشر، بدلاً من مقارنة كل حرف.

  2. تدوين للكود باستخدام أشجار من الرموز والثوابت.

  3. اللغة بأكملها موجودة طوال الوقت. لا يوجد تمييز حقيقي بين وقت القراءة، ووقت الترجمة، ووقت التشغيل. يمكنك ترجمة أو تشغيل الكود أثناء القراءة، قراءة أو تشغيل الكود أثناء الترجمة، وقراءة أو ترجمة الكود في وقت التشغيل.

تشغيل الكود في وقت القراءة يسمح للمستخدمين بإعادة برمجة بناء جملة Lisp؛ تشغيل الكود في وقت الترجمة هو أساس وحدات الماكرو؛ الترجمة في وقت التشغيل هي أساس استخدام Lisp كلغة توسيع في برامج مثل Emacs؛ والقراءة في وقت التشغيل تمكن البرامج من التواصل باستخدام s-expressions، وهي فكرة أعيد اختراعها مؤخرًا باسم XML.

عندما ظهرت Lisp لأول مرة، كانت هذه الأفكار بعيدة كل البعد عن ممارسة البرمجة العادية، التي كانت تُملى إلى حد كبير بواسطة الأجهزة المتاحة في أواخر الخمسينيات. بمرور الوقت، تطورت اللغة الافتراضية، المجسدة في سلسلة من اللغات الشائعة، تدريجيًا نحو Lisp. الأفكار 1-5 منتشرة الآن على نطاق واسع. الرقم 6 بدأ في الظهور في التيار الرئيسي. Python لديها شكل من أشكال 7، على الرغم من أنه لا يبدو أن هناك أي بناء جمل لها.

أما بالنسبة للرقم 8، فقد يكون هذا هو الأكثر إثارة للاهتمام في المجموعة. الأفكار 8 و 9 أصبحت جزءًا من Lisp بالصدفة، لأن ستيف راسل نفذ شيئًا لم يكن مكارثي ينوي تنفيذه أبدًا. ومع ذلك، تبين أن هذه الأفكار مسؤولة عن كل من مظهر Lisp الغريب وأكثر ميزاتها تميزًا. تبدو Lisp غريبة ليس كثيرًا لأن لديها بناء جمل غريب بقدر ما لأنها لا تملك بناء جمل؛ أنت تعبر عن البرامج مباشرة في أشجار التحليل التي يتم بناؤها في الخلفية عندما يتم تحليل اللغات الأخرى، وهذه الأشجار مصنوعة من قوائم، وهي هياكل بيانات Lisp.

تبين أن التعبير عن اللغة في هياكل بياناتها الخاصة ميزة قوية جدًا. الأفكار 8 و 9 معًا تعني أنه يمكنك كتابة برامج تكتب برامج. قد يبدو هذا فكرة غريبة، ولكنه أمر يومي في Lisp. الطريقة الأكثر شيوعًا للقيام بذلك هي باستخدام شيء يسمى macro.

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

لا تزال وحدات الماكرو (بالمعنى Lisp) ، على حد علمي، فريدة من نوعها لـ Lisp. ويرجع ذلك جزئيًا إلى أنه لكي تحصل على وحدات ماكرو، ربما عليك أن تجعل لغتك تبدو غريبة مثل Lisp. قد يكون ذلك أيضًا لأنه إذا أضفت تلك الزيادة النهائية في القوة، فلا يمكنك بعد ذلك الادعاء بأنك اخترعت لغة جديدة، بل مجرد لهجة جديدة من Lisp.

أذكر هذا في الغالب على سبيل المزاح، ولكنه صحيح تمامًا. إذا قمت بتعريف لغة تحتوي على car، cdr، cons، quote، cond، atom، eq، وتدوين للدوال المعبر عنها كقوائم، فيمكنك بناء كل ما تبقى من Lisp منها. هذا في الواقع هو الصفة المميزة لـ Lisp: كان من أجل جعل هذا هو الحال أن أعطى مكارثي Lisp الشكل الذي لديها.

أين تهم اللغات

لذا، إذا كانت Lisp تمثل نوعًا من الحد الذي تقترب منه اللغات الرئيسية بشكل تقاربي - فهل هذا يعني أنه يجب عليك بالفعل استخدامها لكتابة البرمجيات؟ ما مدى ما تخسره باستخدام لغة أقل قوة؟ أليس من الحكمة، أحيانًا، عدم أن تكون في طليعة الابتكار؟ وهل الشعبية ليست مبررًا بحد ذاتها إلى حد ما؟ أليس الرئيس ذو الشعر المدبب على حق، على سبيل المثال، في الرغبة في استخدام لغة يمكنه من خلالها توظيف مبرمجين بسهولة؟

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

يمكنك أيضًا كتابة برامج ربط صغيرة في Lisp (أستخدمها كآلة حاسبة مكتبية)، ولكن أكبر مكسب للغات مثل Lisp هو في الطرف الآخر من الطيف، حيث تحتاج إلى كتابة برامج متطورة لحل مشاكل صعبة في مواجهة منافسة شرسة. مثال جيد هو برنامج البحث عن أسعار تذاكر الطيران الذي ترخصه ITA Software لـ Orbitz. دخل هؤلاء الرجال إلى سوق تهيمن عليه بالفعل شركتان كبيرتان وراسختان، Travelocity و Expedia، ويبدو أنهم أذلوهما تقنيًا.

جوهر تطبيق ITA هو برنامج Common Lisp بحجم 200 ألف سطر يبحث عن عدد أكبر بكثير من الاحتمالات مقارنة بمنافسيهم، الذين يبدو أنهم لا يزالون يستخدمون تقنيات برمجة من عصر الحواسيب المركزية. (على الرغم من أن ITA تستخدم أيضًا، بمعنى ما، لغة برمجة من عصر الحواسيب المركزية.) لم أر أبدًا أيًا من كود ITA، ولكن وفقًا لأحد أفضل المبرمجين لديهم، فإنهم يستخدمون الكثير من وحدات الماكرو، ولا أتفاجأ بسماع ذلك.

قوى الجذب المركزي

لا أقول إنه لا توجد تكلفة لاستخدام التقنيات غير الشائعة. الرئيس ذو الشعر المدبب ليس مخطئًا تمامًا في القلق بشأن هذا. ولكن لأنه لا يفهم المخاطر، فإنه يميل إلى تضخيمها.

يمكنني التفكير في ثلاث مشاكل قد تنشأ عن استخدام لغات أقل شيوعًا. قد لا تعمل برامجك بشكل جيد مع البرامج المكتوبة بلغات أخرى. قد يكون لديك عدد أقل من المكتبات المتاحة لك. وقد تواجه صعوبة في توظيف المبرمجين.

ما مدى مشكلة كل من هذه؟ تختلف أهمية الأولى اعتمادًا على ما إذا كنت تتحكم في النظام بأكمله. إذا كنت تكتب برامج يجب تشغيلها على جهاز المستخدم عن بُعد فوق نظام تشغيل مغلق ومليء بالأخطاء (لا أذكر أسماء)، فقد تكون هناك مزايا لكتابة تطبيقك بنفس لغة نظام التشغيل. ولكن إذا كنت تتحكم في النظام بأكمله ولديك الكود المصدري لجميع الأجزاء، كما تفترض ITA، يمكنك استخدام أي لغات تريدها. إذا نشأ أي عدم توافق، يمكنك إصلاحه بنفسك.

في التطبيقات المستندة إلى الخادم، يمكنك الإفلات من استخدام التقنيات الأكثر تقدمًا، وأعتقد أن هذا هو السبب الرئيسي لما يسميه جوناثان إريكسون "نهضة لغة البرمجة". هذا هو السبب في أننا نسمع حتى عن لغات جديدة مثل Perl و Python. نحن لا نسمع عن هذه اللغات لأن الناس يستخدمونها لكتابة تطبيقات Windows، ولكن لأن الناس يستخدمونها على الخوادم. ومع تحول البرمجيات خارج سطح المكتب وإلى الخوادم (مستقبل حتى Microsoft تبدو مستقيلة منه)، سيكون هناك ضغط أقل وأقل لاستخدام التقنيات المتوسطة.

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

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

في الواقع، اختيار لغة أكثر قوة ربما يقلل من حجم الفريق الذي تحتاجه، لأن (أ) إذا استخدمت لغة أكثر قوة فلن تحتاج على الأرجح إلى العديد من المبرمجين، و (ب) المبرمجون الذين يعملون في لغات أكثر تقدمًا من المرجح أن يكونوا أذكى.

لا أقول إنك لن تواجه الكثير من الضغط لاستخدام ما يُنظر إليه على أنه تقنيات "قياسية". في Viaweb (الآن Yahoo Store)، أثرنا بعض الدهشة بين أصحاب رؤوس الأموال والمشترين المحتملين باستخدام Lisp. لكننا أثرنا أيضًا الدهشة باستخدام صناديق Intel عامة كخوادم بدلاً من خوادم "قوة صناعية" مثل Sun، لاستخدام متغير نظام تشغيل مفتوح المصدر غير شائع آنذاك يسمى FreeBSD بدلاً من نظام تشغيل تجاري حقيقي مثل Windows NT، لتجاهل معيار تجارة إلكترونية مفترض يسمى SET الذي لا يتذكره أحد الآن، وما إلى ذلك.

لا يمكنك السماح للمديرين باتخاذ قرارات تقنية لك. هل أزعجنا بعض المشترين المحتملين بأننا استخدمنا Lisp؟ البعض، قليلاً، ولكن إذا لم نستخدم Lisp، لما تمكنا من كتابة البرمجيات التي جعلتهم يرغبون في شرائنا. ما بدا وكأنه شذوذ بالنسبة لهم كان في الواقع سببًا ونتيجة.

إذا بدأت شركة ناشئة، فلا تصمم منتجك لإرضاء أصحاب رؤوس الأموال أو المشترين المحتملين. صمم منتجك لإرضاء المستخدمين. إذا فزت بالمستخدمين، فكل شيء آخر سيتبع. وإذا لم تفعل، فلن يهتم أحد بمدى تقليدية خياراتك التكنولوجية.

تكلفة أن تكون متوسطًا

ما مدى ما تخسره باستخدام لغة أقل قوة؟ هناك بالفعل بعض البيانات حول ذلك.

المقياس الأكثر ملاءمة للقوة هو ربما حجم الكود. الهدف من اللغات عالية المستوى هو منحك تجريدات أكبر - لبنات بناء أكبر، كما لو، لذلك لا تحتاج إلى الكثير لبناء جدار بحجم معين. لذلك كلما كانت اللغة أقوى، كان البرنامج أقصر (ليس فقط بالأحرف، بالطبع، ولكن بالعناصر المميزة).

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

حجم الكود مهم، لأن الوقت الذي يستغرقه كتابة برنامج يعتمد في الغالب على طوله. إذا كان برنامجك سيكون أطول بثلاث مرات بلغة أخرى، فسيستغرق كتابته ثلاثة أضعاف الوقت - ولا يمكنك التغلب على ذلك بتوظيف المزيد من الأشخاص، لأن الموظفين الجدد بعد حجم معين هم في الواقع خسارة صافية. وصف فريد بروكس هذه الظاهرة في كتابه الشهير The Mythical Man-Month، وكل ما رأيته يميل إلى تأكيد ما قاله.

إذن، ما مدى قصر برامجك إذا كتبتها بلغة Lisp؟ معظم الأرقام التي سمعتها لـ Lisp مقابل C، على سبيل المثال، كانت حوالي 7-10 أضعاف. لكن مقالًا حديثًا عن ITA في مجلة New Architect قال إن "سطرًا واحدًا من Lisp يمكن أن يحل محل 20 سطرًا من C"، وبما أن هذا المقال كان مليئًا بالاقتباسات من رئيس ITA، أفترض أنهم حصلوا على هذا الرقم من ITA. إذا كان الأمر كذلك، فيمكننا وضع بعض الثقة فيه؛ يتضمن برنامج ITA الكثير من C و C++ بالإضافة إلى Lisp، لذلك فهم يتحدثون من واقع خبرتهم.

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

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

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

لذلك عندما أقول إن الأمر سيستغرق منافس ITA الخيالي خمس سنوات لتكرار شيء يمكن لـ ITA كتابته بلغة Lisp في ثلاثة أشهر، فأنا أعني خمس سنوات إذا لم يحدث شيء خاطئ. في الواقع، بالطريقة التي تسير بها الأمور في معظم الشركات، من المحتمل أن أي مشروع تطوير يستغرق خمس سنوات لن يكتمل أبدًا.

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

وصفة

هذا هو نوع الاحتمال الذي لا يريد الرئيس ذو الشعر المدبب التفكير فيه. ولذلك فإن معظمهم لا يفعلون. لأن، كما تعلم، عندما يتعلق الأمر بالأمر، فإن الرئيس ذو الشعر المدبب لا يمانع إذا تعرضت شركته للضرب، طالما لا يمكن لأحد أن يثبت أن ذلك خطؤه. الخطة الأكثر أمانًا بالنسبة له شخصيًا هي البقاء قريبًا من وسط القطيع.

داخل المنظمات الكبيرة، العبارة المستخدمة لوصف هذا النهج هي "أفضل ممارسات الصناعة". الغرض منها هو حماية الرئيس ذو الشعر المدبب من المسؤولية: إذا اختار شيئًا هو "أفضل ممارسات الصناعة"، وخسرت الشركة، فلا يمكن لومه. لم يختر هو، بل اختارت الصناعة.

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

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

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


ملحق: القوة

كتوضيح لما أعنيه بشأن القوة النسبية للغات البرمجة، ضع في اعتبارك المشكلة التالية. نريد كتابة دالة تولد المجمعات - دالة تأخذ رقمًا n، وتعيد دالة تأخذ رقمًا آخر i وتعيد n مضافًا إليه i.

(هذا مضاف إليه ، وليس زائد. يجب على المجمع أن يجمع.)

في Common Lisp سيكون هذا (defun foo (n) (lambda (i) (incf n i))) وفي Perl 5، sub foo { my ($n) = @_; sub {$n += shift} } الذي يحتوي على عناصر أكثر من إصدار Lisp لأن عليك استخراج المعلمات يدويًا في Perl.

في Smalltalk، الكود أطول قليلاً من Lisp foo: n |s| s := n. ^[:i| s := s+i. ] لأنه على الرغم من أن المتغيرات المعجمية تعمل بشكل عام، لا يمكنك تعيين متغير لمعلمة، لذلك عليك إنشاء متغير جديد s.

في Javascript، المثال أطول قليلاً مرة أخرى، لأن Javascript تحتفظ بالتمييز بين الجمل والتعبيرات، لذلك تحتاج إلى عبارات return صريحة لإعادة القيم: function foo(n) { return function (i) { return n += i } } (للمنصفة، Perl تحتفظ أيضًا بهذا التمييز، ولكنها تتعامل معه بالطريقة المعتادة لـ Perl بالسماح لك بتخطي returns).

إذا حاولت ترجمة كود Lisp/Perl/Smalltalk/Javascript إلى Python، فستواجه بعض القيود. نظرًا لأن Python لا تدعم المتغيرات المعجمية بالكامل، عليك إنشاء هيكل بيانات للاحتفاظ بقيمة n. وعلى الرغم من أن Python لديها نوع بيانات للدالة، لا يوجد تمثيل حرفي لها (ما لم يكن الجسم عبارة عن تعبير واحد فقط)، لذلك تحتاج إلى إنشاء دالة مسماة لإعادتها. هذا ما ستنتهي به: def foo(n): s = [n] def bar(i): s[0] += i return s[0] return bar قد يسأل مستخدمو Python بشكل شرعي لماذا لا يمكنهم ببساطة كتابة def foo(n): return lambda i: return n += i أو حتى def foo(n): lambda i: n += i وتخميني هو أنهم ربما سيفعلون ذلك، يومًا ما. (ولكن إذا لم يرغبوا في انتظار Python لتتطور بالكامل إلى Lisp، فيمكنهم دائمًا فقط...)

في لغات OO، يمكنك، إلى حد محدود، محاكاة إغلاق (دالة تشير إلى متغيرات معرفة في نطاقات مغلقة) عن طريق تعريف فئة بطريقة واحدة وحقل لاستبدال كل متغير من نطاق مغلق. هذا يجعل المبرمج يقوم بتحليل الكود الذي سيتم إنشاؤه بواسطة المترجم في لغة ذات دعم كامل للنطاق المعجمي، ولن يعمل إذا أشارت أكثر من دالة إلى نفس المتغير، ولكنه كافٍ في الحالات البسيطة مثل هذه.

يتفق خبراء Python على أن هذه هي الطريقة المفضلة لحل المشكلة في Python، بكتابة إما def foo(n): class acc: def init(self, s): self.s = s def inc(self, i): self.s += i return self.s return acc(n).inc أو class foo: def init(self, n): self.n = n def call(self, i): self.n += i return self.n أدرج هذه لأنني لا أريد أن يقول دعاة Python أنني أسيء تمثيل اللغة، ولكن كلاهما يبدو لي أكثر تعقيدًا من الإصدار الأول. أنت تفعل الشيء نفسه، تقوم بإعداد مكان منفصل للاحتفاظ بالمجمع؛ إنه مجرد حقل في كائن بدلاً من رأس قائمة. واستخدام هذه الأسماء الحقول الخاصة والمحجوزة، خاصة __call__، يبدو وكأنه خدعة.

في التنافس بين Perl و Python، يبدو أن ادعاء مبرمجي Python هو أن Python بديل أكثر أناقة لـ Perl، ولكن ما يظهره هذا هو أن القوة هي الأناقة المطلقة: برنامج Perl أبسط (له عناصر أقل)، حتى لو كان بناء الجملة أقبح قليلاً.

ماذا عن اللغات الأخرى؟ في اللغات الأخرى المذكورة في هذا الحديث - Fortran، C، C++، Java، و Visual Basic - ليس من الواضح ما إذا كان يمكنك بالفعل حل هذه المشكلة. يقول كين أندرسون أن الكود التالي هو أقرب ما يمكنك الوصول إليه في Java:

public interface Inttoint {
  public int call(int i);
}

public static Inttoint foo(final int n) {
  return new Inttoint() {
    int s = n;
    public int call(int i) {
    s = s + i;
    return s;
    }};
}

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

ليس صحيحًا حرفيًا أنه لا يمكنك حل هذه المشكلة في لغات أخرى، بالطبع. حقيقة أن كل هذه اللغات متكافئة تورينج تعني، بالمعنى الدقيق للكلمة، يمكنك كتابة أي برنامج بأي منها. فكيف ستفعل ذلك؟ في الحالة القصوى، عن طريق كتابة مترجم Lisp باللغة الأضعف.

هذا يبدو وكأنه مزحة، ولكنه يحدث كثيرًا بدرجات متفاوتة في مشاريع البرمجة الكبيرة لدرجة أن هناك اسمًا لهذه الظاهرة، القاعدة العاشرة لجرينسبون:

أي برنامج C أو Fortran معقد بما فيه الكفاية يحتوي على تنفيذ غير رسمي وغير محدد الأخطاء وبطيء لنصف Common Lisp.

إذا حاولت حل مشكلة صعبة، فإن السؤال ليس ما إذا كنت ستستخدم لغة قوية بما فيه الكفاية، ولكن ما إذا كنت ستفعل (أ) تستخدم لغة قوية، (ب) تكتب مترجمًا فعليًا لها، أو (ج) تصبح أنت نفسك مترجمًا بشريًا لها. نرى هذا يبدأ بالفعل في الحدوث في مثال Python، حيث نقوم فعليًا بمحاكاة الكود الذي سينشئه المترجم لتنفيذ متغير معجمي.

هذه الممارسة ليست شائعة فحسب، بل مؤسسية. على سبيل المثال، في عالم OO تسمع الكثير عن "الأنماط". أتساءل ما إذا كانت هذه الأنماط ليست أحيانًا دليلًا على الحالة (ج)، المترجم البشري، قيد العمل. عندما أرى أنماطًا في برامجي، أعتبرها علامة على المتاعب. يجب أن يعكس شكل البرنامج فقط المشكلة التي يحتاج إلى حلها. أي انتظام آخر في الكود هو علامة، بالنسبة لي على الأقل، أنني أستخدم تجريدات ليست قوية بما يكفي - غالبًا أنني أقوم بإنشاء توسعات يدويًا لماكرو أحتاج إلى كتابته.

ملاحظات

  • كانت وحدة المعالجة المركزية IBM 704 بحجم ثلاجة تقريبًا، ولكنها أثقل بكثير. بلغت وحدة المعالجة المركزية 3150 رطلاً، و 4K من ذاكرة الوصول العشوائي كانت في صندوق منفصل يزن 4000 رطلاً أخرى. تزن Sub-Zero 690، وهي واحدة من أكبر الثلاجات المنزلية، 656 رطلاً.

  • كتب ستيف راسل أيضًا أول لعبة كمبيوتر (رقمية)، Spacewar، في عام 1962.

  • إذا كنت تريد خداع رئيس ذي شعر مدبب لجعلك تكتب برامج بلغة Lisp، يمكنك محاولة إخباره أنها XML.

  • إليك مولد المجمع في لهجات Lisp الأخرى: Scheme: (define (foo n) (lambda (i) (set! n (+ n i)) n)) Goo: (df foo (n) (op incf n _))) Arc: (def foo (n) [++ n _])

  • قصة إران جات الحزينة عن "أفضل ممارسات الصناعة" في JPL ألهمتني لمعالجة هذه العبارة التي يساء استخدامها بشكل عام.

  • وجد بيتر نورفيج أن 16 من أصل 23 نمطًا في Design Patterns كانت "غير مرئية أو أبسط" في Lisp.

  • شكرًا للعديد من الأشخاص الذين أجابوا على أسئلتي حول لغات مختلفة و/أو قرأوا مسودات هذا، بما في ذلك كين أندرسون، تريفور بلاكويل، إران جات، دان غيفين، سارة هيرلين، جيريمي هيلتون، روبرت موريس، بيتر نورفيج، جاي ستيل، وأنطون فان ستراتن. لا يتحملون أي لوم على أي آراء معبر عنها.

ذات صلة:

استجاب العديد من الأشخاص لهذا الحديث، لذلك قمت بإعداد صفحة إضافية للتعامل مع القضايا التي أثاروها: Re: Revenge of the Nerds.

كما أطلق نقاشًا مكثفًا ومفيدًا غالبًا على قائمة البريد LL1. انظر بشكل خاص البريد من أنطون فان ستراتن حول الضغط الدلالي.

أدى بعض البريد على LL1 إلى محاولة التعمق في موضوع قوة اللغة في Succinctness is Power.

تم تجميع مجموعة أكبر من التنفيذات القياسية لـ مولد المجمع القياسي في صفحتها الخاصة.

ترجمة يابانية، ترجمة إسبانية، ترجمة صينية