سؤال

نوع الطبقات يبدو أن هناك احتمال كبير أن أكتب عامة قابلة لإعادة الاستخدام وظائف في مجموعة متسقة للغاية وفعالة للتوسعة.ولكن لا يزال لا "التيار اللغة" يوفر لهم - على العكس من ذلك: المفاهيم, التي هي تماما هاته الفكرة ، وقد استبعاد من C++!

ما هو المنطق ضد typeclasses?على ما يبدو العديد من اللغات تبحث عن طريقة للتعامل مع مشاكل مماثلة:.صافي أدخلت عامة القيود واجهات مثل IComparable والتي تسمح الوظائف مثل

T Max<T>(T a, T b) where T : IComparable<T> { // }

تعمل على جميع الأنواع التي تنفذ واجهة.

سكالا بدلا من ذلك يستخدم مزيج من الصفات و يسمى ضمني المعلمات/عرض حدود, التي تلقائيا تنتقل إلى المهام العامة.

ولكن كلا المفهومين هو موضح هنا كبيرة عيوب - واجهات الميراث على أساس وبالتالي بطيئة نسبيا بسبب المراوغة و علاوة على ذلك لا توجد إمكانية السماح نوع موجود تنفيذها.

إذا كنا في حاجة إلى التجريد عن مونويد يمكننا جيد جدا كتابة واجهة واسمحوا لنا أنواع تنفيذ هذا لكن مدمج أنواع مثل int لا يمكن أبدا أن تعمل على الوظائف أصلا.

ضمني المعلمات بدلا تتعارض مع العادية واجهات/الصفات.

مع نوع الطبقات ، لن يكون هناك مشكلة (pseudo-code)

typeclass Monoid of A where
    static operator (+) (x : A, y : A) : A
    static val Zero : A 
end

instance Int of Monoid where
   static operator (+) (x : Int, y : Int) : Int = x + y
   static val Zero : Int = 0
end

فلماذا لا نستخدم نوع الطبقات ؟ هل لديهم عيوب خطيرة بعد كل شيء ؟

تحرير:رجاء لا تخلط بين typeclasses مع الهيكلية الكتابة ، pure C++ قوالب أو بطة الكتابة.أ typeclass هو صراحة مثيل من أنواع وليس فقط راض حسب الاتفاقية.وعلاوة على ذلك فإنه يمكن أن تحمل تطبيقات مفيدة وليس مجرد تعريف واجهة.

هل كانت مفيدة؟

المحلول

واستبعدت المفاهيم لأن اللجنة لم اعتقد انه يمكن الحصول عليها الحق في الوقت المناسب، ولأنها لم تعتبر ضرورية لإطلاق سراحهم. انها ليست أنهم لا يعتقدون انهم فكرة جيدة، وانهم فقط لا أعتقد أن تعبير منهم عن C ++ ناضجة: <لأ href = "http://herbsutter.wordpress.com/2009/07/21 / رحلة / تقرير "يختلط =" نوفولو noreferrer "> http://herbsutter.wordpress.com/2009/07/21/trip-report/

وأنواع ساكنة في محاولة لمنع لكم تمرير كائن إلى وظيفة، وهذا لا تلبي متطلبات الوظيفة. في C ++ هذا هو صفقة كبيرة ضخمة، لأنه في الوقت الذي يتم الوصول إلى الكائن رمز، وليس هناك التحقق من أن هذا هو الشيء الصحيح.

والمفاهيم في محاولة لمنع لكم تمرير معلمة قالب، وهذا لا تلبي متطلبات القالب. ولكن في الوقت الذي يتم الوصول إلى المعلمة القالب من قبل المترجم، هناك بالفعل <م> هو التحقق من أن هذا هو الشيء الصحيح، حتى من دون المفاهيم. إذا حاولت استخدامها بطريقة أنها لا تدعم، وتحصل على خطأ مترجم [*]. في حالة من التعليمات البرمجية باستخدام القالب الثقيلة قد تحصل ثلاث شاشات كاملة من أقواس زاوية، ولكن من حيث المبدأ هذا هو رسالة إعلامية. الحاجة إلى التقاط الأخطاء قبل الترجمة لم أقل إلحاحا من الحاجة إلى التقاط الأخطاء قبل سلوك غير معرف في وقت التشغيل.

ومفاهيم تجعل من السهل تحديد واجهات القالب الذي سيعمل <م> عبر التجسيدات متعددة . وهذا أمر مهم، ولكن مشكلة أقل إلحاحا بكثير من تحديد واجهات وظيفة التي ستعمل عبر مكالمات متعددة.

في الإجابة على سؤالك - أي بيان رسمي "I تنفيذ هذه الواجهة" لديه عيب واحد كبير، وأنه يتطلب واجهة أن اخترع قبل التنفيذ. نظم الاستدلال نوع لا، ولكن لديهم عيب كبير أن اللغات بشكل عام لا يمكن التعبير عن كل من واجهة استخدام أنواع، وذلك قد يكون لديك كائن الذي يستدل أن يكون من النوع الصحيح، ولكنها لا تملك دلالات أرجع إلى هذا النوع. إذا كانت عناوين لغتك واجهات على الإطلاق (وبخاصة إذا كان يتطابق لهم الطبقات)، ثم AFAIK لديك لاتخاذ موقف هنا، واختيار العيب الخاص بك.

[*] عادة. وهناك بعض الاستثناءات، على سبيل المثال نظام نوع C ++ حاليا لا يمنعك من استخدام مكرر الإدخال كما لو كان مكرر إلى الأمام. تحتاج الصفات مكرر لذلك. بطة الكتابة وحدها لا يمنعك تمرير كائن الذي يمشي، وتسبح والدجالين، ولكن على تفتيش قريبة في الواقع لا تفعل أي من تلك الأشياء بالطريقة بطة لا، وهو دهش عندما علم ان كنت اعتقد انه سيكون ؛-)

نصائح أخرى

واجهات لا يلزم أن يكون راثة المستندة ... هذا قرار تصميم مختلف ومنفصل. وrel="nofollow الذهاب لغة لها واجهات، ولكن لم يكن لديك الميراث، على سبيل المثال: "نوع تلقائيا يرضي أي اجهة يحدد مجموعة فرعية من أساليبها "، حيث أن العودة التعليمات يضعه. Simionato في التأملات حول الميراث واجهات وشجعه على الإفراج العودة مؤخرا، قد تكون تستحق القراءة.

وأنا أتفق أن typeclasses، بل هي أكثر قوة، أساسا، لأنه مثل مجردة الفئات الأساسية ، أنها تتيح لك بالإضافة إلى تحديد كود المفيد (تحديد وسيلة X إضافية من حيث آخرين لجميع أنواع تطابق وإلا فإن baseclass ولكن لا تحدد X أنفسهم) - دون أمتعة الميراث أن أبجديات (بشكل مختلف من واجهات) تقريبا تحمل حتما. تقريبا حتما لأنه، على سبيل المثال، أبجديات بايثون "خيالية" أنها تنطوي على الميراث، من حيث المفاهيم التي تقدمها ... ولكن، في الواقع، انهم لا يلزم أن يكون الميراث على أساس (الكثير منها مجرد التحقق من وجود وتوقيع أساليب معينة، تماما مثل واجهات العودة ل).

وأما بالنسبة لماذا مصمم لغة (مثل غيدو، في حالة بايثون) اختيار مثل هذه "ذئاب في ثياب الحملان"، كما أبجديات بايثون، على مدى أكثر وضوحا هاسكل مثل typeclasses أنني اقترحت منذ ظهر في عام 2002، هذا سؤال من الصعب الإجابة. بعد كل شيء، فإنه ليس كما لو ديه بيثون أي تأنيب للضمير ضد المفاهيم الاقتراض من هاسكل (على سبيل المثال، القوائم المضمنة / عبارات مولد - بايثون يحتاج إلى ازدواجية هنا، في حين لا هاسكل لا، لأن هاسكل هو "كسول"). أفضل فرضية يمكنني أن أقدم هو أنه، حتى الآن، والميراث هو مألوف جدا لمعظم المبرمجين أن يشعر معظم المصممين اللغة التي يمكن الحصول أسهل قبول الصب الأمور بهذه الطريقة (على الرغم من المصممين العودة يجب أن يستحق الثناء لعدم القيام بذلك).

اسمحوا لي أن أبدأ جريئة:أنا أفهم تماما الدافع من بعد ذلك وما لا يمكن فهم الدافع من بعض الناس أن يجادل ضد ذلك...

ما تريده هو nonvirtual المخصصة تعدد الأشكال.

  • المخصص:تنفيذ يمكن أن تختلف
  • nonvirtual:الأداء الأسباب ؛ compiletime الإرسال

بقية السكر في رأيي.

C++ بالفعل المخصصة تعدد الأشكال عن طريق القوالب."المفاهيم" ومع ذلك من شأنه أن يوضح أي نوع من المخصص وظائف متعددة الأشكال المستخدمة من قبل أي مستخدم تعريف الكيان.

C# فقط لا يملك أي وسيلة للقيام بذلك. نهج لن تكون nonvirtual:إذا أنواع مثل تعويم فقط تنفيذ شيء من هذا القبيل "INumeric" أو "IAddable" (...) فإننا على الأقل أن تكون قادرة على كتابة عامة min, max, lerp وبناء على ذلك المشبك ، maprange, بيزيه (...).ومع ذلك لن تكون سريعة.كنت لا تريد أن.

طرق تحديد هذا:منذ .صافي لا جيت تجميع على أي حال أيضا يولد رمز مختلف عن List<int> من List<MyClass> (بسبب اختلاف قيمة وأنواع مرجع) ربما لن تضيف كثيرا من النفقات العامة لتوليد أيضا مختلفة الكود المخصص أجزاء متعددة الأشكال.لغة C# فقط بحاجة إلى وسيلة للتعبير عن ذلك. طريقة واحدة ما كنت رسمت فوق.

طريقة أخرى سيكون لإضافة نوع من القيود على وظيفة باستخدام مخصصة متعددة الأشكال الدالة:

    U SuperSquare<T, U>(T a) applying{ 
         nonvirtual operator (*) T (T, T) 
         nonvirtual Foo U (T)
    }
    {
        return Foo(a * a);
    }

بالطبع يمكن أن ينتهي بك الأمر مع المزيد والمزيد من القيود عند تنفيذ شريط يستخدم فو.لذلك قد ترغب في آلية لإعطاء اسم إلى العديد من المعوقات التي كنت تستخدم بانتظام...ولكن هذا هو مرة أخرى السكر طريقة واحدة النهج سيكون مجرد استخدام typeclass مفهوم...

إعطاء اسم لقيود عدة مثل تحديد نوع الطبقة ، ولكن أود أن مجرد إلقاء نظرة على ذلك كنوع من الاختصار آلية - السكر التعسفي مجموعة من وظيفة نوع من القيود:

    // adhoc is like an interface: it is about collecting signatures
    // but it is not a type: it dissolves during compilation 
    adhoc AMyNeeds<T, U>
    {
         nonvirtual operator (*) T (T, T) 
         nonvirtual Foo U (T)
    } 

    U SuperSquare<T, U>(T a) applying AMyNeeds<T, U>        
    {
        return Foo(a * a);
    }

في مكان ما "الرئيسية" كل نوع الحجج معروفة و يصبح كل شيء ملموس يمكن جمعها معا.

ما هو مفقود لا يزال هو عدم إنشاء تطبيقات مختلفة.في الجزء العلوي سبيل المثال نحن فقط تستخدم متعددة الأشكال والوظائف يعلم الجميع

تنفيذ ثم مرة أخرى يمكن أن تتبع طريقة من طرق الإرشاد في قدرتها على إضافة وظائف إلى أي فئة في أي لحظة:

 public static class SomeAdhocImplementations
 {
    public nonvirtual int Foo(float x)
    {
        return round(x);
    }
 }

في الرئيسية يمكنك الآن كتابة:

    int a = SuperSquare(3.0f); // 3.0 * 3.0 = 9.0 rounded should return 9

مترجم يتحقق كل "nonvirtual" المخصصة وظائف يجد كل المدمج في تعويم (*) المشغل ، int Foo (float) وبالتالي غير قادرة على ترجمة هذا الخط.

المخصصة تعدد الأشكال بالطبع يأتي مع الجانب السلبي أن لديك إلى إعادة ترجمة لكل وقت الترجمة النوع الصحيح تطبيقات على المدرج.وربما IL لا يدعم هذا إلى dll.ولكن ربما أنها تعمل على ذلك على أي حال...

لا أرى أي حاجة حقيقية instanciation من نوع الدرجة بناء.إذا كان أي شيء سوف تفشل في تجميع نحصل على أخطاء من القيود أو إذا كانت تلك boundled مع "مخصصا" codeclock رسالة الخطأ يمكن أن الحصول على أكثر قابلية للقراءة.

    MyColor a = SuperSquare(3.0f); 
    // error: There are no ad hoc implementations of AMyNeeds<float, MyColor> 
    // in particular there is no implementation for MyColor Foo(float)

ولكن بالطبع أيضا instanciation من نوع فئة / "مخصصا تعدد الأشكال واجهة" غير قابل للتحقيق.رسالة الخطأ ثم الدولة:"The AMyNeeds constraint of SuperSquare has not been matched. AMyNeeds is available as StandardNeeds : AMyNeeds<float, int> as defined in MyStandardLib".كما سيكون من الممكن وضع التنفيذ في فئة جنبا إلى جنب مع أساليب أخرى وإضافة "مخصصا واجهة" على قائمة دعم الواجهات.

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

tldr:أنا على الجانب الخاص بك.مثل هذه الأشياء تمتص في التيار ثابت كتابة اللغات.هاسكل تبين الطريق.

ما هو المنطق ضد typeclasses?

تنفيذ التعقيد بالنسبة مترجم الكتاب هو دائما مصدر قلق عند النظر في الميزات لغة جديدة.C++ بالفعل هذا الخطأ و لقد عانى سنوات من عربات التي تجرها الدواب C++ compilers نتيجة لذلك.

واجهات الميراث على أساس وبالتالي بطيئة نسبيا بسبب المراوغة و علاوة على ذلك لا توجد إمكانية السماح نوع موجود تنفيذها

ليس صحيحا.انظر OCaml هو هيكليا كتبته وجوه النظام ، على سبيل المثال:

# let foo obj = obj#bar;;
val foo : < bar : 'a; .. > -> 'a = <fun>

أن foo وظيفة يقبل أي شيء من أي نوع التي توفر اللازمة bar الأسلوب.

نفسه مل أعلى من أجل وحدة النظام.في الواقع, بل هناك الرسمية التكافؤ بين هذا النوع الطبقات.في الممارسة, نوع الطبقات هي الأفضل على نطاق صغير تجريدات مثل عامل الحمولة الزائدة في حين العليا وحدات هي الأفضل على نطاق واسع تجريدات مثل Okasaki هو والثوابت من catenable القوائم على قوائم الانتظار.

هل لديهم عيوب خطيرة بعد كل شيء ؟

انظر الخاصة بك على سبيل المثال ، عام الحسابية.F# يمكن بالفعل التعامل مع هذه الحالة الخاصة بفضل INumeric واجهة.F# Matrix نوع يستخدم هذا النهج.

ومع ذلك, لقد استبدال رمز الجهاز لإضافة مع الدينامية الإرسالية إلى وظيفة منفصلة ، مما يجعل الحساب أوامر من حجم أبطأ.بالنسبة لمعظم التطبيقات التي يتم دون جدوى بطيئة.يمكنك حل هذه المشكلة عن طريق أداء البرنامج كله التحسينات ولكن هذا له عيوب واضحة.علاوة على ذلك, هناك القليل من القواسم المشتركة بين الطرق العددية int مقابل float بسبب العددية متانة حتى التجريد هو أيضا غير مجدية عمليا.

السؤال يجب أن يكون بالتأكيد:يمكن لأي شخص جعل حجة مقنعة بالنسبة اعتماد نوع الطبقات ؟

ولكن لا يزال هناك "التيار اللغة" يوفر [نوع الطبقات.]

عندما سئل هذا السؤال, هذا قد يكون صحيحا.اليوم ، هناك أقوى بكثير من الاهتمام في لغات مثل هاسكل و Clojure.هاسكل نوع الطبقات (class / instance) ، Clojure 1.2+ لديه البروتوكولات (defprotocol / extend).

ما هو المنطق ضد [نوع الطبقات]?

أنا لا أعتقد أن نوع الطبقات بموضوعية "أسوأ" من غيرها من الأشكال ؛ أنها مجرد اتباع نهج مختلف.لذا فإن السؤال الحقيقي هو هل أنها تناسب بشكل جيد في لغة برمجة معينة ؟

دعونا ننظر بإيجاز كيف اكتب فئات مختلفة من واجهات في لغات مثل جافا أو C#.في هذه اللغات ، فئة فقط يدعم واجهات صراحة و تنفيذها في هذا الفصل التعريف.نوع الطبقات ، مع ذلك ، هي الواجهات التي يمكن في وقت لاحق إلحاق أي نوع معرف من قبل ، حتى في وحدة أخرى.هذا النوع من نوع التمدد الواضح مختلفة تماما عن آليات معينة "التيار" OO اللغات.


الآن دعونا النظر في نوع الطبقات لعدة لغات البرمجة التيار.

هاسكل:لا حاجة إلى القول بأن هذه اللغة قد نوع الطبقات.

Clojure:كما ذكر أعلاه, Clojure لديه شيء مثل نوع الطبقات في شكل البروتوكولات.

C++:كما قلت بنفسك ، المفاهيم أسقطت من C++11 المواصفات.

على العكس من ذلك:المفاهيم التي هي تماما هاته الفكرة استبعدت من التالي C++!

لم يتبع الجدل حول هذا القرار.من ما قرأت, مفاهيم لم تكن "مستعدة بعد":لا يزال هناك نقاش حول مفهوم الخرائط.بيد أن المفاهيم لم يتخل تماما, ومن المتوقع أنها سوف تجعل في النسخة المقبلة من C++.

C#:مع إصدار اللغة 3, C# يصبح أساسا الهجين من وجوه المنحى البرمجة الوظيفية النماذج.واحد إضافة إلى اللغة التي هي من الناحية النظرية مماثلة تماما نوع فصول: أساليب الإرشاد.والفرق الرئيسي هو أنك (على ما يبدو) ربط أساليب جديدة نوع موجود ، وليس الواجهات.

(منح ، طريقة تمديد آلية لا يكاد أنيقة مثل هاسكل instance … where بناء الجملة.امتداد الطرق ليست "حقا" تعلق على نوع تنفيذها كما النحوية التحول.في نهاية المطاف ولكن هذا لا يجعل كبير جدا فرق عملي.)

أنا لا أعتقد أن هذا سيحدث في وقت قريب — لغة المصممين ربما حتى لن تضيف امتداد خصائص إلى اللغة, امتداد واجهات حتى تذهب خطوة أبعد من ذلك.

(VB.NET:وكانت مايكروسوفت "المشارك المتطورة" C# ، VB.NET لغة لبعض الوقت ، حتى تصريحاتي عن C# يحدث أن تكون صالحة VB.NET أيضا.)

جافا:أنا لا أعرف جافا بشكل جيد جدا, ولكن من اللغات C++, C# و Java, هو على الأرجح "أنقى" OO اللغة.أنا لا أرى كيف اكتب فصول من شأنها أن تناسب بشكل طبيعي في هذه اللغة.

F#:لقد وجدت منتدى آخر شرح لماذا اكتب دروس قد لا تحصل أدخلت F#.هذا التفسير تدور حول حقيقة أن F# لديها الاسمي ، لا الهيكلي نوع النظام.(على الرغم من أنني لست متأكدا ما إذا كان هذا سببا كافيا F# ليس لديهم نوع الطبقات.)

وحاول تحديد Matroid، وهو ما نقوم به (logcally ولا أقول Matroid شفويا)، وانها لا تزال شيء من المرجح مثل البنية C. Liskov مبدأ (وأحدث الميدالية تورينج) يحصل مجردة جدا، قاطعة جدا، النظريه جدا، أقل التخليص الفعلية البيانات وأكثر نقاء النظري الطبقة النظام، لhandson حل مشكلات واقعية، واتجهت لفترة وجيزة والذي بدا وكأنه PROLOG، رمز حول قانون حول مدونة حول قانون ... في حين يصف خوارزمية تسلسل وtravelsals نفهم على الورق أو السبورة. يعتمد الذي هدف لديك، وحل مشكلة مع رمز الحد الأدنى أو معظم مجردة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top