---
title: "مطوّر Ruby يلقي نظرة على Crystal"
description: "نظرة أقرب على Crystal، لغة برمجة جديدة ومثيرة للاهتمام، يُقال إنها سريعة مثل C وأنيقة مثل Ruby."
canonical: https://oozou.com/ar/blog/a-rubyist-looks-at-crystal-23
locale: ar
date: 2016-11-08
author: "OOZOU Team"
---

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

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

Published: 2016-11-08 · Author: OOZOU Team (Engineering) · 8 min read

لا نعاني اليوم من نقص في لغات البرمجة الجديدة والمثيرة للاهتمام. يكاد يكون من المستحيل أن تقضي بعض الوقت على Hacker News أو Twitter من دون أن ترى إعلانات عن لغات جديدة كل أسبوع. ولأنني مهووس كبير بلغات البرمجة، أقرأ عادة عن معظمها، لكن قلة منها فقط تنجح في الاحتفاظ باهتمامي طويلًا. غير أن [Crystal](https://crystal-lang.org/) واحدة من اللغات الجديدة التي نجحت في ذلك، وقد ساهم في ذلك بلا شك الشعار المكتوب على [صفحتها في BountySource](https://salt.bountysource.com/teams/crystal-lang):

> _سريعة مثل 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)](https://crystal-lang.org/docs/syntax_and_semantics/type_restrictions.html)، التي نادرًا ما نحتاج إليها بفضل قدرة 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](http://ruby-doc.org/core-2.3.1/Symbol.html#method-i-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)](https://crystal-lang.org/docs/syntax_and_semantics/union_types.html) من خطأ وقت التشغيل.

```
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](https://crystal-lang.org/api/0.19.4/Enumerable.html#find%28if_none%3Dnil%2C%26block%29-instance-method) إما نصًا وإما 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)](https://crystal-lang.org/docs/syntax_and_semantics/enum.html) تجميع القيم المترابطة:

```
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](https://crystal-lang.org/docs/syntax_and_semantics/literals/tuple.html). تُعرَّف الـ tuples بالصيغة `{element1, element2,...}`، وهي ممتازة لتجميع القيم المترابطة مؤقتًا. وتُستخدم داخليًا أيضًا في الإسناد المتعدد مثل a, b = 1, 2، حيث تستخدم Ruby بدلًا منها Array.

# الخلاصة

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

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/ar/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
