البحث في الموقع
المحتوى عن 'مدخل إلى لغة البرمجة go'.
-
لعلّك سمعت في السنوات القليلة الماضية عن لغة برمجة جديدة نشأت من داخل شركة Google تحمل اسم Go (أو Golang كمصطلح قابل للبحث على محركات البحث)، سنحاول من خلال هذه السلسلة التعرّف على هذه اللغة، مزاياها، عيوبها وما يجعلها مختلفة عن غيرها. سيكون الدرس الأول من هذه السلسلة درسًا كلاميا فقط، يركز على نقاط اختلاف اللغة مع باقي اللغات، وهو موجه لمن له خلفية برمجية نوعا ما مع باقي اللغات، لكن ستكون باقي الدروس موجهة للمبتدئين. كعادة كل عقد من الزمن، فإنه تظهر هناك أدوات وتقنيات جديدة تحاول الاستفادة من أخطاء أسلافها من الأدوات والتقنيات، هذا يشمل لغات البرمجة أيضا، لذلك دعك من قول "لماذا لغة برمجة جديدة؟" و "ما الحاجة للغة برمجة جديدة؟" أو "لماذا لا يتفقون على لغة برمجة واحدة؟"، فما لغة البرمجة إلا أداة تقضي بها مهامك، وكل منها يكون أصلح لنوع مهام ما. فاختر لكل مهمة أنسب أداة، ولا داعي لتقديس الأداة وإنشاء الحروب الكلامية -غير التقنية- حولها أو خلق هاجس وهمي يحول عن تعلمها. أَحَسَّ فريقٌ من المبرمجين (العريقين) من داخل Google أنه حان الوقت لتحسين سير عملهم بلغة C و ++C، وأنهم يحتاجون إلى أداة جديدة تلغي عيوب هاتين الأخيرين وتحسن من إنتاجيتهم أكثر، وتكون ملائمة لنوعية احتياجات Google الحسابية. أسسوا لغة البرمجة Go داخل Google عام 2007. الفريق العريق المؤسس لهذه اللغة يشمل مؤسس نظام Unix نفسه Ken Thompson (عَلَمٌ في عِلم الحاسوب، يُعرف اختصارًا بـ Ken) الذي عمل لدى Bell Labs، كذلك Robert Pike الذي كان أيضا من ضمن فريق Unix لدى Bell Labs وأحد مؤسسي ترميز UTF-8 وأخيرًا Robert Griesemer أحد العاملين على محرك جافاسكريب V8. Ken على يسار الصورة بجانب Dennis Ritchie مؤسس لغة C بمعرفة هذه النبذة التاريخية البسيطة عن Go، أحب أن أقتل فيك كل حماس مُفرط، فلا أحب منك أن تعشق لغة برمجة بسبب مؤسيسها، أو أنها خرجت من شركة تحبها، لذلك سأبدأ بسرد أشياء قد لا تحبها حول لغة البرمجة Go، حتى إذا قرأتها ولم تعجبك، فقد يجدر بك التوقف عن القراءة حينها. سأكون سعيدا شخصيا بمتابعة عدد قليل جدا من القراء لهذه السلسلة يدركون حقا ما يفعلون، عوض زخم يتحمس لكل جديد دون وعي وإلمام بالأمور. أشياء قد لن تحبها حول لغة البرمجة Go1. ليست كائنية التوجههذا إن كنت تظن أنها كذلك، فلا يوجد مفهوم الصنف (Class) ولا الكائنات (Objects) وبالتالي لا يوجد وراثة (inheritance). مع ذلك فإن Go تحمل بضعا من مزايا البرمجة الكائنية كتوفيرها لـ Interface و دوال البنيات (Struct methods) وتركيب البنيات (Struct composition). لماذا؟ مؤسسي اللغة يرون أن اللغات كائنية التوجه تحمل العديد من العيوب والتعقيدات التي يمكن إلغاؤها بالتخلي عن بعض هذه المفاهيم كلية. حتى مبرمجي Java أنفسهم ينصحون بـ Composition مثلا في كثير من الأحيان عوض inheritance، مؤسسي اللغة ممن يرون أن البرمجة كائنية التوجه غالبا ما تكون فكرة سيئة -تقنيا-. 2. لا توجد معالجة للاستثناءات Exception Handlingقد تكون هذه نتيجة طبيعية بسبب غياب مفهوم البرمجة الكائنية لدى Go، فمعالجة الأخطاء في Go تتم بطريقة واضحة وتقليدية نوعا ما، حيث أن الأخطاء تُرجع كقيم عادية من نوع error. حيث error هو نوع بدائي في حد ذاته مثله مثل أي نوع أصلي آخر (int, string .. الخ). مع ذلك تسمح لك Go بقذف خطأ للحالات الاستثنائية عبر الكلمة المفتاحية panic (أشبه بـ raise أو throw في باقي اللغات) وكذا التعافي من هذه الأخطاء عن طريق recover. 3. لا توجد معامِلات افتراضية أو اختيارية يمكن تمريرها للدوال (default/optional arguments)ربما قد اعتدت في لغات البرمجة الأخرى على القيام بشيء مثل: function listFolders(path, subfolders=false, recursive=false){ ... } لكن في Go لن يمكنك تمرير subfolder=false ولا recursive=false كإمضاء للدالة listFolders لأنها لن تقبل مثل هذه المعاملات الافتراضية/الاختيارية، وسينتج عن ذلك خطأ عند التجميع (compile error). لماذا؟ يرى مؤسسوا اللغة أن هذه السلوكيات تساهم في بناء واجهات برمجية (API) غير ثابتة أو تساهم في جعل تصرفها غير مُتوقع. في مثالنا السابق مثلا، هم يفضلون كتابة الدالة من دون معاملات افتراضية، أي: func listFolders(path string, subfolders bool, recursive bool) { ... }يجبرك هذا على كتابة التصرف الذي تريده من الدالة بشكل صريح عوض ترك الواجهة البرمجية تملي عليك التصرف الافتراضي، هذا لتقليل الأخطاء البشرية. أيضا قد يدفعك هذا إلى كتابة ثلاث دوال، كل بتصرفها الخاص الواضح من اسمها، مثال: listFolders, listFoldersRecursivly و listFoldersWithFirstLevelSubFolders. 4. لا توجد ميزة إثقال الدوال (Method Overloading)لنفس الأسباب السابقة، فإنه لا يوجد Method overloading، أي لا يمكنك إعادة تعريف دالة تحمل نفس الاسم لكن بإمضاء مغاير. مثلا، إذا وُجدت دالة باسم: func listFolders(path string) { ... }فلا يمكنك إنشاء دالة أخرى بنفس الاسم لكن بإمضاء مغاير مثل: func listFolder(path string, level int) { ... }بل عليك تغيير اسمها إلى مثلا: dunc listFolderToLevel(path string, level int) { ... }على الرغم من ذلك فهناك طريقة غير مباشرة لجعل دالة ما تقبل قيما اعتباطية عبر جعل الإمضاء من نوع {}interface سنتطرق إليها في الدروس القادمة. 5. لا وجود للعموميات (generics)تمكّنك باقي اللغات من كتابة دوال أو أصناف عامة، حيث لا تصرّح عند كتابتها بنوع المعاملات التي تقبلها لكن تترك لها مهمة معرفة نوع المعاملات لاحقا عند استدعائها، لعل أقرب مثال هو ما توفره لغة Java مثلا في صنف <List<T حيث T يرمز إلى أي نوع يتم تحديده لاحقا، بالتالي يمكن إنشاء <List<String أو <List<Integer بكل سهولة مع الحفاظ على نفس الوظائف والعمليات التي يمكن إجراؤها على القائمة List بشكل عام. لا يوجد في Go مثل هذا، وعوضا عن ذلك فهناك {}interface كنوع شامل يرضي جميع الأنواع، لكنه ليس كبديل تام لـ Generics. لماذا؟ سبب عدم توفر العموميات (Generics) في Go هو أن مؤسسيها لم يتبيّنوا بعد الطريقة الأنسب لهم لإضافة هذه الميزة إلى اللغة دون زيادة حِمل أثناء وقت التشغيل (run-time). 6. Go لُغة مملة كما أنها ليست أفضل لغة برمجة!لشدة بساطة اللغة وعدم إتيانها بشيء جديد، فإن الكثيرين يعتبرها لغة مملة. فعدد الكلمات المفتاحية بها والأنواع الأصلية فيها ضئيل مقارنة بباقي اللغات، كما أنها تقلل كثيرا من وجود أكثر من طريقة للقيام بمهمة معينة. حتى أنها لا تحتوي على حلقة while وتقتصر فقط على حلقة for، الكثيرون يعتبرون هذا من مزايا اللغة، لكني ذكرتها لك حتى لا تتوقع شيئا جديدا يصلح للتباهي. أيضا لن تسمح لك اللغة بترك متغير دون استعمال أو استيراد شيء غير مستعمل (unused import/variable) ولن يقبل المُجمع (compiler) أبدا بذلك. 7. لغة عنيدةمؤسسوا اللغة متشبثون برأيهم وقراراتهم في تصميم اللغة، فلا تتوقع تغيرات جوهرية قد تحدث على المدى القريب أو المتوسط في اللغة أو تغيرات في طريقة القيام بالأمور وسلوك المُجمّع (compiler). ولا داعي لفتح نقاشات فارغة حول تصميم اللغة وعيوبها إلا إذا كنت في نفس مستوى خبرتهم وحكمتهم. هم نفسهم يصرحون بهذا، ويذكرون أن هناك خيارات ولغات برمجة أخرى إن لم تعجبك Go. 8. لا يوجد إجماع على مدير حزم واحدتملك بايثون pip، وجافاسكريب تملك npm، وغيرهم من اللغات تملك مدير حزم (package manager) شهير أو متفق عليه، لا تخلو Go من مدير حزم، فهي تملك الكثير من ذلك، لكنها لم تتفق بعد على مدير حزم واحد ولا عن كيفية جلب وسرد الاعتماديات بطريقة قياسية، لكن حديثا يتم العمل على ذلك عبر مفهوم Vendoring. هذه الأمور الثمانية، للذكر وليس للحصر من أشد الانتقادات التي توجّه إلى Go كلغة برمجة، فإن كنت توافقها فقد لا تناسبك اللغة، وإن كنت ترى أن من ورائها حكمة -مثلي- فأكمل قراءة المقال حول أمور قد تعجبك حول Go. أشياء قد تحبها حول لغة البرمجة Go1. خيوط المعالجة المتوازية الخفيفة (goroutines)تسمح لك Go بعمل معالجة متوازية والاستفادة من كامل قوة المعالج (CPU) لديك بكل بساطة، حيث تملك ما يسمى بـ goroutine وهو خيط معالجة متوازي أخف من thread في باقي اللغات. يسمح لك هذا بإطلاق عشرات الآلاف من خيوط المعالجة المتوازية (عوض مثلا عشرات أو مئات threads فقط) دون عناء إنشائها وجعلها تتواصل فيما بينها أو تُكمّل بعضها البعض. يكفي مثلا لعمل دالة تعمل في خيط معالجة لوحدها بسبقها بالكلمة المفتاحية go، مثلا: go func(){ // دالة نكرة تقوم بعمل أمور في خيط معالجة لوحدها .... }هذا يسمح بكتابة تطبيقات عالية الأداء بشكل أسهل بكثير. 2. لغة تجميعية سريعة (compiled)عكس Ruby, Python, Java و PHP فإن Go لغة تجميعية، حيث أن الناتج النهائي لبرنامجك سيكون عبارة عن ملف تنفيذي قائم بذاته يحوي جميع الاعتماديات اللازمة لتشغيله دون الحاجة لاعتماديات خارجية، هذا مفيد جدا في تطوير الويب مثلا للتخلص أو تقليص مشاكل الاعتمادية (dependency hell). أيضا تساهم هذه الميزة في تسهيل عملية نشر التطبيق الخاص بك (deployment) حيث سيكون عليك فقط نقل الملف التنفيذي إلى الخادوم مثلا ثم تشغيله وكفى. لا حاجة لتنصيب أي شيء آخر لتشغيل برمجيتك، لا حاجة حتى لتنصيب Go على الخادوم نفسه. قد يتبادر إلى الذهن أن عملية التجميع (compiling) قد تأخذ وقتا خاصة إذا كان حجم البرنامج كبيرا، لكن الحقيقة أن عملية تجميع Go سريعة جدا وفي كثير من الأحيان لن تلاحظها حتى. 3. لغة إنتاجيةبسبب أنها لغة مملة، فإنها تضيق عليك مجال المجادلة مع زميلك في العمل أو من يعمل معك على المشروع أو البحث عن أفضل طريقة -مذهلة- للقيام بشيء ما، فكل شيء واضح وبسيط. 4. متعددة المنصات بامتيازنفس البرنامج يمكن إعادة تجميعه ليعمل على ويندوز، لينكس، Mac OSx أو حتى الهواتف الذكية ومعمارية ARM. حتى أنه بعد الإصدارة رقم 1.5 من Go يمكن إنشاء ملف تنفيذي لجميع المنصات من خلال المنصة التي أنت فيها، مثلا يمكن من خلال نظام لينكس إنشاء ملف تنفيذي لنظام ويندوز و Mac OS دون الحاجة ﻷن تكون على ذلك النظام. 5. مكتبة قياسية قوية (Rich Standard Library)على الرغم من حداثة اللغة، فإن مكتبتها القياسية حققت تقدما في ظرف قياسي. وغالبا ما يُنصح بالاعتماد على المكتبة القياسية قدر الإمكان، حيث تحتوي على أمور كثيرة تحتاجها أغلب أنواع التطبيقات، حتى أنها تحتوي على محرك قوالب html، أدوات للتعامل مع ترميز json، تشفير كلمات المرور وغيرها من الأمور الصالحة للاستعمال في تطبيقات الويب على سبيل المثال. 6. كل شيء Unicode افتراضاموضوع ترميز السلاسل النصية وكيفية التعامل مع الحرف رقميا كبير، لكن Go جعلت كل شيء unicode بشكل افتراضي إلا إذا تم تعيين غير ذلك صراحة. تملك Go نوع rune عوض نوع char في باقي اللغات، وهو ببساطة اختصار (alias) لنوع int32 فقط يشير إلى نقطة ترميز Unicode لذلك الحرف. بالتي فإن string في Go عبارة عن سلسلة rune وليس عن سلسلة char يشير إلى byte كما في C مثلا. 7. أدوات مساعدة جيدة مضمنة يمكن توليد توثيق برمجيتك بمجرد كتابة أمر godoc أو تحسين التنسيق بكتابة gofmt أو بدء إجراءات فحص الشفرة البرمجية (Unit testing مثلا) عبر كتابة go test وكلها أدوات تأتي مع تنصيب Go. 8. لغة سهلة من حيث الكتابة والتنسيق (Syntax)تنسيق اللغة شبيه نوعا ما بلغة بايثون، حيث لا وجود للفاصلة المنقوطة بعد نهاية كل تعليمة (;)، كما يمكن عمل متغير جديد دون تحديد نوعه، على الرغم من أن اللغة شديدة النوعية strongly styped إلا أنها توحي أنها سَلِسَة وديناميكية، مثلا يمكن كتابة age := 35 وسيتم اسناد نوع int إلى المتغير age. كما أنه يمكن للدوال إرجاع أكثر من قيمة كما في بايثون مثلا. لعل أكثر شيء مغاير لباقي اللغات هو طريقة كتابة الدوال، حيث أن القيمة التي تُرجعها الدالة تُكتب على يمين الدالة (كما في Pascal) وليس على يسارها، مثلا: func HelloHsoub() string { return "Hello Hsoub Academy!" }لاحظ أنه تم كتابة string على يمين الدالة وليس يسارها، أي أن هذه الدالة تُرجع قيمة من نوع string. يمكن للدوال إرجاع دوال أخرى أيضا كقيمة كما يمكن للمتغيرات أن تشير إلى دوال (كما في جافاسكريبت وبايثون...) مثلا: sayHi := HelloHsoub حتى لا أنحاز، فهذه 8 أشياء قد تحبها سأتوقف عندها مقابل 8 قد لا تعجبك كنت قد ذكرتها. بهذا نكون وصلنا إلى ختام الدرس الأول -الكلامي- من هذه السلسلة، والتي رأيت أن أبدأها هكذا عمدا ولو أن بعضا مما قيل في هذا الدرس قد لا يكون بالضرورة موجها للمبرمج المبتدئ، لكني سأحاول جعل الدروس القادمة أبسط ما يكون وتُعنى بالمبتدئ أولا.
- 5 تعليقات
-
- golang
- concurrency
-
(و 3 أكثر)
موسوم في:
-
كيف تبدو البرمجة بلغة Go؟ وكيف أقوم بتشغيل برنامج كُتب بـ Go؟ هذا ما سنتعرف عليه خلال هذا الدرس من سلسلة مدخل إلى لغة البرمجة Go، وقبل متابعة الدرس، أحب أن أتأكد أنك تابعت الدرسين السابقين منها أولا. أهلا بالعالمسنحاول اتباع العادة التي جرت عليها دروس تعلم لغات البرمجة، وهي ببساطة كتابة جملة "أهلا بالعالم" (بالانجليزية "Hello World") باللغة المُراد تعلمها، ببساطة هذا ما سيبدو عليه برنامج أهلا بالعالم في Go: package main import "fmt" func main() { fmt.Print("أهلا بالعالم\n") }ضع هذا في ملف باسم helloworld.go داخل مجلد باسم hellogo مثلا، تحت مسار: $GOPATH/src/github.com/YOUR_USER/حيث GOPATH$ هو مسار بيئة عملك، راجع الدرس الثاني لتفهم آلية عمله.غيّر YOUR_USER إلى اسم مستخدمك على github.com مثلا أو أحد خدمات إدارة الشفرة البرمجية.بالتالي سيكون المسار النهائي للملف الجديد الذي أنشأناه هو: $GOPATH/src/github.com/YOUR_USER/hellogo/helloworld.goكل ما عليك فعله الآن هو التنقل إلى مسار الملف وكتابة أمر go run helloworld.go $ go run helloworld.go أهلا بالعالمإذا كانت طرفية سطر الأوامر لديك لا تدعم اللغة العربية، فقد تجد أن الكتابة العربية مقلوبة والحروف متقطعة، لا بأس بذلك حاليا، لكن لاحظ أن Go قبلت بالحروف العربية داخل نص الشفرة البرمجية وتعاملت معها بشكل عادي، ذلك لأن كل شيء تعتبره unicode افتراضا. لنشرح البرنامج سطرًا بسطر: package mainببساطة: لابد لكل ملف شيفرة برمجية في Go أن ينتمي إلى حزمة (package) ما.ولابد لكل حزمة في Go أن تنتمي إلى مجلد ما.لا يمكن لحزمتين التواجد على مستوى نفس المجلد، لكن يمكن لعدة ملفات أن تنتمي إلى نفس الحزمة (يعني مجلد به عدة ملفات)، كما يمكن أن تتواجد حزمة داخل حزمة أخرى لكن كلٌ في مجلد فرعي على حِدى.في مثالنا السابق، أعطينا main كاسم لحزمتنا، وهو اسم خاص، حيث يُعامل مُجمّع (compiler) لغة Go هذه الحزمة على أنها مدخل البرنامج (program entry)، أي أن التعليمات الموجودة في هذه الحزمة يتم تشغيلها أولا. أسبقنا اسم الحزمة بالكملة المفتاحية package. import "fmt"import: كلمة مفتاحية أخرى وتعني "استرِد" أو "اجلِب" المكتبة الفلانية أو الحزمة الفلانية."fmt" اختصار لـ format أو formatting وهي مكتبة قياسية (standard library) تأتي مع تنصيب Go، خاصة ببناء وطباعة النص. لاحظ أن اسم المكتبات أو الحزم المُراد استيرادها دائما ما يتم إحاطتها بعلامة اقتباس "".func main() {هنا قمنا بإنشاء دالة تحمل اسم main: لإنشاء الدالة استعملنا الكملة المفتاحية func ثم مسافة، ثم اسم الدالة.مثل حزمة main فإن دالة main أيضا يعاملها المجمع (Compiler) معاملة خاصة، حيث تعتبر هي نقطة دخول البرنامج (Entry point)، أي أن التعليمات الموجودة في هذه الدالة تحديدا، داخل حزمة main تحديدًا، يتم تشغيلها أولا، إلا في حالة وجود دالة أخرى باسم ()init والتي سنتعرض لها في دروس أخرى.مثل جافاسكريبت وباقي اللغات، فإن } يفتح جسم الدالة (function body).fmt.Print("أهلا بالعالم\n")هنا قمنا باستعمال حزمة "fmt" التي قمنا باستيرادها أعلاه، لاستعمال أي حزمة في Go يكفي كتابة اسمها، ثم نقطة، ثم اسم الدالة التي تريدها استعمالها من تلك الحزمة، في حالتنا هذه، أردنا ببساطة طباعة نص "أهلا بالعالم" والرجوع إلى السطر، لذلك استعملنا دالة Print ومررلنا لها القيمة "n\أهلا بالعالم":مررنا القيمة بين علامتي اقتباس "" ﻷنها عبارة عن سلسلة نصية (string).أنهينا سلسلتنا النصية بعلامة n\ وهي علامة خاصة في أغلب لغات البرمجة، وتعني "سطر جديد"، استعملناها للرجوع إلى السطر.نُمرّر القيم والمعاملات للداوال عبر وضعها بين قوسين مباشرة بجنب اسم الدالة.}أغلقنا جسم الدالة main باستعمال علامة {، أي أن تعليمات هذه الدالة قد انتهت.لاحظ غياب علامة الفاصلة المنقوطة ";" بعد نهاية كل تعليمة، لا حاجة لها في Go.مفهوم الحزم (Packages) في Goالحزم (packages) طريقة بسيطة لتجزئة برنامجك إلى أجزاء أصغر مُقسّمة حسب الغرض أو الوظيفة، يُمكن الاشارة للحزم على أنهم "مكتبات" (libraries) أو وحدات (modules)، تركيب الحزم مع بعضها يشكل مُجمل برنامجك. تتبع Go القواعد التالية في كيفية تقسيم برنامج إلى حزم: الحزمة غالبا عبارة عن مجلد داخل مشروعك به الملفات التي تحمل اسم الحزمة.لابد من وجود حزمة واحدة على الأقل في أي برنامج أو مكتبة.إذا كان البرنامج عبارة عن برنامج تنفيذي (وليس مكتبة - library) فإنه يجب وجود حزمة باسم main (أي package main) تكون هي مدخل البرنامج كما رأيناه في مثالنا الأول.وكتذكير لما ذكرناه سابقا: لابد لكل ملف شفرة برمجية في Go أن ينتمي إلى حزمة (package) ما.ولابد لكل حزمة في Go أن تنتمي إلى مجلد ما.لا يمكن لحزمتين التواجد على مستوى نفس المجلد، لكن يمكن لعدة ملفات أن تنتمي إلى نفس الحزمة (يعني مجلد به عدة ملفات).يمكن أن تتواجد حزمة داخل حزمة أخرى لكن كلٌ في مجلد فرعي على حِدى.يمكن للمجلد الرئيسي لمشروعك أن يحتوي على حزمة ما، واحدة فقط، باقي الحزم الخاصة به يمكنها أن تتواجد في مجلدات فرعية.غياب حزمة main من برنامج ما، يجعل منه مكتبة فقط وليس برنامجا تشغيليا قائما بحد ذاته.عند كتابة الحزم، أي اسم دالة أو متغير أو بُنية (Struct) يبدأ بحرف كبير (Uppercase) يعني أن هذه الدالة/المتغير/البنية متوفر بشكل عام لجميع من يقوم باستيراد هذه الحزمة عبر الكلمة المفتاحية import. وأي اسم دالة/متغير يبدأ بحالة أحرف صغيرة (Lowercase) يعني أنه خاص ولن يتم تصديره (لن يكون متاحا) لباقي الحزم والبرامج. في مثالنا السابق، وفّرت حزمة "fmt" دالة Print لنا بشكل مُتاح، لاحظ أن Print تبدأ بحرف P كبير (Uppercase). وكان فقط يكفي كتابة (...)fmt.Print مكتبة (حزمة) أهلا بالعالمماذا لو أردنا قول "أهلا بالعالم" في أكثر من مشروع؟ ربما سيكون من الأمثل جعل طباعة "أهلا بالعالم" في مكتبة مستقلة نقوم باستيرادها في مشاريع أخرى، هذا مثال فقط لكيفية بناء مكتبة في Go، فطبعا لن تحتاج إلى مكتبة كل ما تقوم به هو "أهلا بالعالم". دعنا نحاول كتابة هذا في دالة بسيطة، نسميها SayHello ولنضها في حزمة خاصة، نسميها sayhello. دعنا نبني هذه المكتبة في مشروع مستقل، لتكون مكتبة يُمكن جلبها في أكثر من برنامج أو مشروع آخر. سنسمي المشروع sayhello، أي بنفس اسم الحزمة لتسهيل الأمر. لن نحتاج إلى package main ﻷننا سنبني مكتبة فقط وليس برنامجا تنفيذيا في حد ذاته. بالتالي قم بإنشاء مجلد مشروع جديد تحت مسار: $GOPATH/src/github.com/YOUR_USER/بنفس الطريقة التي شرحناها سابقا. أنشئ ملفا باسم sayhello.go تحت مجلد sayhello، ليكون مسار الملف أشبه بهذا: $GOPATH/src/github.com/YOUR_USER/sayhelo/sayhello.goوالآن، نفتح ملف sayhello.go بمحرر النصوص المفضل لديك ثم نشرع في كتابة دالتنا المذهلة، لا يُفترض بك فهم كل شيء حاليا، فالغرض شرح مفهوم الحزم والمكتبات، مع ذلك سنحاول شرح تعليمات هذه الدالة: package sayhello // SayHello returns Hello World in Arabic. func SayHello() string { return "أهلا بالعالم\n" } لاحظ أن دالتنا لا تختلف كثيرًا عن برنامج "أهلا بالعالم" الذي كتبناه أول مرة: استعملنا package sayhello عوض package main.سمينا الدالة ()func SayHello عوض ()func main. لاحظ أننا جعلنا اسم الدالة يبدأ بحرف كبير (Uppercase) ذلك ﻷننا نريد تصدير هذه الدالة وإتاحتها لباقي المشاريع التي تسترد حزمة sayhello.قمنا بتصريح أن الدالة تُرجع سلسلة نصية عبر كتابة الكلمة المفتاحية string على يمين الدالة.قمنا بإرجاع السلسلة النصية "n\أهلا بالعالم" عبر الكلمة المفتاحية return عوض طباعتها باستخدام مكتبة fmt كما فعلنا أول مرة.أصبح الآن لدينا "أهلا بالعالم" في شكل مكتبة! لكن كيف نجعلها قابلة للجلب؟ أي كيف نضعها في مجلد pkg حتى تستطيع باقي المشاريع جلبها والاستفادة منها؟ كل ما علينا فعله هو كتابة go install داخل مجلد مشروع sayhello. طبعا لن يكون بإمكانك تشغيل هذه المكتبة عبر أمر go run sayhello.go مثل ما فعلنا أو مرّة، ﻷن ما قمنا بكتابته هذه المرة عبارة عن مكتبة وليس برنامجًا تنفيذيًا! فحص مكتبة "أهلا بالعالم"هل تعمل دالتنا بشكل جيد؟ كيف يمكن التحقق من برامجنا وحزمنا التي نكتبها في Go؟ هل مكتبة "أهلا بالعالم" تطبع حقا "أهلا بالعالم"؟ دعنا نتأكد من ذلك عبر كتابة فحص مؤتمت لهذه الدالة! في لغة Go: يمكن كتابة فحوصات واختبار وحدات (Unit testing) بسهولة، يكفي إنشاء ملف بنفس اسم الملف المُراد اختباره مع إضافة test_ له.يمكن فحص دالة معينة بمجرد كتابة Test ثم اسم الدالة.في حالتنا هذه، وفي نفس مسار مشروع sayhello أنشئ ملفا باسم sayhello_test.go بجانب ملف sayhello.go نفسه لنقوم باختبار حزمة sayhello. افتح ملف sayhello_test.go بمحرر النصوص المفضل لديك. كل ما علينا فعله لتجربة دالتنا هو جلبها وفحص إذا كانت نتيجة الطباعة هي فعلا "أهلا بالعالم"! كما هو موضح في الشفرة التالية: package sayhello import "testing" func TestSayHello(t *testing.T) { greeting := SayHello() if greeting != "أهلا بالعالم\n" { t.Error("TEST FAILED!") } } في الشِيفرة أعلاه: قمنا باستيراد حزمة "testing" القياسية التي توفرها لغة Go بشكل افتراضي لأغراض إنشاء الفحوصات المؤتمتة.بما أننا نريد فحص دالة SayHello قمنا بإنشاء دالة TestSayHello مع تمرير testing.T لها، أي أن هذه الدالة تُعتبر "حالة فحص"، لا يجب عليك فهم هذا التعبير حاليا، يمكنك تجاهله، الغرض هو فهم آلية عمل الفحوصات المؤتمتة في Go.داخل هذه الدالة الفاحصة، قمنا ببساطة بنداء دالتنا SayHello وفحص إذا كانت القيمة التي تُرجعها عبارة عن "n\أهلا بالعالم" فإن كانت كذلك، يمر الفحص بسلام ولن نطبع أي شيء، وإن لم تكن كذلك، نطبع "!TEST FAILED" باستخدام المعامل t التابع لحزمة testing القياسية باستعمال الدالة Error، والتي تُخبر بدورها أن حالة الفحص هذه قد فشلت. الآن، في نفس المجلد الذي أنت به يكفي كتابة go test لفحص حزمة sayhello الخاص بنا! يُفترض أن تتلقى نتيجة مشابهة لهذه: PASS ok github.com/01walid/sayhello 0.001sيمكن إضافة v- أو cover-- إلى الأمر السابق لطباعة المزيد من المعلومات حول الفحوصات التي يقوم بها المُجمع، مثلا go test -v --cover === RUN TestSayHello --- PASS: TestSayHello (0.00s) PASS coverage: 100.0% of statements ok github.com/01walid/sayhello 0.001s لاحظ أننا تحصلنا على coverage: 100.0%! أي أن الفحوص التي قمنا بها تغطي جميع الدوال التي قمنا بكتابتها في هذا المشروع (بما أن لدينا دالة واحدة فقط وفحص واحد، فالنتيجة طبيعية)، من المستحسن دائما إبقاء نسبة تغطية الفحوصات مرتفعة في برامجك، حتى تضمن أن استقرارها جيد ويمكن التعويل عليها. إنشاء توثيق لمكتبة "أهلا بالعالم"أنهينا مكتبتنا المذهلة، تأكدنا من صحة عملها، ماذا لو أمكننا توليد ومشاركة توثيق لها؟ لن تحتاج الكثير لفعل ذلك! كل ما عليك فعله هو كتابة الأمر godoc -http=:6060 ثم زيارة localhost:6060 على متصفحك! ستحتاج إلى التنقل إلى توثيق مكتبتك بشكل خاص عبر زيادة http://localhost:6060/pkg/github.com/YOUR_USER/sayhello/طبعا قم بتغيير YOUR_USER إلى اسم المستخدم الخاص بك، تماما مثل مسار المكتبة في بيئة GOPATH$ الخاص بك. استعمال مكتبة "أهلا بالعالم"دعنا نستعمل هذه المكتبة في برنامجنا الأول، سنحتاج إلى تغييرات بسيطة لجعله يستغل مكتبة "أهلا بالعالم" كما هو موضح (تذكر: مشروعنا الأول في مجلد لوحده باسم sayhello): package main import "fmt" import "github.com/01walid/sayhello" func main() { fmt.Print(sayhello.SayHello()) } قمنا فقط بجلب مكتبتنا الجديدة عبر كتابة: import "github.com/YOUR_USER/sayhello"ثم استعمالها عبر مناداة دالة SayHello منها داخل fmt.Print عوض كتابة "أهلا بالعالم" مباشرة: fmt.Print(sayhello.SayHello())الآن شغّل البرنامج مجددا عبر كتابة go run helloworld.go، هذا كل ما في الأمر! تهانينا لك أول برنامج وأول مكتبة لك باستخدام لغة البرمجة Go! إضافة (Bonus)في إضافة هذا الدرس، اخترت أن أشير إلى بعض إضافات محررات النصوص البرمجية الشهيرة، والتي من شأنها أن تسهل عليك البرمجة بـ Go بشكل معتبر. ستجد من هنا قائمة بهذه الإضافات، مثلا إن كنت من مستعملي SublimeText فقم بتنصيب إضافة GoSublime وإن كنت من مستعملي محرر Atom فقط بتنصيب Go-plus. خاتمةتعرفنا في هذا الدرس على كيفية كتابة برنامج باستخدام لغة Go، ثم جعل البرنامج على شكل مكتبة عوض تركه في شكله التنفيذي. من المهم جعل برامجك عبارة عن مجموعة مكتبات منفصلة حسب الغرض، عوض جعل كل شيء متداخل في حزمة واحد أو في برنامج تنفيذي. فكما رأينا إنشاء برنامج تنفيذي أسهل ما يكون، إذ يكفي كتابة ملف به package main ثم func main واستدعاء باقي الحزم، بالتالي الأهم من ذلك هو التفكير التجريدي وفصل المهام عن بعضها في مكتبات وحزم حسب الغرض، كلٌ يقوم بمهمة/غرض ما، ويقوم به بشكل جيد ومُختبر (unit tests). في درسنا هذا، كانت المكتبة منفصلة تماما عن البرنامج التنفيذي. مشاريعك القادمة ستكون على الأغلب مقسمة إلى حزم/مكتبات داخل مجلد المشروع نفسه وليس بالضرورة في مجلد/مشروع آخر، لكن المفهوم هو نفسه. رأينا أيضا، أن التعليقات في Go وسيلة ممتازة لتوليد التوثيق بأدنى جهد، لذلك يجدر بك كمبرمج عدم إهمالها والاهتمام بها وبتفاصيلها. كل هذا كان سهلا! لعلك كنت تسمع بهذه الأمور فتفزع (tests, docs, cover.. الخ) لكني قدمت هذه المفاهيم عمدا في الدروس الأولى لترى أن لا شيء يستحق الهروب منه. وأن البرامج ذات الجودة العالية مجرد اتباع لسلوكيات مهذبة وممارسات محبذة بخطوات ثابتة ينبغي أن تُعوّد نفسك عليها من الآن وتجعل منها أسلوب تطوير/برمجة. في الدروس القادمة سنتطرق إلى أنواع المتغيرات، هيكلة البيانات، كيفية إنشاء متغير، الحلقات والجمل الشرطية.
- 2 تعليقات
-
- 1
-
- hello world
- helloworld
-
(و 2 أكثر)
موسوم في:
-
بعد تعرُّفنا في الدرس السابق على لغة Go ونقاط اختلافها مع باقي لغات البرمجة، سنحاول في هذا الدرس شرح كيفية تنصيبها، تهيئة وفهم بيئة العمل (Workspace) الخاصة بها. من المهم جدا فهم تقسيم المجلدات الذي تمليه عليك بيئة عمل Go، حيث سيمكّنك ذلك من ترتيب مشروعك بشكل أفضل، تسهيل مشاركته، وجعله قابلا للجلب والتضمين في مشاريع أخرى عبر أمر go get الذي سنتعرّف عليه لاحقا. تملك Go نوعين من المُجمّعات (Compilers). وكتذكير سريع، وظيفة المُجمّع هي أخذ شيفرتك البرمجية في شكلها النصّي البحت، وتحويلها إلى برنامج تنفيذي (أو مكتبة). أحد مجمّعَيْ Go هو المجمع القياسي الذي يحمل اسم gc اختصارًا لـ Go Compiler، والآخر مُجمّع يعتمد على مجمّع gcc وما يوفره كمنصة ودعم معماريات معالجات (CPU) أكثر ويحمل اسم gccgo. يجدر الإشارة إلى أن مجمع gccgo متأخر نوعا ما في زرع كامل مواصفات النسخة الأحدث من Go، فعلى سبيل المثال، ولدى كتابة هذا الدرس، يوفر gccgo نسخة 1.4.1 من Go في حين يوفر gc دائما آخر نسخة (1.5.2) من اللغة. ذكرت هذا من باب العلم بالشيء، وباختصار، إن لم يهمك دعم معماريات أكثر أو بعض تفاصيل منصة gcc فلن تحتاج إلى gccgo وستكتفي بـ gc القياسي وهو ما سنتبعه خلال هذه السلسلة. تنصيب Goكما ذكرنا في الدرس السابق، فإن Go لغة متعددة المنصّات بامتياز، وهي جاهزة للتحميل والتنصيب على نظامك المفضل لكلا معماريتي 32bit و 64bit. تنصيب Go على Windows و Mac OSيكفي التوجه إلى موقع اللغة ثم تحميل النسخة الخاصة بنظامك. عملية تنصيبها عادية وهي مشابهة لعملية تنصيب أي برنامج عادي آخر. بعد التنصيب، تأكد من أن العملية تمت بنجاح عبر كتابة go version أو فقط go في سطر الأوامر. إن كنت على Windows تأكد من غلق أي نافذة سطر أوامر مفتوحة مسبقا (إن وُجدت) ثم فتح نافذة جديدة. تنصيب Go على Linuxأنصحك بالاطلاع على مدير حزم توزيعتك قبل تحميل وتنصيب Go من أي مكان آخر. فإن وجدت أن مدير حزم توزيعتك يوفر آخر نسخة من Go فسيكون الأمر أسهل بكثير، إن وجدت أن توزيعتك توفر نسخة أقدم من Go 1.5.1 فعليك تنصيب آخر نسخة منها إن كنت تريد متابعة هذه السلسلة. إن كنت على Ubuntu والتوزيعات المبنية عليها، فأغلب الظن أن نسخة Go التي يوفرها مدير الحزم apt قديمة، كون التوزيعة يتم تحديثها كل 6 أشهر أو عامين (في حال كانت LTS). لذلك أنصحك بالتنصيب من خلال إضافة مستودع etherneum. sudo add-apt-repository -y ppa:ethereum/ethereum sudo apt-get update sudo apt-get install golangيُفترض بباقي التوزيعات أن تحتوي على آخر نسخة من Go، يكفي حينئذ التنصيب من مدير الحزم، مثلا على Arch Linux: sudo pacman -S goبعد التنصيب، تأكد من أن عملية التنصيب تمت بنجاح عبر كتابة go version أو فقط go في سطر الأوامر. من المفروض تتلقى نتيجة مشابهة لهذه: go version go1.5.2 linux/amd64فهم بيئة العملنقصد ببيئة العمل هنا أمران أساسيان: مجلد على حاسوبك، تقوم بإنشائه في أي مكان تريد، سيكون مكانا لجميع مشاريع Go.متغير بيئة (environment variable) يحمل اسم GOPATH يشير إلى هذ المجلد.لنفرض أنك سمّيت مجلد العمل باسم work، فإن هذا الأخير بدوره يجدر به أن يحوي ثلاث مجلدات فرعية أخرى: مجلد فرعي باسم src، حيث ستضع فيه جميع الشيفرات البرمجية لكامل مشاريع Go خاصتك.مجلد فرعي باسم bin، حيث سيقوم مجمع gc بنقل الملفات التنفيذية الناتجة عن شيفراتك البرمجية مباشرة إلى هذا المجلد.مجلد فرعي باسم pkg، مثل bin لكن ليس للملفات التنفيذية، إن كتبت برنامجا عبارة عن مكتبة وليس برنامجا تنفيذيا في حد ذاته، أو جلبت مكتبة خارجية عبر أمر go get، فسيتم تجميعها كمكتبة يتم وضعها في هذا المجلد حتى يسهل جلبها في باقي البرامج دون إعادة تجميعها في كل مرة (هذا يُسرّع من عملية تجميع برنامجك التنفيذي الذي يستخدم هذه المكتبات).باعتبار ما سبق، فإن بيئة Go ستكون مشابهة لهذا التقسيم الشجري: work # مجلد العمل ├── bin # مجلد الملفات التنفيذية الناتجة ├── pkg # مجلد المكتبات البرمجية الناتجة └── src # مجلد شيفراتنا البرمجية هل هذا كل شيء؟ طبعا لا. بما أن Go لغة حديثة نسبيا، فإنها تفترض أنك ستستضيف شيفرتك البرمجية في مكان (مستودع) ما، وهذه عادة أي مبرمج محترف، خشية على الشيفرة البرمجية من الضياع أو من أجل مشاركتها مع أناس آخرين. طبعا مستودع شيفرتك البرمجية سيكون مُدارا بأحد أدوات إدارة النُسخ (VCS) مثل Git أو Mercurial مثلا. قد تكون الشيفرة على خادومك الخاص (Server)، أو على أحد خدمات استضافة المشاريع البرمجية مثل Github و Bitbucket. بما أن Github أشهر المنصات لمشاركة الشيفرات البرمجية، فسأضرب به المثال. تدفعك Go (أي أنه من المستحسن) إلى وضع شيفرتك البرمجية في مسار مشابه لمسار حسابك على github مثلا -وحتى أقرّب الصورة- لنفرض أن حسابك على github اسمه HsoubAcademy وبالتالي رابط حسابك هو: https://github.com/HsoubAcademyإذا أنشأت مشروعا بـ Go يحمل اسم example على سبيل المثال ثم رفعته على Github باستخدام حسابك HsoubAcademy فسيكون رابط المشروع هو: https://github.com/HsoubAcademy/exampleبالتالي فإن Go تدفعك ﻷن يكون مجلد المشروع على حاسوبك مشابها لهذا الرابط، أي تحت هذا المسار: work/src/github.com/HsoubAcademy/exampleحيث: github.com هو مجلد فرعي تحت مجلد src.HsoubAcademy هو مجلد فرعي تحت مجلد github.com آنف الذكر، غيّره إلى حسابك على Github مثلا.example هو مجلد فرعي يحوي ملفات مشروعك تحت مجلد HsoubAcademy.ملاحظة: أسماء المجلدات حسّاسة لحالة الأحرف (case-sensitive) ماعدا على نظام Windows. لماذا ترتيب بيئة العمل بهذا الشكل؟ببساطة ﻷن التنظيم بهذا الشكل مفيد جدًا من ناحيتين: 1. بالإمكان جلب أي مكتبة خارجية بمجرد عمل import لها من مسار مستودعها، مثلا: لنفرض أننا نريد استعمال مكتبة لنزع التشكيل عن النص العربي، سنستخدم مكتبة goarabic، سيكفي لتضمينها في مشروعنا البرمجي كتابة الأمر: go get github.com/01walid/goarabic2. يقوم أمر go get بوضع ناتج تجميع هذه المكتبة في مجلد pkg بنفس مسار رابطها على github، أي سيضعها داخل مجلد فرعي باسم 01walid داخل مجلد github.com الذي هو بدوره عبارة عن مجلد فرعي داخل مجلد العمل، بالتالي يمكن تضمينها مباشرة في مشروعنا بهذا الشكل: import "github.com/01walid/goarabic" لاحظ أن مسار استيراد هذه المكتبة هو نفسه رابطها على github. هذا يُسهّل من عملية مشاركة المكتبات البرمجية وإثراء مكتبات اللغة نفسها. من المهم جدا أن يتبع مشروعك نفس هذا المنهاج، خاصة إن كان عبارة عن مكتبة. تهيئة بيئة العملإن كنت على لينكس أو Mac OS، يمكنك مثلا إنشاء مجلد باسم work داخل مجلد Home خاصتك بكتابة أمر: mkdir $HOME/workثم جعل هذا الأخير هو بيئة عمل Go عبر إنشاء متغير بيئة باسم GOPATH (حسّاس لحالة الأحرف) يُشير له: export GOPATH=$HOME/workطبعا لجعل هذا المتغير دائم حتى بعد إعادة التشغيل، سيكون عليك كتابته في bashrc. (في حال كنت تستعمل صدفة bash، أو ملف zshrc. إن كنت تستعمل صدفة zsh كمثال، وهي ملفات موجود داخل مجلد المنزل خاصتك HOME$). أما إن كنت على Windows فيكفي إضافة متغير باسم GOPATH عبر التوجه إلى: خصائص النظام (System -> Advanced system settings)ثم النقر على متغيرات البيئة (Environment Variables)ثم إضافة المتغير من قسم System variables كما هو مبين أسفله. إذا كان مجلد عملك مثلا في Windows هو C:\Projects\Go فسيكون عليك إضافته بهذا الشكل: يمكنك التحقق من أنه تم إضافة المتغير الجديد، عبر فتح نافذة سطر أوامر جديدة، وكتابة الأمر: %echo %GOPATH كما هو موضح أسفله: إن تم إظهار المسار المُختار لمجلد عملك، فقد قمت أخيرًا بتنصيب Go وتهيئتها بنجاح. إضافة (Bonus)ماذا لو أمكننا جعل البرامج التنفيذية التي نكتبها أو نجلبها عبر أمر go get متوفرة مباشرة لدينا في نافذة سطر الأوامر دون التصفح إليها في كل مرة؟ مثلا لنقل أننا نريد الاستفادة من أداة ha المكتوبة بلغة Go، لتحويل ملفات Markdown إلى HTML جاهز للّصق على محرر آخر، نقوم بجلب الأداة: go get github.com/HsoubAcademy/haبما أن أداة ha عبارة عن برنامج تنفيذي فإن أمر go get سينقله مباشرة إلى مجلد bin داخل بيئة العمل. لكن لا نريد التنقل إلى مجلد bin داخل مجلد العمل لاستعمالها، نريد فقط كتابة ha في سطر الأوامر مباشرة للاستفادة من الأداة واستعمالها حتى وإن كنا في مجلد آحر. بما أن Go تقوم بوضع ناتج الملفات التنفيذية في مجلد bin تحت مجلد العمل، بالتالي، يكفي إضافة مسار مجلد bin إلى مسارات متغير البيئة PATH ليكون كل شيء متوفر لديك! حتى برامجك التنفيذية التي تكتبها بنفسك. بالنسبة لمستخدي Linux أو Mac OS فإن ذلك ممكن عبر إضافة مسار مجلد bin داخل مجلد العمل إلى متغير البيئة PATH: export PATH=$PATH:$GOPATH/binأما لمستخدمي Windows فعليهم إضافة مسار مجلد bin داخل مجلد العمل إلى متغير البيئة PATH عبر التوجه إلى: خصائص النظام (System -> Advanced system settings)ثم النقر على متغيرات البيئة (Environment Variables)البحث عن قيمة Path في قسم System variables ثم الضغط على edit وإضافة المسار إلى آخر السطة مسبوقا بفاصلة منقوطة (;) كما هو مبين أسفله. الآن ستجد أن كل ما هو موجود في مجلد bin من برامج وأدوات متاح لك، من ذلك أداة ha التي أشرنا إليها سابقا (جرب فتح نافذة سطر أوامر جديدة وكتابة الأمر ha). بهذا نأتي إلى نهاية الدرس الثاني من هذه السلسلة، سنشرع في الدرس القادم في كيفية بدء البرمجة بـ Go والاستفادة من بيئة العمل هذه.