لماذا ما زالت Lisp مهمة بعد ستة عقود
لغة Lisp تجاوز عمرها 65 عامًا وما زالت تؤثر في تصميم لغات البرمجة الحديثة. ما الذي يجعلها تصمد، وماذا يمكن للمطورين اليوم أن يتعلموا منها؟

كل بضع سنوات، يكتب أحدهم مقالًا بعنوان «Lisp ماتت»، وكل بضع سنوات يأتي آخر ليشير إلى أن نصف الميزات في لغتك الحديثة المفضلة مسروقة من Lisp. جمع القمامة (Garbage Collection)، والدوال من الدرجة الأولى، والإغلاقات (Closures)، والأنواع الديناميكية، وHomoiconicity، والتطوير المدفوع بـ REPL، والماكرو، كلها ابتُكرت أو انتشرت أولًا في Lisp قبل أن يولد معظم مطوري اليوم. Lisp هي من المشاريع التي تحتاج عقودًا حتى يُقدَّر حقها كاملًا.
ومع ذلك... اسأل أي مطور يعمل في الميدان إن كان قد استخدم Lisp، وسيقول معظمهم لا. واسألهم إن كانوا سيبدؤون مشروعًا إنتاجيًا بلغة Lisp، وسينظرون إليك بدهشة. هنا مفارقة: أفكار Lisp غزت عالم البرمجة، لكن Lisp نفسها ما زالت لغة متخصصة. فهم السبب أكثر إثارة للاهتمام من طروحات «Lisp جيدة والبقية سيئة» التي تجدها في معظم الدفاع عن Lisp.
ما الذي أحسنت Lisp فعله فعلًا
ابتكر جون مكارثي لغة Lisp عام 1958. لنضع ذلك في سياقه: كان عمر FORTRAN عامًا واحدًا فقط، ولم تكن COBOL موجودة بعد. ولم يُطلق الحاسوب المصغر PDP-1 إلا بعد عامين. صُمّمت Lisp على الورق ونُفّذت على الحاسوب IBM 704، وهو آلة تملأ غرفة كاملة وتعمل بكلمات من 36 بت.
ورغم ذلك، اتخذ مكارثي قرارات تصميمية ما زالت ذات صلة بعد ستة عقود. أبرزها:
الكود كبيانات (Homoiconicity)
في معظم اللغات، الكود والبيانات شيئان مختلفان جوهريًا. تكتب الكود بصياغة معينة، وهو يعالج هياكل البيانات. أما في Lisp فالكود هو بيانات. برنامج Lisp مبني من قوائم. التعبير (+ 1 2) هو في الوقت نفسه استدعاء دالة تجمع 1 و2، وقائمة تحتوي على ثلاثة عناصر: الرمز +، والعدد 1، والعدد 2. يمكنك التعامل مع هذه القائمة، بإعادة ترتيبها أو إضافة عناصر إليها أو تحويلها، ثم تنفيذ الناتج.
;; This is a function call
(+ 1 2) ; => 3
;; This is a list containing the same elements
'(+ 1 2) ; => (+ 1 2)
;; You can build code as data and then evaluate it
(def my-expr '(+ 1 2))
(eval my-expr) ; => 3
;; Or transform it
(def doubled (list '* 2 my-expr))
;; doubled is now (* 2 (+ 1 2))
(eval doubled) ; => 6
يبدو هذا كطرافة بسيطة، حتى تدرك ما يتيحه: برامج تكتب برامج. ماكروات Lisp لا تقوم بالاستبدال النصي مثل ماكروات معالج C المسبق. بل تستقبل بنية البرنامج الفعلية كبيانات، وتحوّلها باستخدام كامل قدرات اللغة، ثم تعيد بنية برنامج جديدة. هذا توليد للكود وقت الترجمة، ومترجم اللغة نفسه يصبح بمثابة واجهة برمجية لك.
الـ REPL كبيئة تطوير
ابتكرت Lisp حلقة Read-Eval-Print (REPL)، وهي الفكرة القائلة إنه ينبغي أن تكتب تعبيرًا فيُنفَّذ فورًا وترى نتيجته. يبدو هذا عاديًا في زمن صارت فيه كل لغة تملك REPL، لكن REPL في Lisp يذهب أبعد من معظم الأمثلة.
في Common Lisp أو Clojure، لا تكتفي باختبار تعبيرات معزولة في REPL، بل تطور أنظمة كاملة بشكل تفاعلي. تعرّف دالة، تختبرها، تعيد تعريفها، كل ذلك والبرنامج يعمل. يمكنك فحص الحالة الجارية وتعديلها، وإعادة ترجمة دالة واحدة دون إعادة تشغيل التطبيق. دورة التطوير ليست كتابة-ترجمة-تشغيل-تصحيح، بل حوار مستمر مع نظام حي.
مطورو Clojure بوجه خاص يعملون غالبًا بهذه الطريقة: يربطون محررهم بـ REPL يعمل، ويكتبون الكود في المحرر، وينفذون تعبيرات منفردة بضغطة مفتاح، ويرون النتائج فورًا. حلقة التغذية الراجعة تُقاس بالمللي ثانية لا بالدقائق. وبعد أن تعمل بهذه الطريقة، تبدو دورة التحرير-الترجمة-إعادة-التشغيل التقليدية أشبه بالتواصل عبر البريد.
صياغة بسيطة، ومرونة قصوى
صياغة Lisp، أو غيابها، هي أكثر سماتها إثارةً للجدل. ليس فيها صيغ خاصة لجمل if أو حلقات for أو تعريف الفئات. كل شيء قائمة يأتي المُعامِل في أولها: (if condition then-expr else-expr)، و(defn name [args] body)، و(for [x (range 10)] (* x x)). الأقواس التي ينفر منها المبتدئون هي ثمن الدخول إلى أكثر لغة منتظمة نحويًا صُممت على الإطلاق.
لهذا الانتظام فائدة عملية هي الأدوات. عندما يتبع كل بناء النمط نفسه (operator operands...)، تستطيع المحررات أن توازن المسافات البادئة وتعيد الهيكلة وتتنقل في الكود بنيويًا بثقة. «حدد التعبير المحيط» أمر واضح في Lisp، فهو دائمًا الأقواس المتطابقة. أما في اللغات ذات الصياغات المتنوعة، فتحرير الكود البنيوي مشكلة لم تُحل بعد. في Lisp، حُلّت منذ الستينيات.
لماذا لم تنتصر Lisp
إذا كانت Lisp بهذه الجودة، فلماذا لا يستخدمها الجميع؟ إجابة مدافعي Lisp المعتادة هي «الصناعة مخطئة»، وهي إجابة غير مفيدة وغير صحيحة في أغلبها. لم تحقق Lisp انتشارًا واسعًا لأسباب حقيقية وغير تافهة.
- فجوة النظام البيئي. المكتبات تهم أكثر من ميزات اللغة في معظم الأعمال العملية. Python ليست شائعة لأن صياغتها جميلة، بل لأن
pip installيمنحك وصولًا فوريًا إلى آلاف الحزم المُصانة جيدًا في علم البيانات وتطوير الويب وتعلم الآلة وكل شيء آخر. أنظمة Lisp البيئية (Common Lisp وScheme وClojure) متينة لكنها أصغر. ستقضي وقتًا أطول في كتابة أشياء من الصفر أو في تكييف مكتبات أقل صيانة. - منحنى التعلم يتركز في البداية. صياغة Lisp ذات الأقواس بسيطة تمامًا بمجرد استيعابها، لكنها حاجز حقيقي أمام المبتدئين. والأهم أن كتابة Lisp بأسلوب اصطلاحي تتطلب تفكيرًا غير مألوف لمن جاء من لغات إجرائية أو كائنية. المكافأة تأتي لاحقًا، لكن كثيرين يتركون قبل الوصول إليها.
- التشتت. «Lisp» ليست لغة واحدة بل عائلة: Common Lisp وScheme وRacket وClojure وEmacs Lisp وHy وJanet، ولكل منها نقاط قوة وأنظمة بيئية ومجتمعات مختلفة. هذا التشتت يمنع تركيز الجهد الذي تنمو به الأنظمة البيئية.
- الدعم المؤسسي مهم. Java كان له Sun، وC# كان له Microsoft، وGo كان له Google، وPython استفاد من Google ومجتمع ضخم في علم البيانات. وأكثر لغات عائلة Lisp نجاحًا في الإنتاج، أي Clojure، اكتسبت زخمها جزئيًا لأن Rich Hickey مفكر ومتحدث بالغ الوضوح، لكنها ما زالت تفتقر إلى الدفعة المؤسسية التي تقود الانتشار السائد.
Clojure: لغة Lisp تعلمت من التاريخ
تستحق Clojure اهتمامًا خاصًا لأنها تُظهر كيف يمكن أخذ نقاط قوة Lisp وجعلها عملية لتطوير البرمجيات الحديثة. صمم Rich Hickey لغة Clojure عام 2007 وهو يدرك تمامًا أسباب عدم انتشار Lisp السابقة، واتخذ مفاضلات متعمدة.
- التشغيل على JVM. بدلًا من بناء نظام بيئي منفصل من الصفر، تعمل Clojure على Java Virtual Machine وتستطيع استخدام أي مكتبة Java مباشرة. تحتاج مشغل قاعدة بيانات؟ استخدم نسخة Java. تحتاج عميل HTTP؟ استخدم نسخة Java. هذا القرار وحده منح Clojure الوصول إلى آلاف المكتبات المختبرة منذ اليوم الأول.
- عدم القابلية للتغيير افتراضيًا. بنيات البيانات الأساسية في Clojure، أي القوائم والمتجهات والخرائط والمجموعات، غير قابلة للتعديل. لا تعدّل الخريطة، بل تنشئ واحدة جديدة مع تغييراتك. هذا يلغي فئات كاملة من أخطاء التزامن ويجعل البرامج أسهل في الاستدلال. والبنى الدائمة (Persistent Data Structures) تحت الغطاء تتشارك الهياكل لتبقى هذه العملية فعالة.
- أدوات تزامن عملية. Atoms وRefs وAgents: توفر Clojure نماذج تزامن متعددة، لكل منها نمط يناسبه. لم تكن هذه أناقة نظرية، بل كانت خبرة Hickey المباشرة بمعاناة الحالة المشتركة القابلة للتغيير في تطبيقات Java الإنتاجية.
- ClojureScript. تُترجم Clojure إلى JavaScript، فتحصل على الوصول إلى منظومتي المتصفح وNode.js. اكتب الخلفية بـ Clojure والواجهة بـ ClojureScript، وشارك الكود بينهما. هذا هو الوعد نفسه الذي تقدمه TypeScript أو Kotlin Multiplatform، لكنه جاء أبكر.
ما الذي استعارته اللغات الحديثة
حتى لو لم تكتب سطرًا واحدًا بلغة Lisp، فأنت تستخدم أفكارها يوميًا. تأثيرها منتشر إلى حد أن تتبعه يصبح تمرينًا لرؤية مدى عمقه.
دوال map وfilter وreduce في JavaScript هي أحفاد مباشرون لدوال معالجة القوائم في Lisp، واللغة نفسها مسماة بها (LISt Processing). قوائم الفهم (List Comprehensions) في Python هي سكر نحوي للعمليات نفسها. مطابقة الأنماط (Pattern Matching) في Rust تعود عبر ML إلى تعبيرات cond في Lisp. إغلاقات Swift، وصياغة Lambda في Kotlin، وواجهة Streams في Java، كلها مفاهيم من Lisp بثياب مختلفة.
أنظمة الماكرو في Rust (macro_rules! والماكروات الإجرائية)، وElixir، وNim، وJulia مستوحاة مباشرة من ماكروات Lisp، رغم أن أيًا منها لا يحقق السلاسة نفسها لأنه لا يملك صياغة homoiconic. عندما يكون الكود بيانات، تصبح الماكروات بسيطة. وعندما تكون صياغة الكود معقدة، تتطلب الماكروات تحليل تلك الصياغة وتوليدها، وهذا يضيف احتكاكًا.
صار التطوير المدفوع بـ REPL هو المعتاد في علم البيانات (دفاتر Jupyter هي عمليًا REPL مع حفظ الحالة)، وأدوات مثل Figwheel وshadow-cljs أدخلت إعادة التحميل الفوري إلى تطوير الواجهات الأمامية، وهي فكرة جاءت مباشرة من تقليد Lisp في التطوير ضد نظام حي.
هل يجب أن تتعلم Lisp؟
الإجابة الصادقة تعتمد على ما تريد أن تحصل عليه منها.
إذا أردت أن تصبح مبرمجًا أفضل بتوسيع طريقة تفكيرك في الكود: نعم، بالتأكيد. تعلّم Lisp، خاصة التفكير في تحويل البيانات والبنى التكرارية والكود كبيانات، سيغيّر طريقتك في حل المشكلات في أي لغة. إنه يشبه تعلم البرمجة الوظيفية: حتى لو لم تستخدم Haskell في الإنتاج، فالمفاهيم تجعلك تكتب Python وJavaScript بشكل أفضل.
إذا أردت بناء برمجيات إنتاجية بلغة من عائلة Lisp، فClojure هي الخيار العملي. لديها نظام بيئي حقيقي، ومجتمع احترافي، وشركات (Nubank وWalmart وCircleCI) تشغّلها على نطاق واسع. Common Lisp قابلة للاستخدام لكنها متخصصة، وستقضي وقتًا أطول في البنية التحتية وأقل في مشكلتك الفعلية. أما Scheme وRacket فممتازتان للتعليم وبحوث اللغات، لكنهما أقل عملية للتطوير متعدد الأغراض.
إذا كنت فضوليًا لكنك لست مستعدًا للالتزام، فاقرأ شيئًا من كود Clojure. ليس دروسًا تعليمية، بل كودًا إنتاجيًا حقيقيًا. انظر كيف يركّب مطورو Clojure دوالًا صغيرة، وكيف يستخدمون ماكروات السلسلة (-> و->>>) لبناء مسارات البيانات، وكيف يمثّلون المجالات بخرائط بسيطة بدلًا من الفئات. ستجد أسلوبًا برمجيًا أكثر إيجازًا وقابلية للتركيب، ويركز على تدفق البيانات أكثر مما تجده في قواعد الكود الكائنية السائدة.
بقاء Lisp ليس حنينًا إلى الماضي. إنه نتيجة إصابة عدد قليل من الأشياء الأساسية بشكل صحيح إلى درجة أن ستة عقود من تصميم اللغات لم تتمكن من تحسينها. الأقواس غريبة الشكل، والنظام البيئي أصغر مما تتمنى. لكن الأفكار خالدة، وإن قضيت وقتًا معها فستجد نفسك تكتب كودًا أفضل في أي لغة تستخدمها يوميًا.


