التصنيف الفرعي

في نظرية لغات البرمجة ، يُعدّ التنميط الفرعي (ويُسمى أيضًا تعدد أشكال الأنواع الفرعية أو تعدد أشكال التضمين ) شكلاً من أشكال تعدد أشكال الأنواع . النوع الفرعي هو نوع بيانات يرتبط بنوع بيانات آخر ( النوع الأصلي ) من خلال مفهوم الاستبدال ، مما يعني أن عناصر البرنامج (عادةً ما تكون إجراءات فرعية أو دوال)، المكتوبة للعمل على عناصر النوع الأصلي، يمكنها أيضًا العمل على عناصر النوع الفرعي.

إذا كان S نوعًا فرعيًا من T، فإن علاقة النوع الفرعي (المكتوبة على النحو التالي : S < : T ، ST ، [ 1 ] أو S ≤ : T ) تعني أنه يمكن استخدام أي مصطلح من النوع S بأمان في أي سياق يُتوقع فيه استخدام مصطلح من النوع T. وتعتمد الدلالات الدقيقة للنوع الفرعي هنا بشكل حاسم على تفاصيل كيفية تعريف "الاستخدام الآمن" و "أي سياق" بواسطة نظام النوع أو لغة البرمجة . ويُعرّف نظام النوع في لغة البرمجة علاقة النوع الفرعي الخاصة به، والتي قد تكون بديهية ، إذا لم تدعم اللغة آليات تحويل (أو دعمت عددًا قليلًا جدًا منها).   

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

تسمح لغات البرمجة الوظيفية غالبًا بتصنيف السجلات إلى أنواع فرعية . ونتيجةً لذلك، يُعد حساب لامدا البسيط المُوسّع بأنواع السجلات ربما أبسط إطار نظري يُمكن من خلاله تعريف مفهوم مفيد للتصنيف الفرعي ودراسته. [ 2 ] ولأن الحساب الناتج يسمح للمصطلحات بأن يكون لها أكثر من نوع واحد، فإنه لم يعد نظرية أنواع "بسيطة" . وبما أن لغات البرمجة الوظيفية، بحكم تعريفها، تدعم الدوال الحرفية ، التي يُمكن تخزينها أيضًا في السجلات، فإن أنواع السجلات مع التصنيف الفرعي تُوفر بعض ميزات البرمجة كائنية التوجه. وعادةً ما تُوفر لغات البرمجة الوظيفية أيضًا شكلًا من أشكال تعدد الأشكال البارامتري، وعادةً ما يكون محدودًا. في الإطار النظري، من المستحسن دراسة تفاعل هاتين الميزتين؛ ويُعد النظام F < : إطارًا نظريًا شائعًا . ويمكن اشتقاق حسابات مختلفة تُحاول استيعاب الخصائص النظرية للبرمجة كائنية التوجه من النظام F < : .

يرتبط مفهوم التفرع الفرعي بالمفاهيم اللغوية للتضمين والشمولية . كما يرتبط بمفهوم التكميم المحدود في المنطق الرياضي (انظر: منطق الترتيب ). يجب عدم الخلط بين التفرع الفرعي ومفهوم وراثة (الفئة أو الكائن) في لغات البرمجة كائنية التوجه؛ [ 3 ] فالتفرع الفرعي هو علاقة بين الأنواع (الواجهات في مصطلحات البرمجة كائنية التوجه)، بينما الوراثة هي علاقة بين التطبيقات، وهي ناتجة عن ميزة لغوية تسمح بإنشاء كائنات جديدة من كائنات موجودة. في عدد من لغات البرمجة كائنية التوجه، يُطلق على التفرع الفرعي اسم وراثة الواجهة ، بينما تُعرف الوراثة باسم وراثة التطبيق .

الأصول

يعود مفهوم التفرع في لغات البرمجة إلى ستينيات القرن العشرين؛ حيث طُرح في مشتقات لغة سيمولا . وقدّم جون سي. رينولدز أولى المعالجات الرسمية للتفرع عام 1980، مستخدمًا نظرية الفئات لصياغة التحويلات الضمنية ، وكذلك لوكا كارديلي عام 1985. [ 4 ]

اكتسب مفهوم التفرع الفرعي شهرةً واسعة (وأصبح مرادفًا لتعدد الأشكال في بعض الأوساط) مع انتشار البرمجة كائنية التوجه. في هذا السياق، يُطلق على مبدأ الاستبدال الآمن غالبًا اسم مبدأ استبدال ليسكوف ، نسبةً إلى باربرا ليسكوف التي روّجت له في كلمة رئيسية ألقتها في مؤتمر حول البرمجة كائنية التوجه عام ١٩٨٧. ولأنه يجب أن يأخذ في الاعتبار الكائنات القابلة للتغيير، فإن المفهوم الأمثل للتفرع الفرعي الذي وضعته ليسكوف وجانيت وينغ ، والذي يُسمى التفرع الفرعي السلوكي ، أقوى بكثير مما يمكن تنفيذه في مدقق الأنواع . (انظر قسم  أنواع الدوال أدناه لمزيد من التفاصيل).

أمثلة

مثال على الأنواع الفرعية: حيث يكون الطائر هو النوع الرئيسي، وجميع الأنواع الأخرى هي أنواع فرعية كما هو موضح بالسهم في تدوين UML.

يُظهر الرسم التخطيطي مثالًا عمليًا بسيطًا للأنواع الفرعية. يحتوي النوع "طائر" على ثلاثة أنواع فرعية هي "بطة" و"وقواق" و"نعامة". من الناحية المفاهيمية، يُعد كل نوع فرعي من النوع الأساسي "طائر" امتدادًا للعديد من خصائص "الطائر"، ولكنه يتميز ببعض الاختلافات المحددة. يُستخدم في هذا الرسم التخطيطي ترميز UML ، حيث تُشير الأسهم المفتوحة إلى اتجاه ونوع العلاقة بين النوع الرئيسي وأنواعه الفرعية.

كمثال عملي أكثر، قد تسمح لغة برمجة باستخدام القيم الصحيحة في أي مكان يُتوقع فيه استخدام قيم الفاصلة العائمة ( Integer< Float:)، أو قد تُعرّف نوعًا عامًا.رقمكنوعٍ فائقٍ مشتركٍ للأعداد الصحيحة والأعداد الحقيقية. في هذه الحالة الثانية، لدينا فقط Integer<: Numberو Float<: Number، لكن Integerو Floatليسا نوعين فرعيين من بعضهما البعض.

قد يستفيد المبرمجون من التوريث لكتابة التعليمات البرمجية بطريقة أكثر تجريدًا مما هو ممكن بدونه. انظر المثال التالي:

دالة max ( x كرقم ، y كرقم ) هي: إذا كان x < y ، فأرجع y ، وإلا فأرجع x .

إذا كان كل من `integer` و`real` نوعين فرعيين من ` NumberNumber`، وتم تعريف عامل مقارنة مع أي عدد حقيقي لكلا النوعين، فإنه يمكن تمرير قيم من أي من النوعين إلى هذه الدالة. مع ذلك، فإن إمكانية تطبيق مثل هذا العامل تُقيّد نوع `Number` بشكل كبير (على سبيل المثال، لا يمكن مقارنة عدد صحيح بعدد مركب)، وفي الواقع، لا يُجدي نفعًا سوى مقارنة الأعداد الصحيحة مع الأعداد الصحيحة، والأعداد الحقيقية مع الأعداد الحقيقية. إعادة كتابة هذه الدالة بحيث تقبل فقط 'x' و'y' من نفس النوع يتطلب تعدد الأشكال المحدود .

يُمكّن التفرع من استبدال نوع مُحدد بنوع آخر أو بمفهوم تجريدي آخر. ويُقال إن التفرع يُنشئ علاقة "هو نوع" بين النوع الفرعي ومفهوم تجريدي موجود، إما ضمنيًا أو صراحةً، وذلك بحسب دعم اللغة. ويمكن التعبير عن هذه العلاقة صراحةً عبر الوراثة في اللغات التي تدعم الوراثة كآلية للتفرع.

لغة سي++

يقوم كود C++ التالي بإنشاء علاقة وراثية صريحة بين الفئتين B و A ، حيث B هي فئة فرعية ونوع فرعي من A ، ويمكن استخدامها كـ A أينما تم تحديد B (عبر مرجع أو مؤشر أو الكائن نفسه).

class A { public : void methodOfA () const { // ... } };class B : public A { public : void methodOfB () const { // ... } };void functionOnA ( const A & a ) { a . methodOfA (); }int main () { B b ; functionOnA ( b ); // يمكن استبدال b بـ A. }

[ 5 ]

بايثون

يقوم كود بايثون التالي بإنشاء علاقة وراثية صريحة بين الفئتين B و A ، حيث أن B هي فئة فرعية ونوع فرعي من A ، ويمكن استخدامها كـ A أينما كانت هناك حاجة إلى B.

class A : def method_of_a ( self ) -> None : passclass B ( A ): def method_of_b ( self ) -> None : passdef function_on_a ( a : A ) -> None : a . method_of_a ()if __name__ == "__main__" : b : B = B () function_on_a ( b ) # يمكن استبدال b بـ A.

في المثال التالي، يُعدّ `type(a) ` نوعًا "عاديًا"، بينما يُعدّ `type(type(a))` نوعًا فوقيًا. مع أن جميع الأنواع في التوزيع لها نفس النوع الفوقي ( PyType_Type ، وهو أيضًا نوع فوقي خاص به)، إلا أن هذا ليس شرطًا. يُمكن أيضًا اعتبار نوع الفئات الكلاسيكية، المعروف باسم `types.ClassType` ، نوعًا فوقيًا مميزًا. [ 6 ]

a = 0 print(type(a)) يطبع: <type 'int'> print(type(type(a))) يطبع: <type 'type'> print(type(type(type(a)))) يطبع: <type 'type'> print(type(type(type(type(a))))) يطبع: <type 'type'>

جافا

في لغة جافا، يتم تحديد علاقة "is-a" بين معلمات النوع لفئة أو واجهة ومعلمات النوع لفئة أو واجهة أخرى بواسطة عبارات extends و implements .

باستخدام Collectionsالفئات، ArrayList<E>تُنفذ List<E>وتُوسّع . لذا List<E>، فإنّ نوعًا فرعيًا من ، وهو نوع فرعي من . تُحفظ علاقة النوع الفرعي بين الأنواع تلقائيًا. عند تعريف واجهة، ، التي تربط قيمة اختيارية من النوع العام P بكل عنصر، قد يبدو تعريفها كما يلي:Collection<E>ArrayList<String>List<String>Collection<String>PayloadList

interface PayloadList < E , P > extends List <E> { void setPayload ( int index , P val ) ; ... }

تُعتبر المعاملات التالية لـ PayloadList أنواعًا فرعية من List<String>:

PayloadList < String , String > PayloadList < String , Integer > PayloadList < String , Exception >

الاستيعاب

في نظرية الأنواع ، يتم استخدام مفهوم التضمين [ 7 ] لتحديد أو تقييم ما إذا كان النوع S نوعًا فرعيًا من النوع T.

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

عند مناقشة مفهوم التضمين، يُشار إلى مجموعة قيم نوع ما بكتابة اسمه بخط مائل رياضي: T. ويُشار إلى النوع، عند النظر إليه كمسند على مجال، بكتابة اسمه بخط غامق: T. ويعني الرمز التقليدي <: "هو نوع فرعي من"، ويعني :> "هو نوع فائق من".

  • يستوعب النوع T المجموعة S إذا كانت مجموعة القيم T التي يحددها هي مجموعة فائقة للمجموعة S ، بحيث يكون كل عنصر من S هو أيضًا عنصر من T.
  • قد يندرج نوع واحد تحت أكثر من نوع واحد: تتقاطع الأنواع الفائقة لـ S عند S.
  • إذا كان S <: T (وبالتالي ST )، فإن T ، المسند الذي يحيط بالمجموعة T ، يجب أن يكون جزءًا من المسند S (على نفس المجال) الذي يحدد S.
  • إذا كان S يشمل T ، و T يشمل S ، فإن النوعين متساويان (على الرغم من أنهما قد لا يكونان من نفس النوع إذا كان نظام النوع يميز الأنواع بالاسم).

من حيث دقة المعلومات، يُعتبر النمط الفرعي أكثر تحديدًا من أي نمط رئيسي له، لأنه يحتوي على معلومات لا تقل عن تلك الموجودة في كل نمط رئيسي. قد يزيد هذا من قابلية تطبيق النمط الفرعي، أو أهميته (عدد الحالات التي يمكن قبوله أو استخدامه فيها)، مقارنةً بأنماطه الرئيسية "الأكثر عمومية". أما عيب امتلاك هذه المعلومات الأكثر تفصيلًا فهو أنها تمثل خيارات مُدمجة تُقلل من انتشار النمط الفرعي (عدد الحالات التي يمكن أن تُنتجه أو تُنشئه).

في سياق التضمين، يمكن التعبير عن تعريفات الأنواع باستخدام تدوين بناء المجموعات ، الذي يستخدم دالة منطقية لتعريف مجموعة. يمكن تعريف الدوال المنطقية على مجال (مجموعة من القيم الممكنة) D. الدوال المنطقية هي دوال جزئية تقارن القيم بمعايير الاختيار. على سبيل المثال: "هل قيمة عددية صحيحة أكبر من أو تساوي 100 وأصغر من 200؟". إذا تطابقت قيمة مع المعايير، فإن الدالة تُرجع تلك القيمة. وإذا لم تتطابق، فلا يتم اختيار القيمة، ولا يتم إرجاع أي شيء. (تُعدّ تراكيب القوائم شكلاً من أشكال هذا النمط المستخدم في العديد من لغات البرمجة).

إذا كان هناك مسندان،Pتي{\displaystyle P_{T}}والتي تطبق معايير الاختيار للنوع T ، وPs{\displaystyle P_{s}}والتي تطبق معايير إضافية للنوع S ، ثم يمكن تحديد مجموعات للنوعين:

تي={vد| Pتي(v)}{\displaystyle T=\{v\in D\mid \ P_{T}(v)\}}
S={vد| Pتي(v) و Ps(v)}{\displaystyle S=\{v\in D\mid \ P_{T}(v){\text{ and }}P_{s}(v)\}}

المسندتي=Pتي{\displaystyle \mathbf {T} =P_{T}}يتم تطبيقه جنبًا إلى جنبPs{\displaystyle P_{s}}كجزء من المسند المركب S الذي يُعرّف S. المسندان متصلان ، لذا يجب أن يكون كلاهما صحيحًا لاختيار قيمة. المسندS=تيPs=PتيPs{\displaystyle \mathbf {S} =\mathbf {T} \land P_{s}=P_{T}\land P_{s}}يشمل المسند T ، لذا S <: T.

على سبيل المثال: هناك فصيلة فرعية من أنواع القطط تسمى Felinae ، وهي جزء من عائلة Felidae . جنس Felis ، الذي ينتمي إليه نوع القطط المنزلية Felis catus ، هو جزء من تلك الفصيلة الفرعية.

Fهـلأنانأهـ={جأتFهـلأنادأهـ| oوSuبوأمأنالy(جأت،وهـلأنانأهـSuبوأمأنالyشمالأمهـ)}{\displaystyle {\mathit {Felinae=\{cat\in Felidae\mid \ ofSubfamily(cat,felinaeSubfamilyName)\}}}}
Fهـلأناs={جأتFهـلأنانأهـ| oوجيهـنus(جأت،وهـلأناsجيهـنusشمالأمهـ)}{\displaystyle {\mathit {Felis=\{cat\in Felinae\mid \ ofGenus(cat,felisGenusName)\}}}}

تم التعبير عن اقتران المسندات هنا من خلال تطبيق المسند الثاني على نطاق القيم المطابقة للمسند الأول. وبالنظر إلى الأنواع، فإن: Felis <: Felinae <: Felidae .

إذا كانت T تشمل S ( T  :> S )، فإن الإجراء أو الدالة أو التعبير الذي يُعطى قيمةsS{\displaystyle s\in S}وبالتالي، سيتمكن المعامل (قيمة المعامل أو الحد) من العمل على تلك القيمة كواحد من النوع T ، لأنsتي{\displaystyle s\in T}في المثال أعلاه، يمكننا أن نتوقع أن تكون وظيفة Subfamily قابلة للتطبيق على قيم جميع الأنواع الثلاثة Felidae و Felinae و Felis .

مخططات التصنيف الفرعي

يُفرّق علماء نظرية الأنواع بين التفرع الاسمي ، حيث لا تكون الأنواع المُعلنة بطريقة مُحددة إلا أنواعًا فرعية لبعضها البعض، والتفرع البنيوي ، حيث يُحدد هيكل نوعين ما إذا كان أحدهما نوعًا فرعيًا للآخر أم لا. يُعد التفرع الكائني التوجه القائم على الأصناف والموصوف أعلاه تفرعًا اسميًا؛ أما قاعدة التفرع البنيوي للغة كائنية التوجه فقد تنص على أنه إذا استطاعت كائنات من النوع A معالجة جميع الرسائل التي تستطيع كائنات من النوع B معالجتها (أي إذا كانت تُعرّف جميع الطرق نفسها )، فإن A نوع فرعي من B بغض النظر عما إذا كان أي منهما يرث من الآخر. يُعرف هذا النوع باسم " التفرع الديناميكي" وهو شائع في اللغات الكائنية التوجه ذات الأنواع الديناميكية. كما أن قواعد التفرع البنيوي السليمة للأنواع الأخرى غير أنواع الكائنات معروفة جيدًا.

تنقسم تطبيقات لغات البرمجة التي تستخدم التوريث إلى فئتين رئيسيتين: التطبيقات الشاملة ، حيث يمثل أي قيمة من النوع A نفس القيمة من النوع B إذا كان A < : B ، والتطبيقات القسرية ، حيث يمكن تحويل قيمة من النوع A تلقائيًا إلى قيمة من النوع B. عادةً ما يكون التوريث الناتج عن التوريث في لغة كائنية التوجه شاملاً؛ أما علاقات التوريث التي تربط الأعداد الصحيحة والأعداد العشرية، والتي تُمثل بشكل مختلف، فعادةً ما تكون قسرية.  

في معظم أنظمة الأنواع التي تُعرّف علاقة التفرع، تكون هذه العلاقة انعكاسية (بمعنى أن A < : A لأي نوع A ) ومتعدية (بمعنى أنه إذا كان A < : B و B < : C فإن A < : C ). وهذا يجعلها ترتيبًا جزئيًا على الأنواع.        

أنواع السجلات

تحديد الأنواع الفرعية للعرض والعمق

تُؤدي أنواع السجلات إلى ظهور مفهومي التنميط الفرعي للعرض والعمق . يُعبّر هذان المفهومان عن طريقتين مختلفتين للحصول على نوع جديد من السجلات يسمح بإجراء نفس العمليات التي يُجريها نوع السجل الأصلي.

تذكر أن السجل عبارة عن مجموعة من الحقول (المسماة). وبما أن النوع الفرعي هو نوع يسمح بجميع العمليات المسموح بها على النوع الأصلي، فيجب أن يدعم النوع الفرعي للسجل نفس العمليات على الحقول التي يدعمها النوع الأصلي.

إحدى طرق تحقيق هذا الدعم، والتي تُسمى "التصنيف الفرعي للعرض "، تُضيف حقولًا إضافية إلى السجل. وبشكل أدق، فإن كل حقل (مُسمى) يظهر في النوع الرئيسي للعرض سيظهر أيضًا في النوع الفرعي للعرض. وبالتالي، فإن أي عملية ممكنة على النوع الرئيسي ستكون مدعومة من قِبل النوع الفرعي.

الطريقة الثانية، المسماة بالتصنيف الفرعي العميق ، تستبدل الحقول المختلفة بأنواعها الفرعية. أي أن حقول النوع الفرعي هي أنواع فرعية لحقول النوع الأصلي. وبما أن أي عملية مدعومة لحقل في النوع الأصلي مدعومة أيضًا لنوعه الفرعي، فإن أي عملية ممكنة على النوع الأصلي للسجل مدعومة من قِبل النوع الفرعي للسجل. لا يُجدي التصنيف الفرعي العميق إلا مع السجلات غير القابلة للتغيير: على سبيل المثال، يمكنك تعيين القيمة 1.5 للحقل 'x' لنقطة حقيقية (سجل يحتوي على حقلين حقيقيين)، ولكن لا يمكنك فعل الشيء نفسه مع الحقل 'x' لنقطة عددية صحيحة (والتي، مع ذلك، هي نوع فرعي عميق من نوع النقطة الحقيقية) لأن 1.5 ليس عددًا صحيحًا (انظر التباين ).

يمكن تعريف التنميط الفرعي للسجلات في System F <: ، والذي يجمع بين تعدد الأشكال البارامتري والتنميط الفرعي لأنواع السجلات وهو أساس نظري للعديد من لغات البرمجة الوظيفية التي تدعم كلا الميزتين.

تدعم بعض الأنظمة أيضًا تصنيف أنواع الاتحاد المنفصلة المصنفة (مثل أنواع البيانات الجبرية ). وتنعكس قاعدة تصنيف العرض الفرعي: يجب أن يظهر كل وسم يظهر في نوع العرض الفرعي في نوع العرض الأصلي.

أنواع الدوال

إذا كان T1 T2 نوعًا داليًا ، فإن أي نوع داليّ S1S2 يحقق الخاصية T1 < : S1 و S2 <: T2 هو نوع فرعي منه . ويمكن تلخيص ذلك باستخدام قاعدة الكتابة التالية :

تي1≤ :S1S2≤ :تي2S1S2≤ :تي1تي2{\displaystyle {T_{1}\leq :S_{1}\quad S_{2}\leq :T_{2}} \over {S_{1}\rightarrow S_{2}\leq :T_{1}\rightarrow T_{2}}}

يُقال إن نوع المعامل في S1S2 متغير عكسيًا لأن علاقة التفرع فيه معكوسة، بينما نوع الإرجاع متغير مشتركًا . وبشكل غير رسمي، يحدث هذا الانعكاس لأن النوع المُحسَّن "أكثر تساهلًا " في الأنواع التي يقبلها و"أكثر تحفظًا" في النوع الذي يُرجعه. هذا ما يعمل بالضبط في Scala : الدالة ذات المعامل n هي داخليًا فئة ترثFuنجتأناoنشمال(-أ1،-أ2،...،-أن،+ب){\displaystyle {\mathtt {Function_{N}({-A_{1}},{-A_{2}},\dots ,{-A_{n}},{+B})}}}السمة (التي يمكن اعتبارها واجهة عامة في لغات شبيهة بلغة جافا )، حيثأ1،أ2،...،أن{\displaystyle {\mathtt {A_{1},A_{2},\dots ,A_{n}}}}وهي أنواع المعاملات، وب{\displaystyle {\mathtt {B}}}هو نوع الإرجاع الخاص به؛ "−" قبل النوع يعني أن النوع متغاير عكسيًا بينما "+" يعني متغايرًا مشتركًا.

في اللغات التي تسمح بالآثار الجانبية، كمعظم لغات البرمجة كائنية التوجه، لا يكفي التنميط الفرعي عادةً لضمان إمكانية استخدام دالة ما بأمان في سياق دالة أخرى. ركزت أعمال ليسكوف في هذا المجال على التنميط الفرعي السلوكي ، الذي يتطلب، بالإضافة إلى سلامة نظام الأنواع المذكورة في هذه المقالة، أن تحافظ الأنواع الفرعية على جميع الثوابت التي تضمنها الأنواع الأصلية في عقد ما . [ 8 ] هذا التعريف للتنميط الفرعي غير قابل للتقرير عمومًا ، لذا لا يمكن التحقق منه بواسطة مدقق الأنواع .

يُشابه تصنيف المراجع القابلة للتغيير تصنيف قيم المعاملات وقيم الإرجاع. المراجع المخصصة للكتابة فقط (أو المصادر ) متغيرة عكسيًا، مثل قيم المعاملات؛ والمراجع المخصصة للقراءة فقط (أو المصادر ) متغيرة توافقيًا، مثل قيم الإرجاع. أما المراجع القابلة للتغيير التي تعمل كمصادر ومصادر في آنٍ واحد، فهي ثابتة.

العلاقة بالميراث

التصنيف الفرعي والوراثة علاقتان مستقلتان (متعامدتان). قد تتطابقان، لكن لا تُعدّ أيٌّ منهما حالةً خاصةً من الأخرى. بعبارة أخرى، بين نوعين S و T ، جميع تركيبات التصنيف الفرعي والوراثة ممكنة.

  1. S ليس نوعًا فرعيًا ولا نوعًا مشتقًا من T
  2. S هو نوع فرعي ولكنه ليس نوعًا مشتقًا من T
  3. S ليس نوعًا فرعيًا ولكنه نوع مشتق من T
  4. S هو نوع فرعي ونوع مشتق من T

يتم توضيح الحالة الأولى من خلال الأنواع المستقلة، مثل Booleanو Float.

يمكن توضيح الحالة الثانية من خلال العلاقة بين Int32و Int64. في معظم لغات البرمجة كائنية التوجه، Int64لا ترتبط بـ عن طريق الوراثة Int32. ومع ذلك، Int32يمكن اعتبار نوعًا فرعيًا من Int64حيث يمكن تحويل أي قيمة عددية صحيحة 32 بت إلى قيمة عددية صحيحة 64 بت.

الحالة الثالثة هي نتيجة لخاصية التباين العكسي في إدخال الدوال الفرعية . لنفترض وجود فئة أساسية من النوع T تحتوي على دالة m تُرجع كائنًا من نفس النوع ( أي أن نوع m هو TT ، مع ملاحظة أن المعامل الأول لـ m هو this/self)، وفئة مشتقة S من T. بالوراثة، يكون نوع m في S هو SS. لكي يكون S نوعًا فرعيًا من يجب أن يكون نوع m في S نوعًا فرعيًا من نوع m في T ، أي: SSTT. بتطبيق قاعدة النوع الفرعي للدوال من الأسفل إلى الأعلى، هذا يعني: ST و TS ، وهو أمر ممكن فقط إذا كان S و T متطابقين. بما أن الوراثة علاقة غير انعكاسية، فلا يمكن أن يكون S نوعًا فرعيًا من T.

يكون التفرع والوراثة متوافقين عندما تكون جميع الحقول والأساليب الموروثة من النوع المشتق لها أنواع فرعية من الحقول والأساليب المقابلة من النوع الموروث. [ 3 ]

الإكراه

في أنظمة التصنيف الفرعي القسري، تُعرَّف الأنواع الفرعية بواسطة دوال تحويل صريحة من النوع الفرعي إلى النوع الرئيسي. لكل علاقة تصنيف فرعي ( S <: T )، تُقدَّم دالة إكراه coerce : S T ، ويُعتبر أي كائن s من النوع S كائنًا coerce ST ( s ) من النوع T. يمكن تعريف دالة الإكراه بالتركيب: إذا كان S <: T و T <: U ، فيمكن اعتبار s كائنًا من النوع u تحت الإكراه المركب ( coerce TUcoerce ST ). إكراه النوع من نوع إلى نفسه coerce T T هو دالة التطابق id T.

يمكن تعريف دوال الإكراه للسجلات وأنواع الاتحاد المنفصلة على مستوى المكونات؛ في حالة السجلات ذات العرض الممتد، يتجاهل إكراه النوع ببساطة أي مكونات غير مُعرَّفة في النوع الأصلي. يمكن التعبير عن إكراه النوع لأنواع الدوال بالصيغة f ' ( t ) = coerce S2T2 ( f ( coerce T1S1 ( t ) ))، مما يعكس التباين العكسي لقيم المعاملات والتباين المشترك لقيم الإرجاع.

تُحدد دالة التحويل بشكل فريد بناءً على النوع الفرعي والنوع الأصلي . لذا، عند تعريف علاقات أنواع فرعية متعددة، يجب الحرص على ضمان اتساق جميع تحويلات الأنواع. على سبيل المثال، إذا كان من الممكن تحويل عدد صحيح مثل 2  : int  إلى عدد عشري (مثلاً، 2.0 : float )، فلا يجوز تحويل 2.1  : float إلى 2  : int ، لأن التحويل المركب coerce floatfloat المُعطى بواسطة coerce intfloatcoerce floatint سيكون مختلفًا عن تحويل الهوية id float .

انظر أيضاً

ملحوظات

  1. كوبيستاك، آن. تطبيق قواعد بنية الميزات المكتوبة. المجلد 110. ستانفورد: منشورات CSLI، 2002.
  2. كارديلي، لوكا. دلالات التوريث المتعدد. في: جي. كان، دي. ماكوين، وجي. بلوتكين (محررون)، دلالات أنواع البيانات، المجلد 173 من سلسلة محاضرات في علوم الحاسوب، الصفحات 51-67. سبرينغر-فيرلاغ، 1984. النسخة الكاملة منشورة في مجلة المعلومات والحوسبة، 76(2/3):138-164، 1988.
  3. 1 2 كوك، هيل وكانينغ 1990 .
  4. بيرس، الفصل 15 ملاحظات
  5. ميتشل، جون (2002). "10 مفاهيم في لغات البرمجة الكائنية التوجه"مفاهيم في لغات البرمجة . كامبريدج، المملكة المتحدة: مطبعة جامعة كامبريدج. ص  287. ISBN 0-521-78098-5.
  6. غيدو فان روسوم. "التصنيف الفرعي للأنواع المضمنة" . تم الاطلاع عليه في 2 أكتوبر 2012 .
  7. بنجامين سي. بيرس، الأنواع ولغات البرمجة ، مطبعة معهد ماساتشوستس للتكنولوجيا، 2002، 15.1 "الاستيعاب"، ص 181-182
  8. باربرا ليسكوف، جانيت وينغ، مفهوم سلوكي للتصنيف الفرعي ، مجلة ACM للمعاملات في لغات البرمجة والأنظمة، المجلد 16، العدد 6 (نوفمبر 1994)، الصفحات 1811-1841. نُشرت نسخة مُحدثة كتقرير فني من جامعة كارنيجي ميلون: ليسكوف، باربرا ؛ وينغ، جانيت (يوليو 1999). "التصنيف الفرعي السلوكي باستخدام الثوابت والقيود" ( PS ) . تم الاطلاع عليه بتاريخ 5 أكتوبر 2006 .

مراجع

الكتب الدراسية

  • بنيامين سي. بيرس، أنواع لغات البرمجة ، مطبعة معهد ماساتشوستس للتكنولوجيا، 2002، رقم ISBN 0-262-16209-1، الفصل 15 (التصنيف الفرعي لأنواع السجلات)، 19.3 (الأنواع الاسمية مقابل الأنواع الهيكلية والتصنيف الفرعي)، و23.2 (أنواع تعدد الأشكال)
  • سي. شيبرسكي، دي. غرونتز، إس. مورر، برمجيات المكونات: ما وراء البرمجة الكائنية التوجه ، الطبعة الثانية، بيرسون إديوكيشن، 2002، رقم ISBN 0-201-74572-0، الصفحات  93-95 (عرض تقديمي عالي المستوى موجه لمستخدمي لغات البرمجة)

أوراق

كوك، ويليام ر.؛ هيل، والتر؛ كانينغ، بيتر س. (1990). الوراثة ليست تنويعًا فرعيًا . وقائع الندوة السابعة عشرة لجمعية ACM SIGPLAN-SIGACT حول مبادئ لغات البرمجة (POPL). الصفحات 125-135 . CiteSeerX 10.1.1.102.8635 . doi : 10.1145/96709.96721 . ISBN   0-89791-343-4.
  • رينولدز، جون سي. استخدام نظرية الفئات لتصميم التحويلات الضمنية والمعاملات العامة. في: إن دي جونز (محرر)، وقائع ورشة عمل آرهوس حول توليد المترجمات الموجهة بالدلالات، العدد 94 في سلسلة محاضرات في علوم الحاسوب. سبرينغر-فيرلاغ، يناير 1980. أيضًا في: كارل أ. غونتر وجون سي. ميتشل (محرران)، الجوانب النظرية للبرمجة الكائنية التوجه: الأنواع، والدلالات، وتصميم اللغة (مطبعة معهد ماساتشوستس للتكنولوجيا، 1994).

للمزيد من القراءة