الطباعة التدريجية
الكتابة التدريجية هي نظام أنواع يسمح بالكتابة الثابتة والكتابة الديناميكية ، ولكنه يفرض الأنواع أثناء التشغيل . تُمكّن الكتابة التدريجية مطوري البرامج من اختيار أي من نموذجي الكتابة المناسبين، من داخل لغة واحدة. [ 1 ]
في كثير من الحالات، تُضاف الكتابة التدريجية إلى لغة ديناميكية موجودة، [ 2 ] مما يُنشئ لغة مشتقة تسمح باستخدام الكتابة الثابتة ، ولكنها لا تُلزم بها . في بعض الحالات، تستخدم اللغة الكتابة التدريجية منذ البداية. ومع ذلك، توجد مفاضلات في الأداء وتعقيد التنفيذ مقارنةً بالكتابة الديناميكية أو الاختيارية.
تاريخ
صاغ هذا المصطلح جيريمي سيك، الذي طور أسلوب الكتابة التدريجي في عام 2006 مع وليد طه. [ 3 ]
تطبيق
في التنميط التدريجي، يُستخدم نوع خاص يُسمى "ديناميكي" لتمثيل الأنواع غير المعروفة ثابتًا. ويُستبدل مفهوم تساوي الأنواع بعلاقة جديدة تُسمى " الاتساق" تربط النوع الديناميكي بكل نوع آخر. علاقة الاتساق انعكاسية ومتناظرة، ولكنها ليست متعدية. [ 4 ]
سعت المحاولات السابقة لدمج الكتابة الثابتة والديناميكية إلى جعل النوع الديناميكي هو أعلى وأسفل التسلسل الهرمي للأنواع الفرعية. إلا أنه نظرًا لأن الكتابة الفرعية عملية متعدية، فإن ذلك يؤدي إلى ارتباط كل نوع بجميع الأنواع الأخرى، وبالتالي لن تستبعد الكتابة الفرعية أي أخطاء في النوع الثابت. ولم تُحل هذه المشكلة تمامًا بإضافة مرحلة ثانية للتحقق من المعقولية إلى نظام الأنواع. [ 5 ] [ 6 ]
يمكن دمج الكتابة التدريجية بسهولة في نظام أنواع لغة كائنية التوجه تستخدم بالفعل قاعدة التضمين للسماح بالتحويلات الضمنية فيما يتعلق بالأنواع الفرعية. الفكرة الأساسية هي أن الاتساق والأنواع الفرعية مفهومان مستقلان يتكاملان بشكل جيد. لإضافة الأنواع الفرعية إلى لغة ذات كتابة تدريجية، أضف ببساطة قاعدة التضمين وقاعدة أنواع فرعية تجعل النوع الديناميكي نوعًا فرعيًا من نفسه، لأن الأنواع الفرعية يفترض أن تكون انعكاسية. (لكن لا تجعل أعلى ترتيب الأنواع الفرعية ديناميكيًا!) [ 7 ]
مقارنة مع الأنواع الاختيارية
يتشابه كل من التحديد التدريجي والتحديد الاختياري للأنواع في إمكانية تحديد أنواع المتغيرات والتعبيرات، والتحقق من صحة هذا التحديد أثناء الترجمة (وهو ما يُعرف بالتحديد الثابت للأنواع ). مع ذلك، في نظام التحديد التدريجي، تُضاف عمليات التحقق أثناء التشغيل عند حدود الكود المحدد النوع وغير المحدد النوع. أما في نظام التحديد الاختياري، فتُحذف جميع الأنواع أثناء الترجمة، ويمكن للقيم غير المحددة النوع أن تنتشر عبر الكود الذي كان يحتوي على تعريفات للأنواع. [ 8 ]
فئة الحساب {الرصيد: عدد صحيح // عدد الرصيد في الحساب، نوع ثابتجديد ( الائتمانات: عدد صحيح ) {this.credits = credits ;}قارن ( الحساب: الحساب ) {لن يظهر خطأ في حالة تحديد النوع "1" اختياريًا، حيث أن المقارنة "1" صحيحة.أعد هذا الرصيد > رصيد الحساب ؛}إنفاق الرصيد (){this.credits = this.credits - 1 ; // يتم تحديد نوع الاستثناءات هنا ( اختياري )}}//افترض أن كل ما يلي يتم تنفيذه أثناء التشغيل/بعد تجميع الحسابlet a = new Account ( 1 ); // صالحة في كلا النظامينlet b = new Account ( "2" ); // يتم طرح استثناءات الكتابة التدريجية هنا، لمنع إنشاء مثيلات غير صالحة من نوع Accountb.compare ( a ); // تُرجع القيمة false ( اختياريًا )ب . أنفق الرصيد (); // يتم تحديد نوع الخطأ هنا اختياريًافي لغة ذات كتابة تدريجية، يُثار استثناء عند إدخال قيمة غير عددية credit، وتتوقف عمليات تتبع المكدس عند الاستدعاءات غير الصالحة new Account("2"). أما في لغة ذات كتابة اختيارية، فلا يُثار استثناء إلا عند إجبار اللغة على تنفيذ عمليات غير متوافقة مع نوعها: creditيمكن تعيين قيمة إلى "1" ( سلسلة نصية )، ولكن هذا لا يُثير خطأً إلا عند إجراء عمليات حسابية عليها (مثل spendCreditمحاولة طرح العدد الصحيح 1). compareتعمل الدوال بشكل طبيعي، مما يُوضح كيف يمكن لبرنامج يعمل في بيئة إنتاجية إنشاء ومعالجة قيم غير صالحة Accountsدون التسبب في خطأ. قد يؤدي هذا إلى تلف البيانات إذا تم حفظ هذه القيم غير الصالحة في مخزن البيانات.
يتطلب الحصول على نتائج مماثلة في لغة ذات كتابة اختيارية من المبرمج إجراء فحص يدوي للأنواع عن طريق إدراج حواجز:
فئة الحساب {الائتمان الخاص : عدد صحيح // يمنع الخاص البرامج الخارجية من تعيين قيمة (غير صالحة)جديد ( الائتمان: عدد صحيح ) {this.setCredit ( credit ) ; // يجب أن يمر عبر مُعدِّل محمي}// دالة تعيين لضمان تعيين القيم من النوع الصحيح فقطsetCredit ( credit: int ){إذا كان ( الائتمان عددًا صحيحًا ) { // فحص يدوي للنوع للحماية من تلف البياناتهذا الائتمان = الائتمان ؛} آخر {throw TypeError ;}}قارن ( الحساب: الحساب ) { // يتم حذف الأنواع في وقت الترجمةif ( instanceOf ( account , Account )){ // فحص يدوي للأنواع في كل مكان!أعد هذا الرصيد > رصيد الحساب ؛} آخر {throw TypeError ;}}//بما أن حقل `credits` الخاص لا يمكن الوصول إليه إلا داخل الفئة،//يمكن أن تعتمد الطرق على فحص النوع في وقت الترجمة.إنفاق الرصيد (){this.credits = this.credits - 1 ; // لا حاجة إلى `isInt( this.credits ) `}}//افترض أن كل ما يلي يتم تنفيذه أثناء التشغيل/بعد تجميع الحسابlet a = new Account ( 1 ); // صحيحlet b = new Account ( "2" ); // لن يتوقف تتبع الأخطاء هنا، بل داخل `setCredit`مع ذلك، تجدر الإشارة إلى أن تتبعات المكدس لن تُشير إلى الاستدعاء المُسبب للمشكلة، بل إلى دالة التعيين (setter). إذا نسي المبرمج تمرير القيم المُقدمة خارجيًا عبر دالة التعيين، فمن المُحتمل أن يتم إنشاء نسخة غير صالحة وحفظها على القرص، ولكن يتم اكتشافها أثناء تنفيذ لاحق. هذا يجعل منع تلف البيانات أكثر صعوبة، ويعيق تتبع مسارات التعليمات البرمجية المُسببة للمشكلة.
التطبيقات
من أمثلة اللغات ذات الكتابة التدريجية المشتقة من لغات ذات كتابة ديناميكية موجودة: Closure Compiler ، و Hack (لغة PHP)، وPHP (منذ الإصدار 7.0 [ 9 ] )، وTyped Racket (لغة Racket [ 10 ] [ 11 ] )، وTyped Clojure (لغة Clojure ) [ 12 ] ، و cperl (لغة Perl 5 ذات كتابة ). كانت ActionScript لغة ذات كتابة تدريجية [ 13 ] تتوافق مع ECMAScript ، على الرغم من أنها نشأت في الأصل بشكل منفصل كلغة شقيقة، وكلاهما متأثر بـ HyperTalk من Apple .
تم تطوير نظام للغة البرمجة J ، [ 14 ] إضافة الإكراه، ونشر الأخطاء، والتصفية إلى خصائص التحقق العادية لنظام النوع بالإضافة إلى تطبيق وظائف النوع خارج تعريفات الوظائف، وبالتالي زيادة مرونة تعريفات النوع.
على النقيض من ذلك، بدأت لغة C# كلغة ذات كتابة ثابتة، ولكن اعتبارًا من الإصدار 4.0 أصبحت ذات كتابة تدريجية، مما يسمح بتحديد المتغيرات بشكل صريح على أنها ديناميكية باستخدام dynamicالنوع. [ 15 ]
تم تطبيق نظام الكتابة التدريجي في لغة Raku (المعروفة سابقًا باسم Perl6) منذ البداية. وتُجرى فحوصات النوع في جميع المواضع التي تُسند فيها القيم أو تُربط. يُعرّف المتغير أو المعامل "غير المُحدد النوع" على أنه Any، وهو ما يتطابق (تقريبًا) مع جميع القيم. يُشير المُصرّف إلى تعارضات فحص النوع أثناء وقت الترجمة إذا تمكن من تحديد أنها لن تنجح أبدًا.
تعتمد لغة Objective-C على التحديد التدريجي لأنواع مؤشرات الكائنات فيما يتعلق باستدعاءات الدوال. يُستخدم التحديد الثابت للأنواع عندما يُعرَّف متغير كمؤشر إلى فئة معينة من الكائنات: فعند استدعاء دالة على هذا المتغير، يتحقق المترجم بشكل ثابت من أن الفئة مُعلنة لدعم هذه الدالة، وإلا فإنه يُصدر تحذيرًا أو خطأً. مع ذلك، إذا idاستُخدم متغير من النوع المحدد، فسيسمح المترجم باستدعاء أي دالة عليه.
لغة البرمجة JS ++ ، التي صدرت عام 2011، هي مجموعة فرعية من لغة جافا سكريبت (ذات كتابة ديناميكية) بنظام كتابة تدريجي مناسب لحالات ECMAScript و DOM API الخاصة. [ 16 ]
بدأت لغة GDScript ، وهي لغة البرمجة النصية الأساسية لمحرك الألعاب Godot، بنظام كتابة ديناميكي، ثم أُدخلت الكتابة التدريجية مع الإصدار 3.1 من المحرك، [ 17 ] مما يسمح للمستخدمين بتحديد أنواع المتغيرات ومعاملات الدوال والمصفوفات والقواميس من خلال تلميحات الأنواع. بالإضافة إلى ذلك، عند إسناد قيمة إلى متغير باستخدام الترميز :=، سيحاول Godot استنتاج نوع المتغير أثناء عملية البناء. [ 18 ]
مراجع
- ↑ سيك، جيريمي (24 مارس 2014). "ما هو الكتابة التدريجية؟" .
- ↑ براخا، جلعاد (2004). "أنظمة الأنواع القابلة للتوصيل" . ورشة عمل OOPSLA'04 حول إحياء اللغات الديناميكية .
- ↑ ديفيد هيرمان (21 أكتوبر 2011). "الكتابة التدريجية الموفرة للمساحة" (ملف PDF) .
- ↑ سيك، جيريمي؛ طه، وليد (سبتمبر 2006). الكتابة التدريجية للغات الوظيفية (ملف PDF) . سكيم والبرمجة الوظيفية 2006. جامعة شيكاغو . الصفحات 81-92 .
- ↑ ثات، ساتيش (1990). "الكتابة شبه الثابتة". وقائع الندوة السابعة عشرة لجمعية ACM SIGPLAN-SIGACT حول مبادئ لغات البرمجة - POPL '90 . ACM . الصفحات 367-381 . doi : 10.1145/96709.96747 . ISBN 978-0897913430. S2CID 8725290 .
- ↑ أوليارت، ألبرتو (1994). خوارزمية لاستنتاج الأنواع شبه الثابتة (تقرير فني). جامعة بوسطن. 1994-013.
- ↑ سيك، جيريمي؛ طه، وليد (أغسطس 2007). "الكتابة التدريجية للكائنات". ECOOP 2007 - البرمجة الكائنية التوجه . سلسلة محاضرات في علوم الحاسوب. المجلد 4609. سبرينغر . الصفحات 2-27 . doi : 10.1007/978-3-540-73589-2_2 . ISBN 978-3-540-73588-5.
- ↑ سيك، جيريمي ج.؛ فيتوسيك، مايكل م.؛ سيميني، ماتيو؛ بويلاند، جون تانغ (2015). بول، توماس؛ بوديك، راستيسلاف؛ كريشنامورثي، شري رام؛ ليرنر، بنجامين س.؛ موريسيت، جريج (محررون). "معايير مُحسَّنة للتصنيف التدريجي" . LIPIcs، المجلد 32، SNAPL 2015. 32 : 274-293 . doi : 10.4230 /LIPICS.SNAPL.2015.274 . ISSN 1868-8969 .
- ↑ "PHP: وسائط الدوال - الدليل » الكتابة الصارمة" .
- ↑ توبين-هوششتات، سام؛ فيليسين، ماتياس. "هجرة اللغات الوسيطة: من النصوص البرمجية إلى البرامج" . وقائع ندوة أنظمة البرمجة الكائنية التوجه، المجلد المصاحب . بورتلاند، أوريغون. Tobin-Hochstadt06 . تاريخ الاسترجاع: 2020-11-06 .
- ↑ توبين-هوششتات، سام؛ فيليسين، ماتياس. "تصميم وتنفيذ لغة سكيم المكتوبة" . وقائع مؤتمر مبادئ لغات البرمجة . سان دييغو، كاليفورنيا. توبين-هوششتات 08. تاريخ الاسترجاع: 2020-11-06 .
- ↑ تشاس إيمريك. "دليل مستخدم كلوجر المكتوب" . جيت هاب .
- ↑ أسيم راستوجي؛ أفيك تشودري؛ باسل هوسمر (يناير 2012). "مبادئ الاستدلال التدريجي للأنواع" (ملف PDF) . رابطة آلات الحوسبة (ACM). مؤرشف من الأصل (ملف PDF) بتاريخ 12 أغسطس 2017. تم الاطلاع عليه بتاريخ 23 سبتمبر 2014 .
- ↑ "type-system-j" . GitHub .
- ↑ "dynamic (C# Reference)" . مكتبة MSDN . مايكروسوفت . تم الاسترجاع في 14 يناير 2014 .
- ↑ "نظام أنواع JS++، الملحق ب: المشاكل (لماذا كان حل هذه المشكلة صعبًا؟)" . تم الاطلاع عليه بتاريخ 10 فبراير 2020 .
- ↑ "الكتابة الثابتة في GDScript" . وثائق محرك Godot . تم الاطلاع عليه بتاريخ 25-10-2025 .
- ↑ "مرجع GDScript" . وثائق محرك Godot . تم الاطلاع عليه بتاريخ 25-10-2025 .
للمزيد من القراءة
- سيك، جيريمي G.؛ فيتوسك، مايكل م. سيميني، ماتيو؛ بويلاند ، جون تانغ (2015). الكرة، توماس. بوديك، راستيسلاف؛ كريشنامورثي، شريرام؛ ليرنر، بنيامين س. موريسيت، جريج (محرران). معايير محسنة للكتابة التدريجية . إجراءات لايبنيز الدولية في المعلوماتية. المجلد. 32. داغستوهل، ألمانيا: شلوس داغستوهل – لايبنتز-سينتروم فور إنفورماتيك. الصفحات من 274 إلى 293. دوى : 10.4230/lipics.snapl.2015.274 . رقم ISBN 9783939897804. S2CID 15383644 .
- أنظمة الكتابة
