لقد كان عام 1995 عامًا استثنائيًا في عالم البرمجة الحاسوبية، حيث تم إطلاق أربع لغات برمجية جديدة أثّرت على المجتمعِ البرمجيِّ العالميِ بشكلٍ لمْ يكن مُتوقعًا في ذلك الوقت. وكان قد بدأ نموذجُ الشبكةِ العنكبوتيةِ ينتشرُ ويلقى اهتمامًا من العالم، كما كانت شبكة الإنترنت على وشكِ تحطيمِ الثقافة الرقمية السّائدة حينها.
إنّ اللّغاتِ الأربعُ التي أُطلقت لأوّل مرة عام 1995، والتي لم يُحدِث إطلاقها صدى قويًّا في المُجتمع البرمجيّ، هي: جافا Java، وجافا سكريبت JavaScript ، وروبي Ruby، وPHP، إلّا أنّها أصبحت، في النهاية، أشهر الأدوات البرمجية المنتشرة بين المطورين.
حتى ذلك الوقت كانت كُلّ من اللّغتين C وC++ مُسيطرتان في عالَم لُغات البرمجة، وعلى الرغم من قوتهما إلا أنّهما لمْ تكُنا لغاتٍ مناسبةً للشبكة العنكبوتية، بالإضافة لصعوبتهما وتعقيدهما بالنسبة للمبرمجين الجُدُد.
ومن بين اللّغات الأربع، نجحت لغة جافا نجاحًا باهرًا، مع شعارها المشهور “اكتب مرّة، نفّذ في كل مكان”، وحقّقت ضربةً مُفاجئةً بسبب سهولة تعلُّمها وإتقانها مُقارنةً مع لغاتٍ أُخرى مثل C++، وتقديمها لفكرة الآلة الافتراضية (JVM Java Virtual Machine:)، التي مكّنت من كتابة برامج تعمل على مُختلف المنصّات دون الحاجةِ إلى إعادة ترجمتها للغاتٍ ذات مستوى منخفض توافق المنصّة التي تعمل عليها.
أمّا لغة جافا سكريبت فقد استطاعت في السنوات الأخيرة أن تحلّ محل جافا كأعلى اللغات البرمجية قيمةً في العالم، وخاصةً بعد طرح المنصة البرمجية Node.js التي نقلت جافا سكريبت من عالم المتصفّحات إلى عالم الخوادم الحاسوبية (Servers).
وأصبحت لغة PHP من اللّغات المُسيطرة في عالم برمجة الويب، وخاصةً عند جمعها مع التقنيات مفتوحة المصدر مثل نظام Linux، والخادم المشهور Apache، ونظام إدارة قواعد البيانات MySQL حيث تشكّل معًا ما يعرف بالتركيب LAMP.
كما اشتهرت لغة Ruby بين المبرمجين بعد إطلاق المنصة البرمجية الشهيرة Ruby on Rails، عام 2005، من قبل المبرمج الدنماركي ديفيد هانسون (David Hansson).
الحاجةُ إلى لغات برمجة جديدة
مع بداية عام 2000، بدأ المشهد البرمجي بالتغيّر، حيث امتلكت الآلاتُ الحاسوبيةُ عدّة مُعالجاتٍ، حتى أن المعالجات نفسها أصبحت تمتلكُ عدّة نوى.
تَطَلَّبَت هذه القفزة في عالم العتاد الصلب لغاتٍ برمجيةً تستفيدُ من الهيكليّة الجديدةِ للمُعالجات، لغاتٍ قادرةً على التعامُلِ مع مجموعة عمليات (Processes) في الوقت ذاته، سواء بشكل متزامن (Concurrent)، أو بشكل تفرعيّ (Parallel)، لتحقيق أكبر استفادة من إمكانيةِ المُعالجاتِ متعددةِ النوى الجديدة.
وقد ازدادَ الاهتمامُ، مؤخّرًا، بنموذجِ البرمجةِ الوظيفيةِ (Functional Programming)، وهو عبارة عن نموذجٍ يحاول التخلّص من الآثار الجانبية (تأثير التابع هو عبارة عن قيمته فقط، على عكس البرمجة غرضية التوجّه)، وتعتبر الآثارُ الجانبية، حاليًا، شيئًا سلبيًّا جدًا في عالم البرمجة، وتجعل عملية تنقيح الشيفرة البرمجية (Debugging) عمليةً صعبةً جدًا. بالإضافة إلى أنّ البيئاتِ البرمجيةِ التزامنية تتطلّب الحفاظَ على البيانات المشتركة، وعدم إفسادها وهنا تأتي أهمية هذا النموذج.
ضِفْ إلى ذلك، تطوّرت الآلات الحديثة بشكلٍ كبيرٍ جدًا وأصبحت ذات قدرةٍ هائلةٍ على المُعالجةِ، خلافًا لما كان يحدثُ في الماضي، حيث التركيز على تسريع البرنامج وتطويره فقط. كما اتّسعت دائرةُ الاهتمام الآن لتشمل قدرة المبرمجين أنفسهم وإنتاجيّتهم، وهذا ما تطلّب لغاتٍ برمجيةٍ ذات تركيبٍ نَحْويٍّ (Syntax) أنيق وسهل القراءة والكتابة، حيث يُمكن للمبرمج تعلّم اللغة بأقصر مدّة زمنية ممكنة.
تعليمُ كلبٍ قديمٍ حيلًا جديدةً
(هذه صيغةٌ للتعبير عن تجديد لغات البرمجة القديمة)
ضمانًا للبقاءِ في مشهد البرمجة المتجدّدة، بُذِلَت الكثير من الجهود لتطوير لغات قديمة متل Java وC++.
على سبيل المثال، في Java8 (النسخة الثامنة من لغة جافا) تم إضافة “تعبيرات اللّامدا” (Lambda Expressions)، وواجهاتٍ برمجيةٍ لإنشاء دَفْق (Streams API) وهي عبارة عن بُنى معطياتٍ (بيانات) كسولة تُبنى عند الطلب، وتُستخدم في البرمجة التفرعيّة (Parallel Programming). أمّا في Java9 تم إضافة تقنية اقرأ- قيّم- اطبع- كرّر (REPL: Read-Eval-Print-Loop).
تم إضافة النياسب (Threads) أيضًا إلى لغات البرمجة القديمة؛ لِمُعالجة العمليات بشكل متزامنٍ، إلّا أنّ العائق في هذه اللغات يعود لكونها لم تُبنى في الأساس من أجل حلّ المشكلات المطروحة في عالم البرمجة الجديد، كما لم تُثبِت محاولاتُ التحديث، التي أُجريت عليها، فاعليَّتَها، ونجدُ في ظلِّ هذه الظروف أنه لا خيار لنا سوى ابتكار لغات برمجية تواكبُ العصر، وتندمج مع مفاهيم هندسة البرمجيات الحديثة.
الحاجة أمّ الاختراع
نتيجة للعواملِ التي ذكرناها آنفًا، تمّ اختراعُ مجموعةٍ من لُغات البرمجة بغيةَ حلّ بعض هذه المشاكل، إنْ لمْ يكن كلّها. وسنتّفقُ على وصف هذه اللغاتِ بالحديثة، حيث أنها اختُرعت في القرن 21، وكما ستكتشف لاحقًا، فإنّ جميع هذه اللغات تحوي على مفاهيم مشتركة، والتّركيب النحويّ لبعضها متشابه بشكل كبير.
بعض الميزات المشتركة بين هذه اللغات:
- من المرغوب به أن تكون المتحولات غير قابلة للتغيير (Immutable).
- وجود نمط المعطيات: واجهة (Interface).
- تتحقّق معظمها من سلامة الأنماط (Type Safety).
- يأتي نمط القيم التي تردّها التوابع(الدّوال)، في معظم هذه اللغات، في نهاية ترويسة التابع (Trailing Return Type).
- تقدم بعضها طرق سهلة لتوليد (Spawning) عمليات لتعمل بشكل متزامن.
- يقدم بعضها طرقًا سهلةً من أجل التواصل الداخليّ بين العمليات (IPC) عن طريق ما يُسّمى بالقنوات (Channels).
- تؤكد الكثير منها على النمط الوظيفي في البرمجة.
- تتطلب الكثير منها لا فاصلة منقوطة في نهاية الجملة البرمجيّة.
- تمتلك العديد منها تقنية REPL التي تكلمنا عنها سابقًا.
- تتصف بأنّها ذات تركيب نحوي جميل وواضح.
لغات البرمجة الحديثة الشائعة
سنسردُ لكم فيما يلي أشهر لغات البرمجة الحديثة، كما سنستعرض الميزات الأساسية لكل منها، وكيف طَبقت كُلّ لغة منها مفهوم التزامن (Concurrency)، وهو ميزة مشتركة بينها جميعها.Scala، Go، وRust، وKotlin، وSwift.
Scala
ظهرت هذه اللّغة لأول مرة في العقد الأول من القرن الحالي، وتُعدّ واحدةً من أقدم لغات البرمجة الحديثة، وهي منتج أكاديمي اخترعها مارتن أوديرسكي (Martin Odersky) أثناء عمله في مدرسة لوزان الاتّحاديّة للفنون التطبيقية (EPFL)، في سويسرا، حيث أُطلِقت أول نسخة منها عام 2004.
تجمع لغة Scala ما بين نموذج البرمجة غرضية التوجه (OOP) ونموذج البرمجة الوظيفية. كما تُنفّذ التطبيقات والبرامج التي تكتب بلغة Scala بواسطة ال JVM، وبالتالي هي مستقلة عن المنصة التي تعمل عليها.
مثالٌ بسيط عن تابع (دالّة) معرف بلغة Scala:
يبدأ التابع بلغة Scala بالكلمة المفتاحية def (مثل اللغتين Python وRuby)، وبعدها يأتي اسم التابع (في المثال السابق هو factorial)، ومن ثمّ مجموعة من المعاملات المحصورة بين قوسين (في مثالنا هنالك المعامل x، ويتبعه المحرف “:“، ومن ثم نمط هذا المعامل Int أي عدد صحيح)، وفي حال وجود أكثر من معامل نفصل بينها باستخدام الفاصلة “,“. ومن ثم نمط القيمة التي يعيدها التابع (هي Int في المثال)، يليه إشارة المساواة “=” وبعدها جسم التابع محصور بين القوسين {}.
قد يرى البعض أن إشارة المساواة “=” غريبة بعض الشيء، ولكن قد تكون ذات معنى كبير من وجهة نظر رياضيّة، إذ يمكن اعتبار محتوى القوسين {} تعبيرًا رياضيًّا يردّ قيمًا مختلفة بناءً على الدخل أو المعاملات.
أما بالحديث عن المتحولات في لغة Scala، يتم تعريفها باستخدام إحدى الكلمتين المفتاحيتين val أو var. فالمتحولات المسبوقة بكلمة val تكون غير قابلةٍ للتغيير (ثابتةً)، أي لا يمكن إسناد قيم جديدةٍ إليها، أما المتحولات المسبوقة بكلمة var تكون قابلةً للتغيير. وفي حال أُعطي المتحول قيمة ابتدائية، حينها لا داعٍ لذكر نمط المتحول، كما في المثال التالي:
val x = 20
وإلا يجب ذكر نمط هذا المتحول:
var x: Int
وفيما يخصّ التزامن في لغة Scala، فإنّها تستخدم ما يسمى بالممثلين أو الفاعلين (Actors)، اعتمادًا على نموذج الممثل أو الفاعل (Actor Model)، الذي اقترحه كارل هيويت (Carl Hewitt) في بداية السبعينات، لتطبيق مفهوم التزامن. وقد طبقت Scala هذا النموذج متأثرةً بشكل كبير بلغةِ Erlang.
ولدينا المثال التالي:
عند تنفيذ البرنامج أعلاه سيكون الخرج على الشكل التالي:
This is an integer: 32
I don’t know what this is.
السطرين الأول والثاني هما استيراد لمكتبة Scala.actors لاستخدامها في البرنامج. أما السطر الثالث فيبدأ بكلمة val وهو تعريف لممثل اسمه simpleActor وهو قادر على استقبال رسائل اعتمادًا على الحالات المُعبَّر عنها ضمن قسم الاستقبال (receive). إنّ الممثل أو الفاعل هو عبارة عن كائن (Object) يشبه النيسب (Thread) يستقبل ويخزن الرسائل، وعادةً يتم إسناده إلى نيسب، وبالتالي تتنفذ بشكل متزامن. ويتواصل الممثلون عن طريق رسائل، ولإرسال رسالة إلى ممثل ما يجب استخدام إشارة الاستفهام “!“، كما في المثال التالي:
في هذه الحالة، ترسَلُ الرسالةُ النصية (scala is awesome) إلى الممثِّل simpleActor. لاحظ ان اسم الممثل هو على يسار إشارة الاستفهام، بينما الرسالة على يمينها. وعندما يستقبل ممثل ما رسالة، يحاول إيجاد حالة مطابقة لنوع الرسالة في قسم الاستقبال، وفي حال لم يجد أي حالة مطابقة يقوم بتنفيذ الحالة الافتراضية المعبَر عنها في المثال السابق على الشكل (case _). كما يملك كٌل ممثل في لغة Scala صندوق بريد (mailbox)، حيث تصطفّ فيه الرسائل الواردة من أجل المعالجة المستقبلية.
من المستخدمين الأوائل للغة Scala كانت شركة Twitter في عام 2009. ومن الكتب الموثوقة لتعلّم لغة Scala هو Programming in Scala لكاتبه مارتن اوديرسكي.
Golang – Go
قامت شركة جوجل بتصميم لغة Go لاستخدامات داخلية، حيث أنّها تتعامل بشكل كبير مع النظم الموزّعة. وهي لغة مثيرة للاهتمام بشكل خاصّ، لأن أحد مصمميها هو البطل الفولكلوري في عالم الحوسبة كين طومسون (Ken Thompson)، الذي أسّس نظام التشغيل يونيكس (Unix) في مطلع سبعينيات القرن الماضي، أثناء عمله في مختبرات بِل في أمريكا.
أُعلن عن لغة Go في عام 2009، ومنذ إطلاقها تعدّ مشروعًا مفتوحَ المصدرِ. وقد أشار المصمّمون إلى أن أحد دوافعهم لتصميمها، هو إحباطاتهم المشتركة مع التعقيد الكامن في C++. كما تعتبر كلغة C في القرن الواحد والعشرين، فهي لغة تُتَرجم، أي لها مترجم (Compiler)، وليس مفسِّر (Interpreter)، وتُدار الذاكرة فيها ديناميكيًا. وأحد نقاط القوة التي تتمتّع بها، هو تعاملها مع التزامن عن طريق القنوات (Channels)، ووظائف Go (goroutines).
مثال بسيط عن تابع معرف بلغة Go:
يتم التصريح عن التابع في لغة Go بالكلمة المفتاحية func، وبعدها يأتي اسم التابع (في المثال السابق هو factorial)، ومن ثم مجموعة من المعاملات المحصورة بين قوسين (في مثالنا هنالك المعامل x من نمط Int)، وفي حال وجود أكثر من معامل نفصل بينها باستخدام فاصلة”,”. ومن ثم نمط القيمة التي يعيدها التابع (وهي في المثال Int) و تسمى Trailing return type، كما أوضحنا سابقًا لأنها تأتي في نهاية ترويسة التابع كما في لغة Scala، وبعدها جسم التابع محصور بين القوسين{}.
يتم التصريح عن المتحولات في لغة Go بالكلمة المفتاحية var، كما في المثال التالي:
s = “Welcome to Go”
طبعًا من الممكن أن يكون التصريح والتهيئة في جملة واحدة:
فيما يخصّ التزامن في لغة Golang، كما ذكرنا سابقًا، التزامن هو أحد المناطق التي تشرق بها Go، حيث تستخدم القنوات ووظائف Go (goroutines) لتطبيقه، كما في المثال التالي:
يتم التصريح عن وظيفة Go (goroutine) بالكلمة المفتاحية go، ووظائف Go هي عبارة عن كيانات تشبه النياسب (Threads)؛ تُنفذ بشكل متزامن، وتتواصل مع بعضها البعض عن طريق القنوات.
تُعرّف القناة بالكلمة المفتاحية chan، يتبعه نمط المعطيات الذي سوف يمرّ عبر هذه القناة، كما في المثال التالي:
في المثال السابق، يُنشئ التابع الضمني make قناة اسمها c1، تتعامل مع حركة معطيات من النمط string.
يستخدم العامل الرياضي (<-)لإرسال واستقبال رسائل عبر القناة:
في هذه الحالة، أُرسِلت الرسالة (hello from channel 1) إلى القناة c1، أما تعليمة الاستقبال تكون على الشكل التالي:
معنى التعليمة السابقة هو، استقبل رسالة من القناة c1 وضع المحتوى في متحول اسمه msg1 في قسم الاختيار (select) في وظيفة go الثالثة يتم اختيار القناة التي تحمل معطيات أولًا، وفي حال وجود أكثر من قناة تحمل معطيات، يتم الاختيار بينها بشكل عشوائي، وفي حال لم تكن أي قناة تحمل معطيات، يتم تنفيذ قسم الحالة الافتراضية إنْ وجدت.
ولمزيد عن لغة Go إليك بهذه المصادر الجيدة لتعلّمها:
Rust
Rust هي لغة برمجية خاصّة للتعامُل مع الأنظمة، أصدرتها Mozilla Research كبديل أكثر أمانًا وفاعليةً من C/C++. أطلقت كمشروع مفتوح المصدر عام 2010، والمصمم الأساسي للّغة هو جرايدون هور (Graydon Hoare)، الذي انتقل فيما بعد لشركة Apple، وأصبح جزءًا من فريق لغة Swift التي سنتكلم عنها لاحقًا.
الأهداف المعلنة للغة Rust هي الأمان، والسرعة، والتزامن. ومن بين هذه الأهداف الثلاثة كانت Rust أكثر حزمًا مع مسألة الأمان عن طريق تطبيقها لمبدأ الملكية (Ownership) وهنا مقالة جيدة تشرح مفهوم الملكية.
مثال بسيط عن تابع معرف بلغة Rust:
ترمز i32 إلى عدد صحيح ممثلًا على 32-bit.
المتحولات بلغة Rust غير قابلة للتعديل بشكل افتراضيّ، ويتمّ التصريح عنها بالكلمة المفتاحية let.
وأيضا بالطريقة التالية:
في هذا المثال سيتم إسناد القيمة 7 إلى المتحول x، والقيمة 13 إلى المتحول y.
وفي الحالات السابقة سيتعرف المترجم على أنماط هذه المتحولات، أمّا إذا أردت أن تذكر النمط بشكل صريح:
وفي حال أردت أن يكون المتحول قابلًا للتعديل:
وبالحديث عن التزامن، بما أنّ لغة Rust خاصة بالأنظمة، فهي تتيح مبدأ التزامن عن طريق النياسب بشكل فطري. ويتم تعريف النيسب على الشكل التالي:
يُستخدم التابع spawn () لتوليد نيسب جديد وهو تابع موجود ضمن المكتبة القياسية للنياسب. (std::thread).
مثل لغة Go، تتواصل النياسب في Rust مع بعضها عن طريق قنوات، كما في المثال التالي:
اولًا نبدأ باستيراد المكتبات القياسية للنياسب (std::thread) والمزامنة (std::sync)
يتم تعريف القناة عن طريق استدعاء التابع channel () الموجود في المكتبة(std::sync::mpsc)كما يلي:
mpsc هي اختصار لِـ multiple producers, single consumer، أي عدّة منتجين ومستهلك واحد.
في المثال السابق، يمكن نسخ (clone) طرف القناة المسؤول عن الإرسال في هذه الحالة هو tx، من قبل عدّة نياسب واستخدامه للإرسال، أما طرف القناة المسؤول عن الاستقبال في هذه الحالة هو rx لا يمكن نسخه.
يقوم النيسب بالإرسال على الشكل التالي:
ومن الجدير بالذكر أنّه وفقًا لـ Stack Overflow Developer Survey، فإنّ لغة Rust هي أكثر لغة محبوبة لعامين على التوالي 2016 و2017. ونقدّم لك فيما يلي مشروعين تم بناؤهما بالكامل بلغة Rust، يمكنك الاطلاع عليهما:
Redox وهو نظام تشغيل صغير النواة (Microkernel)، أي يقدّم الآليات المطلوبة لإنشاء خدمات نظام التشغيل فقط.
Servo وهو محرّك تصميم (Browser Engine)، تم إنشاؤه من قبل Mozilla Research بالتعاون مع شركة Samsung.
يمكن تعلم لغة Rust من خلال الأمثلة التفاعلية هنا.
Kotlin
حتى وقت قريب كانت، لغة Kotlin بالكاد معروفة ضمن المجتمع البرمجي، وسُميت بهذا الاسم نسبةً إلى جزيرة كوتلين بالقرب من سانت بطرسبرغ، صمّمتها شركة Jet Brains الشهيرة والمسؤولة عن منتجات مثل IntelliJ IDEA وPhpStorm وPyCharm.
إنّ لغة Kotlin مثل لغة Scala؛ يتم تنفيذ البرامج المكتوبة فيها بواسطة ال JVM. وقد تلقت دفعة كبيرة عام 2017 أثناء مؤتمر Google I/O، حيثُ أُعلِنَ أنّها ستلقى دعمًا من الدرجة الأولى للعمل على نظام التشغيل Android.
كمثال على التصريح عن تابع بلغة Kotlin:
مثل لغة Scala، يتمّ التصريح عن المتحولات في لغة Kotlin باستخدام الكلمتين المفتاحيتين val وvar
var y = 7
في هذه الحالة x هو متحول غير قابل للتعديل (بمعنى أنّه سيؤدي أي تعديل إلى تشكيل كائن جديد)، أما y فهو متحول قابل للتعديل.
وبخصوص التزامن، تعتبر Kotlin الأقل متانة في موضوع التزامن، مقارنة مع اللغات الثلاثة الأخرى السابقة الذكر، وتستخدم ما يسمى بال coroutines لتطبيقه.
إليكم ببعض المصادر الجيدة لتعلم لغة Kotlin:
- كتاب Kotlin in Action، كتبه اثنين من المصممين الأساسيين للغة Kotlin
- كتاب Kotlin for Android Developers ل أنطونيو ليفا (Antonio Leiva).
Swift
صممتها شركة Apple، وهي لغة بديلة عن لغة Objective C لبناء تطبيقات Apple. لقد أُطلِقت خلال المؤتمر العالمي للمطوّرين (Worldwide Developers Conference) في عام 2014، وبعد عام أُطلِقت كمشروع مفتوح المصدر، وكان قائد فريق التصميم، كريس لاتنر (Chris Lattner) ،أحد المصممين الأساسيين في مشروع LLVM.
يشبه الكود المكتوب بلغة Swift إلى حد كبير الكود المكتوب بلغة Rust. وإليكم بمثال على التصريح عن تابع بلغة Swift:
يتم التصريح عن الثوابت باستخدام المفتاحية let، أما المتحولات باستخدام الكلمة المفتاحية var:
var x = 10
أما بالنسبة للتزامن، فللأسف كانت Swift بطيئة جدًا في اعتماد تقنيات تحقيق التزامن على مستوى اللغة، ومع ذلك، هنالك تقدمٌ ملحوظٌ بدأ مع النسخة الثالثة من اللغة (Swift3).
ومن أهم المصادر لتعلم لغة Swift، هو الكتيّب الذي تتيحه شركة Apple تحت اسم The Swift Programming Language.
اقرأ أيضا:
- البرمجة و الحصول على عمل.. من أين أبدأ؟
- تعرف على أغلى وظيفة في العالم، وعلى كيفية الوصول إليها!
- 10 مفاهيم في الجافا سكريبت عليك معرفتها قبل مقابلة العمل
- 10 كنوز برمجية مجانية عليك قراءتها قبل انتهاء 2018
- إعداد: علي صقر علي.
- مراجعة: نور عبدو.
ماهي احدث ثلاث لغات برمجة في العالم