OOZOU
تواصلوا معنا
العودة إلى المدونة

مطوّر Ruby يلقي نظرة على Crystal

نظرة أقرب على Crystal، لغة برمجة جديدة ومثيرة للاهتمام، يُقال إنها سريعة مثل C وأنيقة مثل Ruby.

8 نوفمبر 2016
8 دقائق للقراءة
مطوّر Ruby يلقي نظرة على Crystal

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

سريعة مثل C، وأنيقة مثل Ruby fancy-quote

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

الانطباعات الأولى

يذكر الموقع الإلكتروني للمشروع خمسة أهداف لـ Crystal، أولها "أن تكون صياغتها مشابهة لـ Ruby (لكن التوافق معها ليس هدفًا)".

تبدو Crystal بالفعل مثل Ruby. بل يمكن كتابة برامج (بسيطة) يقبلها كلا المُصرّفين (compilers). لكن كما ذُكر، التوافق ليس هدفًا، وربما يكون ذلك للأفضل، لأن دلالات اللغتين (semantics) مختلفة تمامًا في الواقع رغم التشابه في الصياغة.

يعطي تعريف الصنف (class) التالي انطباعًا أوليًا عن Crystal:

class Person
  property age
  getter name

  def initialize(@name : String, @age : Int32)
  end
end

p = Person.new("Michael", 35)
p.name #=> "Michael" : String
p.age #=> 35 : Int32
p.age += 1 #=> 36 : Int32
p.name = "Other person" # undefined method 'name=' for Person
lang-crystal

مع أن هذا يشبه Ruby كثيرًا، هناك بعض الفروق الملحوظة. تحل property وgetter، وهما أوضح في القراءة، محل attr_accessor وattr_reader. وتستخدم دالة initialize اختصارًا لإسناد معاملاتها مباشرة إلى متغيرات النسخة (instance variables)، كما تستخدم قيود الأنواع (type restrictions)، التي نادرًا ما نحتاج إليها بفضل قدرة Crystal الممتازة على استنتاج الأنواع (type inference).

يعرض المثال التالي فرقًا كبيرًا آخر بين اللغتين، وهو التحميل الزائد للدوال (method overloading) بحسب عدد المعاملات ونوعها، وهي ميزة طالما تمنيتها في Ruby:

class Dog
  def greet
    "Woof! Woof!"
  end

  def greet(name : String)
    "Woof #{name}!"
  end

  def greet(times : Int32)
    greet * times
  end
end

d = Dog.new
d.greet #=> "Woof! Woof!" : String
d.greet("dear readers!") #=> "Woof dear readers!" : String
d.greet(3) #=> "Woof! Woof!Woof! Woof!Woof! Woof!" : String
lang-ruby

هنا نعرّف ثلاثة تطبيقات مختلفة لـ Dog#greet، ويوجّه المُصرّف الاستدعاء بشكل صحيح إلى النسخة التي تطابق عدد المعاملات ونوعها. شخصيًا أجد هذا أجمل بكثير من الشروط التي تتحقق من وجود المعاملات الاختيارية.

ومن المجالات الأخرى التي تتفوق فيها صياغة Crystal على Ruby الصيغة المختصرة للكتل (blocks) (انظروا Symbol#to_proc):

%w(foo bar).map(&.upcase) #=> ["FOO", "BAR"] : Array(String)
(1..5).map(&.+(2)) #=> [3, 4, 5, 6, 7] : Array(Int32)
lang-crystal
%w(foo bar).map(&:upcase) #=> ["FOO", "BAR"]
(1..5).map(&2.method(:+)) #=> [3, 4, 5, 6, 7]
lang-ruby

بينما تستخدم Ruby الصيغة &:method_name، تستخدم Crystal الصيغة &.method_name، وأجدها أوضح في التعبير عن القصد. كما تتيح تمرير معاملات إلى الدالة المستدعاة، وهو أمر غير ممكن عمومًا في Ruby (أو ممكن فقط ببعض الحيل كما في المثال أعلاه).

هناك فروق صياغية بسيطة أخرى، مثل أن النصوص (strings) تُحاط دائمًا بعلامات اقتباس مزدوجة (علامات الاقتباس المفردة تدل على قيم الأحرف الحرفية)، أو أن معدّلات الوصول (access modifiers) جزء من تعريف الدالة. لكن لا ينبغي أن يُربك أي منها مطوّري Ruby كثيرًا (وإن كنت أجد أحيانًا صعوبة في التغلب على ذاكرة العضلات).

الأنواع

Crystal لغة ذات أنواع ثابتة (statically typed). لكن هذا لا يعني أن كودكم يجب أن يمتلئ بتعليقات الأنواع (type annotations)، فالمُصرّف يؤدي عادة عملًا رائعًا في استنتاج الأنواع. ومع ذلك، هناك حالات معينة تحتاج فيها اللغة إلى مساعدتكم، وفي هذه الحالة ستعرض لكم رسالة خطأ مفيدة.

لنلقِ نظرة على مثال:

class Foo
  def initialize(a)
    @a = a
  end
end

Foo.new(1)
lang-crystal

لن يُصرَّف هذا المثال الذي يبدو بريئًا، بل سيُنتج خطأ وقت التصريف التالي:

Can’t infer the type of instance variable ‘@a’ of Foo The type of a instance variable, if not declared explicitly with @a : Type, is inferred from assignments to it across the whole program. The assignments must look like this: 1. @a = 1 (or other literals), inferred to the literal’s type 2. @a = Type.new, type is inferred to be Type 3. @a = Type.method, where method has a return type annotation, type is inferred from it 4. @a = arg, with ‘arg’ being a method argument with a type restriction ‘Type’, type is inferred to be Type 5. @a = arg, with ‘arg’ being a method argument with a default value, type is inferred using rules 1, 2 and 3 from it 6. @a = uninitialized Type, type is inferred to be Type 7. @a = LibSome.func, and LibSome is a lib, type is inferred from that fun. 8. LibSome.func(out @a), and LibSome is a lib, type is inferred from that fun argument. Other assignments have no effect on its type. Can’t infer the type of instance variable ‘@a’ of Foo

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

def initialize(a : Int32)
  @a = a
end

# or shorter
def initialize(@a: Int32)
end
lang-crystal

أكاد أسمع مطوّري Ruby المخضرمين يسألون الآن: لماذا نهتم بالأنواع الثابتة أصلًا؟ والسؤال وجيه. لذا لنلقِ نظرة على مثال ينقذنا فيه استنتاج الأنواع في Crystal والاستخدام الذكي لـ أنواع الاتحاد (union types) من خطأ وقت التشغيل.

found = %w(foo bar).find { "foo" }
typeof(found) #=> (String | Nil)
found.upcase # undefined method 'upcase' for Nil (compile-time type is (String | Nil))
found.upcase if found #=> "FOO" : String
lang-crystal

هنا ستُرجع Enumerable#find إما نصًا وإما nil، وهو ما يؤدي في Ruby إلى RuntimeError حين لا يُعثر على أي عنصر ونحاول استدعاء دالة upcase على nil. أما مُصرّف Crystal فيستخدم هنا نوع الاتحاد String | Nil للمتغير found، ولن يُصرّف هذا الكود لأن ليس كل الأنواع في الاتحاد تعرف كيف تستجيب لرسالة upcase. لذا، لكي يُصرَّف هذا البرنامج فعلًا، علينا أن نتحقق صراحة من حالة nil كما هو موضح في السطر الأخير من الكود أعلاه.

كان هذا بالطبع مثالًا مصطنعًا وقصيرًا، لكنه يُظهر كيف أنقذتنا Crystal من خطأ أثناء تنفيذ البرنامج من دون أي عمل إضافي من جانبنا.

متفرقات

لنختم هذه النظرة الأولى على Crystal، دعونا نستعرض بسرعة بعض الميزات الجميلة الأخرى التي لا تقدمها Ruby.

Structs

تقدم Crystal أكثر من طريقة لتعريف الأصناف. فبدلًا من استخدام الكلمة المفتاحية class، يمكن تعريفها أيضًا باستخدام struct.

struct Point
  property x, y

  def initialize(@x : Int32, @y : Int32)
  end
end

p = Point.new(5, 3)
p.class #=> Point : Class
lang-crystal

مع أن الكود أعلاه يعرّف صنفًا أيضًا، فإن كائنات Point ستُخصَّص في الـ stack وليس في الـ heap، وستُمرَّر بالقيمة (pass-by-value). شخصيًا أرى أنها إضافة رائعة للأنواع غير القابلة للتغيير (immutable)، تمنحكم تحكمًا أكبر في استهلاك برنامجكم للذاكرة.

Enums

تتيح لنا التعدادات (Enums) تجميع القيم المترابطة:

enum Suit
  Spades
  Diamonds
  Clubs
  Hearts
end

Suit::Spades.value #=> 0
lang-crystal

تُستخدم غالبًا في المواضع التي قد يستخدم فيها مطوّرو Ruby الرموز (symbols)، مع ميزة إضافية هي التحقق من الأنواع (أي أن كتابة Suit::Club بدلًا من Suit::Clubs ستؤدي إلى خطأ في المُصرّف، بينما قد تؤدي :club إلى سلوك غير صحيح وقت التشغيل). ولأن التعدادات يمكنها تعريف دوالها الخاصة تمامًا مثل الأصناف والـ structs، فهي مفيدة جدًا لتجميع القيم المترابطة والسلوك المرتبط بها.

Tuples

مثل Python، تمتلك Crystal نوع tuple. تُعرَّف الـ tuples بالصيغة {element1, element2,...}، وهي ممتازة لتجميع القيم المترابطة مؤقتًا. وتُستخدم داخليًا أيضًا في الإسناد المتعدد مثل a, b = 1, 2، حيث تستخدم Ruby بدلًا منها Array.

الخلاصة

Crystal لغة جميلة تشبه Ruby كثيرًا في إحساسها، مع أنها تُصرَّف إلى كود سريع وفعّال عبر LLVM. أرجو أن تكون هذه المقدمة القصيرة قد نجحت في إثارة اهتمامكم باللغة، وربما حفزتكم على تجربتها. في المقال القادم أنوي استعراض بعض الميزات الأكثر تقدمًا، مثل البرمجة الوصفية (metaprogramming) باستخدام الماكرو، والتكامل مع C، والتزامن (concurrency). ترقبوا!

Related Articles

كيف أصمّم برمجيات؟ دليل المبتدئين لأصحاب الأعمال

كيف أصمّم برمجيات؟ دليل المبتدئين لأصحاب الأعمال

تصميم البرمجيات عملية ممتعة لكنها معقدة، تتطلب الإبداع والتفكير المنطقي ومهارات حل المشكلات.

اقرأوا المزيد
مستقبل الحوسبة الطرفية: ما تحتاج الشركات إلى معرفته

مستقبل الحوسبة الطرفية: ما تحتاج الشركات إلى معرفته

يتطور المشهد الرقمي بسرعة، وعلى الشركات أن تتكيّف لتواكب الطلب المتزايد على السرعة والكفاءة والمعالجة في الوقت الفعلي.

اقرأوا المزيد
مستقبل قطاع التجزئة: الذكاء الاصطناعي والواقع المعزّز واتجاهات التحوّل الرقمي

مستقبل قطاع التجزئة: الذكاء الاصطناعي والواقع المعزّز واتجاهات التحوّل الرقمي

يتطور قطاع التجزئة بوتيرة غير مسبوقة بفضل الذكاء الاصطناعي (AI) والواقع المعزّز (AR) وتقنيات التحوّل الرقمي. فلم تعد المتاجر التقليدية مجرد أماكن لبيع المنتجات، بل أصبحت متاجر ذكية وتفاعلية.

اقرأوا المزيد

Have a Project in Mind?

Let's discuss how we can help bring your ideas to life with our expertise in web and mobile development.

ابدأوا المحادثةhello@oozou.com
OOZOU Logo

بانكوك · سنغافورة · هونغ كونغ

X
Facebook
Instagram
LinkedIn
OOZOU Co., Ltd.
548 One City Centre
Ploenchit Road
Bangkok 10330
Thailand
+66 2544 0000
hello@oozou.com

الخدمات

  • تطوير الويب
  • وكلاء الذكاء الاصطناعي والذكاء الاصطناعي التوليدي
  • تطوير تطبيقات الجوال
  • تحليلات البيانات وهندستها
  • تصميم تجربة المستخدم وواجهته (UX/UI) وتصميم المنتجات
  • التحوّل الرقمي

الشركة

  • من نحن
  • الوظائف
  • المدونة
  • دراسات الحالة
  • عروض حية
  • تعلّمت اليوم
  • تواصل معنا

المزيد

  • القطاعات
  • شبكة الشركاء
© 2026 OOZOU. جميع الحقوق محفوظة.
سياسة الخصوصيةمدونة قواعد السلوكسياسة مكافحة الرشوة والفساد