Go: الكل: دعم الإصلاح التدريجي للكود أثناء نقل نوع بين الحزم

تم إنشاؤها على ١ ديسمبر ٢٠١٦  ·  225تعليقات  ·  مصدر: golang/go

العنوان الأصلي: اقتراح: دعم إصلاح الكود التدريجي أثناء نقل نوع بين الحزم

يجب أن يضيف Go القدرة على إنشاء أسماء مكافئة بديلة للأنواع ، من أجل تمكين الإصلاح التدريجي للكود أثناء إعادة بناء قاعدة التعليمات البرمجية. كان هذا هو الهدف من ميزة الاسم المستعار Go 1.8 ، المقترحة في # 16339 ولكن تم إيقافها من Go 1.8. نظرًا لأننا لم نحل المشكلة لـ Go 1.8 ، فإنها تظل مشكلة ، وآمل أن نتمكن من حلها لـ Go 1.9.

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

هذه المسألة _لا_ تقترح حلاً محددًا. بدلاً من ذلك ، أرغب في جمع التعليقات من مجتمع Go حول مساحة الحلول الممكنة. أحد السبل الممكنة هو قصر الأسماء المستعارة على الأنواع ، كما هو مذكور في نهاية المقالة. قد يكون هناك آخرون يجب أن نأخذ في الاعتبار أيضًا.

يرجى نشر الأفكار حول الأسماء المستعارة أو الحلول الأخرى كتعليقات هنا.

شكرا لك.

التحديث، 16 ديسمبر: تصميم وثيقة لنوع الأسماء المستعارة نشرها .
تحديث ، 9 كانون الثاني (يناير) : تم قبول الاقتراح ، وإنشاء مستودع dev.typealias ، ومن المقرر التنفيذ في بداية دورة Go 1.9 للتجريب.


ملخص المناقشة (آخر تحديث 2017/02/02)

هل نتوقع أن نحتاج إلى حل عام يصلح لجميع الإعلانات؟

إذا كانت الأسماء المستعارة للنوع ضرورية بنسبة 100٪ ، فربما تكون الأسماء المستعارة من var ضرورية بنسبة 10٪ ، والأسماء المستعارة الوظيفية ضرورية بنسبة 1٪ ، والأسماء المستعارة الثابتة بنسبة 0٪ ضرورية. نظرًا لأن const تحتوي بالفعل على = ويمكن أن تستخدم func بشكل معقول = أيضًا ، فإن السؤال الرئيسي هو ما إذا كانت الأسماء المستعارة لـ var مهمة بما يكفي للتخطيط لها أو تنفيذها.

كما قال rogpeppe (https://github.com/golang/go/issues/16339#issuecomment-258771806) و ianlancetaylor (https://github.com/golang/go/issues/16339#issuecomment-233644777) في اقتراح الاسم المستعار الأصلي وكما هو مذكور في المقالة ، عادةً ما يكون تغيير var العام خطأً. ربما لا يكون من المنطقي تعقيد الحل لاستيعاب ما هو عادة خطأ. (في الواقع ، إذا تمكنا من معرفة كيفية القيام بذلك ، فلن يفاجئني إذا تحركت على المدى الطويل نحو المطالبة بأن تكون vars العالمية غير قابلة للتغيير).

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

هل نحتاج إلى بناء جملة جديد (= مقابل => مقابل تصدير)؟

أقوى حجة لبناء الجملة الجديد هي الحاجة إلى دعم الأسماء المستعارة لـ var ، إما الآن أو في المستقبل (https://github.com/golang/go/issues/18130#issuecomment-264232763 بواسطةMerovius). يبدو أنه من المقبول التخطيط لعدم وجود أسماء مستعارة مختلفة (انظر القسم السابق).

بدون الأسماء المستعارة لـ var ، تكون إعادة استخدام = أبسط من إدخال بناء جملة جديد ، سواء كان الأمر => كما هو الحال في اقتراح الاسم المستعار ، ~ (https://github.com/golang/go/issues/18130#issuecomment-264185142 بواسطةjoegrasse) ، أو التصدير (https://github.com/golang/go/issues/18130#issuecomment-264152427 بواسطةcznic).

كما أن استخدام = in يطابق تمامًا بناء جملة الأسماء المستعارة للنوع في باسكال وراست. بقدر ما تحتوي اللغات الأخرى على نفس المفاهيم ، من الجيد استخدام نفس الصيغة.

بالنظر إلى المستقبل ، يمكن أن يكون هناك Go في المستقبل توجد فيه الأسماء المستعارة func أيضًا (انظر https://github.com/golang/go/issues/18130#issuecomment-264324306 بواسطةnigeltao) ، وبعد ذلك ستسمح جميع الإعلانات بنفس النموذج :

const C2 = C1
func F2 = F1
type T2 = T1
var V2 = V1

الوحيد من هؤلاء الذي لن يؤسس اسمًا مستعارًا حقيقيًا هو تصريح var ، لأنه يمكن إعادة تعريف V2 و V1 بشكل مستقل أثناء تنفيذ البرنامج (على عكس إعلانات const و func و type التي لا يمكن تغييرها). نظرًا لأن أحد الأسباب الرئيسية للمتغيرات هو السماح لها بالتنوع ، فسيكون من السهل على الأقل شرح هذا الاستثناء. إذا تحرك Go نحو متغيرات عالمية ثابتة ، فسيختفي هذا الاستثناء.

لأكون واضحًا ، أنا لا أقترح هنا أسماء مستعارة وظيفية أو متغيرات عالمية ثابتة ، فقط أعمل من خلال الآثار المترتبة على مثل هذه الإضافات المستقبلية.

اقترح jimmyfrasche (https://github.com/golang/go/issues/18130#issuecomment-264278398) أسماء مستعارة لكل شيء ما عدا consts ، لذلك سيكون const ستكون الاستثناء بدلاً من var:

const C2 = C1 // no => form
func F2 => F1
type T2 => T1
var V2 => V1
var V2 = V1 // different from => form

يبدو أن وجود تناقضات مع كل من const و var يبدو أكثر صعوبة في التفسير من مجرد وجود تناقض في var.

هل يمكن أن يكون هذا تغييرًا في الأدوات أو المترجم فقط بدلاً من تغيير اللغة؟

من الجدير بالتأكيد أن نسأل عما إذا كان بالإمكان تمكين إصلاح الكود التدريجي فقط من خلال المعلومات الجانبية المقدمة إلى المترجم (على سبيل المثال ، https://github.com/golang/go/issues/18130#issuecomment-264205929 بواسطةbtracey).

أو ربما إذا كان المترجم يمكنه تطبيق نوع من المعالجة المسبقة القائمة على القواعد لتحويل ملفات الإدخال قبل التجميع (على سبيل المثال ، https://github.com/golang/go/issues/18130#issuecomment-264329924 بواسطة @ tux21b).

لسوء الحظ ، لا ، لا يمكن حصر التغيير بهذه الطريقة. هناك ما لا يقل عن اثنين من المجمعين (gc و gccgo) يحتاجون إلى التنسيق ، وكذلك أي أدوات أخرى تحلل البرامج ، مثل go vet ، و guru ، و goimports ، و gocode (إكمال الكود) ، وغيرها.

كما قال bcmills (https://github.com/golang/go/issues/18130#issuecomment-264275574) ، فإن "آلية" تغيير اللغة غير اللغوية "التي يجب أن تدعمها جميع عمليات التنفيذ هي تغيير فعلي للغة - إنها مجرد واحدة بها وثائق أقل. "

ما هي الاستخدامات الأخرى التي قد تمتلكها الأسماء المستعارة؟

نحن نعلم ما يلي. بالنظر إلى هذا النوع على وجه الخصوص ، تم اعتبار الأسماء المستعارة على وجه الخصوص مهمة بما يكفي لإدراجها في Pascal و Rust ، فمن المحتمل أن يكون هناك أسماء أخرى.

  1. ستمكّن الأسماء المستعارة (أو مجرد كتابة الأسماء المستعارة) من إنشاء بدائل منسدلة تعمل على توسيع الحزم الأخرى. على سبيل المثال ، راجع https://go-review.googlesource.com/#/c/32145/ ، وخاصة الشرح الوارد في رسالة الالتزام.

  2. ستمكّن الأسماء المستعارة (أو الأسماء المستعارة فقط) من هيكلة حزمة بسطح API صغير ولكن تنفيذ كبير كمجموعة من الحزم لهيكل داخلي أفضل ولكن لا تزال تقدم حزمة واحدة فقط ليتم استيرادها واستخدامها من قبل العملاء. يوجد مثال مجرد إلى حد ما موصوف في https://github.com/golang/go/issues/16339#issuecomment -232813695.

  3. تحتوي مخازن البروتوكول المؤقتة على ميزة "استيراد عام" تكون دلالاتها تافهة للتنفيذ في كود C ++ الذي تم إنشاؤه ولكن من المستحيل تنفيذها في كود Go الذي تم إنشاؤه. يتسبب هذا في إحباط مؤلفي تعريفات المخزن المؤقت للبروتوكول المشتركة بين عملاء C ++ و Go. ستوفر الأسماء المستعارة للنوع طريقة لـ Go لتنفيذ هذه الميزة. في الواقع ، كانت حالة الاستخدام الأصلية للجمهور المستورد هي الإصلاح التدريجي للرمز . قد تظهر مشكلات مماثلة في أنواع أخرى من مولدات الأكواد.

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

ما هي القضايا الأخرى التي يحتاج اقتراح الأسماء المستعارة إلى معالجتها؟

سرد هذه للرجوع اليها. عدم محاولة حلها أو مناقشتها في هذا القسم ، على الرغم من مناقشة بعضها لاحقًا وتم تلخيصها في أقسام منفصلة أدناه.

  1. التعامل مع غودوك. (https://github.com/golang/go/issues/18130#issuecomment-264323137 بواسطة nigeltao و https://github.com/golang/go/issues/18130#issuecomment-264326437 بواسطةjimmyfrasche)

  2. هل يمكن تعريف الأساليب على الأنواع المسماة بالاسم المستعار؟ (https://github.com/golang/go/issues/18130#issuecomment-265077877 بواسطةulikunitz)

  3. إذا تم السماح باستخدام الأسماء المستعارة ، فكيف نتعامل مع دورات الاسم المستعار؟ (https://github.com/golang/go/issues/18130#issuecomment-264494658 بواسطة thwd)

  4. هل يجب أن تكون الأسماء المستعارة قادرة على تصدير المعرفات غير المُصدرة؟ (https://github.com/golang/go/issues/18130#issuecomment-264494658 بواسطة thwd)

  5. ماذا يحدث عندما تقوم بتضمين اسم مستعار (كيف يمكنك الوصول إلى الحقل المضمن)؟ (https://github.com/golang/go/issues/18130#issuecomment-264494658 بواسطة thwd ، أيضًا # 17746)

  6. هل الأسماء المستعارة متوفرة كرموز في البرنامج المبني؟ (https://github.com/golang/go/issues/18130#issuecomment-264494658 بواسطة thwd)

  7. حقن سلسلة Ldflags: ماذا لو أشرنا إلى اسم مستعار؟ (https://github.com/golang/go/issues/18130#issuecomment-264494658 بواسطة thwd ؛ لا يظهر هذا إلا في حالة وجود أسماء مستعارة مختلفة.)

هل الإصدار هو الحل في حد ذاته؟

"في هذه الحالة ، ربما يكون تعيين الإصدار هو الإجابة الكاملة ، وليس كتابة الأسماء المستعارة."
(https://github.com/golang/go/issues/18130#issuecomment-264573088 بواسطة iainmerrick)

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

هل يمكن حل مشكلة إعادة الهيكلة الأكبر بدلاً من ذلك؟

في https://github.com/golang/go/issues/18130#issuecomment -265052639 ، يشير niemeyer إلى أنه كان هناك بالفعل تغييران لنقل نظام التشغيل. تم استخدام طريقة الخطأ لتكون طريقة سلسلة).

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

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

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

هل يمكن التصريح عن الأساليب على أنواع الأسماء المستعارة؟

بالتأكيد لا تسمح الأسماء المستعارة بتجاوز قيود تعريف الطريقة المعتادة: إذا حددت الحزمة النوع T1 = otherpkg.T2 ، فلن تتمكن من تعريف العمليات على T1 ، تمامًا كما لا يمكنها تحديد العمليات مباشرة على otherpkg.T2. أي إذا كان النوع T1 = otherpkg.T2 ، فإن func (T1) M () يعادل func (otherpkg.T2) M () ، وهو غير صالح اليوم ويظل غير صالح. ومع ذلك ، إذا حددت الحزمة النوع T1 = T2 (كلاهما في نفس الحزمة) ، فإن الإجابة تكون أقل وضوحًا. في هذه الحالة ، سيكون func (T1) M () مكافئًا لـ func (T2) M () ؛ بما أن هذا الأخير مسموح به ، فهناك حجة للسماح للأول. لا يفرض مستند التصميم الحالي قيدًا هنا (تمشيا مع التجنب العام للقيود) ، بحيث يكون func (T1) M () صالحًا في هذه الحالة.

في https://github.com/golang/go/issues/18130#issuecomment -267694112 ، يقترح jimmyfrasche أن تحديد "عدم استخدام الأسماء المستعارة في تعريفات الطريقة" سيكون قاعدة واضحة وتجنب الحاجة إلى معرفة تعريف T لمعرفة ما إذا كانت func (T) M () صالحة. في https://github.com/golang/go/issues/18130#issuecomment -267997124 ، يشير rsc إلى أنه حتى اليوم هناك T معينة لا يكون func (T) M () صالحًا لها: https: // play .golang.org / p / bci2qnldej. في الممارسة العملية ، لا يأتي هذا لأن الناس يكتبون كودًا معقولًا.

سنضع هذا التقييد المحتمل في الاعتبار ولكن انتظر حتى يتوفر دليل قوي على الحاجة إليه قبل تقديمه.

هل هناك طريقة أنظف للتعامل مع التضمين وإعادة تسمية الحقول بشكل عام؟

في https://github.com/golang/go/issues/18130#issuecomment -267691816 ، يشير Merovius إلى أن نوعًا مضمنًا يغير اسمه أثناء نقل الحزمة سيتسبب في حدوث مشكلات عندما يجب اعتماد هذا الاسم الجديد في النهاية في استخدام المواقع. على سبيل المثال ، إذا كان نوع المستخدم U يحتوي على io.ByteBuffer مضمنًا ينتقل إلى بايت. المخزن ، فعندئذٍ ، أثناء قيام U بتضمين io.ByteBuffer ، يكون اسم الحقل هو U.ByteBuffer ، ولكن عندما يتم تحديث U للإشارة إلى بايت. التغييرات إلى U.Buffer.

في https://github.com/golang/go/issues/18130#issuecomment -267710478 ، يشير neild إلى وجود حل بديل على الأقل إذا كان يجب حذف الإشارات إلى io.ByteBuffer: الحزمة P التي تحدد U يمكن أيضًا حدد "type ByteBuffer = bytes.Buffer" وقم بتضمين هذا النوع في U. ثم لا يزال U لديه U.ByteBuffer ، حتى بعد اختفاء io.ByteBuffer تمامًا.

في https://github.com/golang/go/issues/18130#issuecomment -267703067 ، يقترح bcmills فكرة الأسماء المستعارة للحقل ، للسماح للحقل بأن يكون له أسماء متعددة أثناء الإصلاح التدريجي. تسمح الأسماء المستعارة للحقل بتعريف شيء مثل type U struct { bytes.Buffer; ByteBuffer = Buffer } بدلاً من الاضطرار إلى إنشاء اسم مستعار من النوع ذي المستوى الأعلى.

في https://github.com/golang/go/issues/18130#issuecomment -268001111 ، يطرح rsc احتمالًا آخر: بعض بناء الجملة لـ "تضمين هذا النوع بهذا الاسم" ، بحيث يمكن تضمين بايت. تخزين مؤقت كاسم حقل ByteBuffer ، دون الحاجة إلى نوع من المستوى الأعلى أو اسم بديل. إذا كان ذلك موجودًا ، فيمكن تحديث اسم النوع من io.ByteBuffer إلى bytes.Buffer مع الاحتفاظ بالاسم الأصلي (وعدم إدخال نوع ثانٍ أو نوع أخرق تم تصديره).

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

كان هناك اقتراح بتقييد استخدام الأسماء المستعارة في الحقول المضمنة أو تغيير الاسم المضمن لاستخدام اسم نوع الهدف ، لكن تلك تجعل مقدمة الاسم المستعار تكسر التعريفات الحالية التي يجب إصلاحها تلقائيًا بعد ذلك ، مما يمنع بشكل أساسي أي إصلاح تدريجي. rsc: "لقد ناقشنا هذا بشيء من التفصيل في # 17746 I كان في الأصل على جانب اسم مستعار io.ByteBuffer جزءا لا يتجزأ من كونها عازلة، ولكن الحجة المذكورة أعلاه أقنعني أنني كنت مخطئاjimmyfrasche على وجه الخصوص جعل بعض الخير. الحجج حول الكود لا يتغير اعتمادًا على تعريف الشيء المضمن. لا أعتقد أنه من الممكن عدم السماح بالأسماء المستعارة المضمنة تمامًا. "

ما هو التأثير على البرامج التي تستخدم التفكير؟

برامج تستخدم انعكاس انظر من خلال الأسماء المستعارة. في https://github.com/golang/go/issues/18130#issuecomment -267903649 ، يشير atdiar إلى أنه إذا كان أحد البرامج يستخدم الانعكاس ، على سبيل المثال ، ابحث عن الحزمة التي يتم فيها تعريف النوع أو حتى الاسم من نوع ما ، فسوف يلاحظ التغيير عند نقل النوع ، حتى إذا تم ترك اسم مستعار لإعادة التوجيه. في https://github.com/golang/go/issues/18130#issuecomment -268001410 ، أكد rsc هذا وكتب "مثل الموقف مع التضمين ، إنه ليس مثاليًا. بخلاف حالة التضمين ، ليس لدي أي الإجابات باستثناء ربما لا ينبغي كتابة التعليمات البرمجية باستخدام انعكاس لتكون حساسة للغاية لتلك التفاصيل ".

يؤدي استخدام الحزم الموردة اليوم أيضًا إلى تغيير مسارات استيراد الحزم التي يراها الانعكاس ، ولم نكن على دراية بالمشكلات الكبيرة الناجمة عن هذا الغموض. يشير هذا إلى أن البرامج لا تفحص بشكل عام reflect.Type.PkgPath بطرق يمكن كسرها باستخدام الأسماء المستعارة. ومع ذلك ، فهي فجوة محتملة ، تمامًا مثل التضمين.

ما هو تأثير التجميع المنفصل للبرامج والإضافات؟

في https://github.com/golang/go/issues/18130#issuecomment -268524504 ، يثير atdiar مسألة التأثير على ملفات الكائنات والتجميع المنفصل. في https://github.com/golang/go/issues/18130#issuecomment -268560180 ، يردrsc بأنه لا داعي لإجراء تغييرات هنا: إذا قام X باستيراد Y و Y يتغير وتم إعادة تجميعه ، فإن X يحتاج إلى يمكن إعادة تجميعها أيضًا. هذا صحيح اليوم بدون أسماء مستعارة ، وسيظل صحيحًا مع الأسماء المستعارة. يعني التجميع المنفصل القدرة على تجميع X و Y بخطوات مميزة (لا يتعين على المترجم معالجتهما في نفس الاستدعاء) ، وليس أنه من الممكن تغيير Y دون إعادة ترجمة X.

هل أنواع التلخيص أو نوع ما من الأنواع الفرعية تكون حلاً بديلاً؟

في https://github.com/golang/go/issues/18130#issuecomment -264413439 ، يقترح iand "أنواع https://github.com/golang/go/issues/18130#issuecomment -268072274 ، يقترح @ j7b استخدام الأنواع الجبرية "لذلك نحصل أيضًا على واجهة فارغة مكافئة مع فحص نوع وقت التجميع كمكافأة". الأسماء الأخرى لهذا المفهوم هي أنواع المجموع وأنواع المتغيرات.

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

في https://github.com/golang/go/issues/18130#issuecomment -268075680 ، تأخذ bcmills الطريق الملموس ، مشيرًا إلى أن الأنواع الجبرية لها تمثيل مختلف عن الأصل ، مما يجعل من غير الممكن معالجة المجموع والأصل قابل للتبديل: يحتوي الأخير على علامات كتابة.

في https://github.com/golang/go/issues/18130#issuecomment -268585497 ، يأخذ rsc الطريقة النظرية ، ويتوسع على https://github.com/golang/go/issues/18130#issuecomment -265211655 بواسطة gri مشيرًا إلى أنه في الإصلاح التدريجي للكود ، تحتاج أحيانًا إلى T1 ليكون نوعًا فرعيًا من T2 وأحيانًا العكس. الطريقة الوحيدة لكلا النوعين ليكونا نوعين فرعيين لبعضهما البعض هي أن يكونا من نفس النوع ، وهذا ليس من الواضح ما تفعله الأسماء المستعارة.

كظل جانبي ، بالإضافة إلى عدم حل مشكلة إصلاح الكود التدريجي ، يصعب إضافة الأنواع الجبرية / أنواع المجموع / أنواع الاتحاد / أنواع المتغيرات في حد ذاتها إلى Go. ارى
إجابة الأسئلة الشائعة ومناقشة Go 1.6 AMA للمزيد.

في https://github.com/golang/go/issues/18130#issuecomment -265206780 ، يقترح thwd أنه نظرًا لأن Go لديه علاقة فرعية بين الأنواع الملموسة والواجهات (بايت ، يمكن اعتبار المخزن المؤقت نوعًا فرعيًا من io.Reader ) وبين الواجهات (io.ReadWriter هو نوع فرعي من io.Reader بالطريقة نفسها) ، مما يجعل الواجهات "متغيرة التكرار (وفقًا لقواعد التباين الحالية) وصولاً إلى وسيطات الطريقة الخاصة بها" من شأنه حل المشكلة بشرط أن تكون جميع الحزم المستقبلية فقط استخدام واجهات ، لا تستخدم أنواعًا ملموسة مثل الهياكل ("يشجع على التصميم الجيد أيضًا").

هناك ثلاث مشاكل مع ذلك كحل. أولاً ، يحتوي على مشكلات التصنيف الفرعي أعلاه ، لذا فهو لا يحل إصلاح الكود التدريجي. ثانيًا ، لا ينطبق على الكود الحالي ، كما أشار thwd في هذا الاقتراح. ثالثًا ، قد لا يكون فرض استخدام الواجهات في كل مكان في الواقع تصميمًا جيدًا ويقدم نفقات عامة للأداء (انظر على سبيل المثال https://github.com/golang/go/issues/18130#issuecomment-265211726 بواسطة Merovius و https: // github .com / golang / go / Issues / 18130 # issuecomment-265224652 بواسطة @ zombiezen).

قيود

يجمع هذا القسم القيود المقترحة للرجوع إليها ، ولكن ضع في اعتبارك أن القيود تزيد من التعقيد. كما كتبت في https://github.com/golang/go/issues/18130#issuecomment -264195616 ، "من المحتمل ألا ننفذ هذه القيود إلا بعد التجربة الفعلية مع التصميم الأبسط غير المقيد الذي يساعدنا على فهم ما إذا كان التقييد سيحقق ما يكفي الفوائد لدفع تكلفتها ".

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

تقييد؟ لا يمكن الإعلان عن الأسماء المستعارة لأنواع المكتبات القياسية إلا في المكتبة القياسية.

(https://github.com/golang/go/issues/18130#issuecomment-264165833 و https://github.com/golang/go/issues/18130#issuecomment-264171370 بواسطةiand)

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

كما هو مذكور ، فإن التقييد لن يسمح بحالة "حزمة الامتداد" الموضحة أعلاه والتي تتضمن x / image / draw.

من غير الواضح لماذا يجب أن تكون المكتبة القياسية خاصة: قد توجد مشاكل مع أي رمز. أيضًا ، لا تعد الواجهة {} ولا الخطأ نوعًا من المكتبة القياسية. إعادة صياغة التقييد كـ "الاسم المستعار لأنواع محددة مسبقًا" من شأنه أن لا يسمح بالخطأ في الاسم المستعار ، لكن الحاجة إلى خطأ الاسم المستعار كانت أحد الأمثلة المحفزة في المقالة.

تقييد؟ يجب أن يكون هدف الاسم المستعار عبارة عن معرف مؤهل للحزمة.

(https://github.com/golang/go/issues/18130#issuecomment-264188282 بواسطةjba)

هذا سيجعل من المستحيل إنشاء اسم مستعار عند إعادة تسمية نوع داخل حزمة ، والذي يمكن استخدامه على نطاق واسع بما يكفي لاستلزام إصلاح تدريجي (https://github.com/golang/go/issues/18130#issuecomment-264274714 بواسطة @ bcmills).

كما أنه لن يسمح بخطأ في الاسم المستعار كما في المقالة.

تقييد؟ يجب أن يكون هدف الاسم المستعار عبارة عن معرف مؤهل للحزمة وله نفس الاسم مثل الاسم المستعار.

(مقترح أثناء مناقشة الاسم المستعار في Go 1.8)

بالإضافة إلى مشاكل القسم السابق المتعلقة بالحد من المعرفات المؤهلة للحزمة ، فإن إجبار الاسم على البقاء كما هو من شأنه منع التحويل من io.ByteBuffer إلى bytes.Buffer في المقالة.

تقييد؟ يجب تثبيط الأسماء المستعارة بطريقة ما.

"ماذا عن إخفاء الأسماء المستعارة خلف عملية استيراد ، تمامًا مثل" C "و" غير آمن "، لزيادة تثبيط استخدامها؟ في نفس السياق ، أود أن يكون بناء جملة الأسماء المستعارة مطولًا وأن يبرز كدعامة للاستمرار في إعادة البناء . " - https://github.com/golang/go/issues/18130#issuecomment -264289940 بواسطةxiegeo

"هل ينبغي لنا أيضًا أن نستنتج تلقائيًا أن نوع الاسم المستعار قديم ويجب استبداله بالنوع الجديد؟ إذا طبقنا أدوات golint و godoc والأدوات المماثلة لتصور النوع القديم على أنه مهمل ، فسيحد من إساءة استخدام الاسم المستعار للنوع بشكل كبير. و سيتم حل المشكلة الأخيرة المتعلقة بإساءة استخدام ميزة التعرّف ". - https://github.com/golang/go/issues/18130#issuecomment -265062154 بواسطةrakyll

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

حتى في حالة إصلاح الكود ، قد يكون النوع القديم أو الجديد هو الاسم المستعار أثناء الانتقال ، اعتمادًا على القيود التي يفرضها الرسم البياني للاستيراد. كونك اسمًا مستعارًا لا يعني أنه تم إهمال الاسم.

توجد بالفعل آلية لتمييز بعض الإعلانات على أنها مهملة (راجع https://github.com/golang/go/issues/18130#issuecomment-265294564 بواسطةjimmyfrasche).

تقييد؟ يجب أن تستهدف الأسماء المستعارة الأنواع المسماة.

"لا ينبغي أن تنطبق الأسماء المستعارة على النوع غير المسمى. فهي ليست قصة" إصلاح رمز "في الانتقال من نوع غير مسمى إلى آخر. ويعني السماح للأسماء المستعارة على الأنواع غير المسماة أنه لم يعد بإمكاني تعليم Go كأنواع مسماة وغير مسماة." - https://github.com/golang/go/issues/18130#issuecomment -276864903 بواسطة davecheney

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

كما هو مذكور في مستند التصميم ، نتوقع تغيير المصطلحات لجعل الموقف أكثر وضوحًا.

FrozenDueToAge Proposal Proposal-Accepted

التعليق الأكثر فائدة

cznic و iand و others: يُرجى ملاحظة أن _ القيود تضيف التعقيد_. إنهم يعقدون شرح الميزة ، ويضيفون عبئًا معرفيًا لأي مستخدم للميزة: إذا نسيت أحد القيود ، فعليك أن تفكر في سبب عدم نجاح شيء كنت تعتقد أنه يجب أن يعمل.

غالبًا ما يكون من الخطأ تطبيق القيود على تجربة التصميم فقط بسبب سوء الاستخدام الافتراضي. حدث ذلك في مناقشات اقتراح الاسم المستعار ، وجعل الأسماء المستعارة في التجربة غير قادرة على معالجة تحويل io.ByteBuffer => bytes.Buffer من المقالة. جزء من الهدف من كتابة المقالة هو تحديد بعض الحالات التي نعلم أننا نريد أن نكون قادرين على التعامل معها ، حتى لا نقوم بتقييدها دون قصد.

كمثال آخر ، سيكون من السهل تقديم حجة إساءة الاستخدام لعدم السماح بالمستقبلات التي لا تحتوي على مؤشر ، أو عدم السماح بالطرق على الأنواع غير الهيكلية. إذا فعلنا أيًا من هذين الأمرين ، فلن تتمكن من إنشاء تعدادات باستخدام أساليب String () لطباعة نفسها ، ولا يمكن أن يكون كل من http.Headers خريطة بسيطة وتوفر طرقًا مساعدة. غالبًا ما يكون من السهل تخيل إساءة الاستخدام ؛ يمكن أن تستغرق الاستخدامات الإيجابية المقنعة وقتًا أطول للظهور ، ومن المهم توفير مساحة للتجربة.

كمثال آخر ، لم يميز التصميم الأصلي والتنفيذ لأساليب المؤشر مقابل أساليب القيمة بين مجموعات الطريقة على T و * T: إذا كان لديك * T ، يمكنك استدعاء طرق القيمة (المتلقي T) ، وإذا كان لديك أ T ، يمكنك استدعاء طرق المؤشر (المتلقي * T). كان هذا بسيطًا ، دون أي قيود للتوضيح. ولكن بعد ذلك أظهرت لنا التجربة الفعلية أن السماح باستدعاءات طريقة المؤشر على القيم أدت إلى فئة معينة من الأخطاء المربكة والمفاجئة. على سبيل المثال ، يمكنك كتابة:

var buf bytes.Buffer
io.Copy(buf, reader)

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

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

ال 225 كومينتر

أنا أحب كيف يبدو هذا موحد بصريا.

const OldAPI => NewPackage.API
func  OldAPI => NewPackage.API
var   OldAPI => NewPackage.API
type  OldAPI => NewPackage.API

ولكن نظرًا لأنه يمكننا نقل معظم العناصر بشكل تدريجي تقريبًا ، فقد يكون أبسطها
حل _is_ فقط للسماح بـ = للأنواع.

const OldAPI = NewPackage.API
func  OldAPI() { NewPackage.API() }
var   OldAPI = NewPackage.API
type  OldAPI = NewPackage.API

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

كما تشير مقالة روس ، فإن أي حل يشبه الاسم المستعار يحتاج إلى حل برشاقة https://github.com/golang/go/issues/17746 و https://github.com/golang/go/issues/17784

شكرا لك على كتابة هذا المقال.

أجد الأسماء المستعارة من النوع فقط باستخدام عامل الإسناد ليكون الأفضل:

type OldAPI = NewPackage.API

أسبابي:

  • إنه أبسط.
    الحل البديل => له معنى مختلف تمامًا بناءً على معامله يبدو في غير محله بالنسبة لـ Go.
  • إنها مركزة ومحافظة.
    تم حل المشكلة المطروحة مع الأنواع ولا داعي للقلق بشأن تخيل مضاعفات الحل المعمم.
  • إنها جمالية.
    أعتقد أنه يبدو أكثر إرضاء.

كل ما سبق: النتيجة بسيطة ومركزة ومحافظة وجمالية تجعل من السهل بالنسبة لي أن أتخيل أنها جزء من Go.

إذا كان الحل يقتصر على الأنواع فقط ، فعندئذٍ بناء الجملة

type NewFoo = old.Foo

نظرت بالفعل من قبل ، كما تمت مناقشته في مقالةrsc ، تبدو جيدة جدًا بالنسبة لي.

إذا أردنا أن نكون قادرين على فعل الشيء نفسه للثوابت والمتغيرات والوظائف ، فإن الصيغة المفضلة لدي ستكون (كما هو مقترح من قبل)

package newfmt

import (
    "fmt"
)

// No renaming.
export fmt.Printf // Note: Same as `export Printf fmt.Printf`.

export (
        fmt.Sprintf
        fmt.Formatter
)

// Renaming.
export Foo fmt.Errorf // Foo must be exported, ie. `export foo fmt.Errorf` would be invalid.

export (
    Bar fmt.Fprintf
    Qux fmt.State
)

كما تمت مناقشته من قبل ، فإن العيب هو أنه يتم تقديم كلمة رئيسية جديدة ذات مستوى عالٍ فقط ، والتي من المسلم به أنها مقلدة ، على الرغم من أنها مجدية تقنيًا ومتوافقة تمامًا مع الإصدارات السابقة. يعجبني هذا النحو لأنه يعكس نمط الواردات. يبدو لي أنه من الطبيعي أن يسمح بالتصدير فقط في نفس القسم الذي يسمح فيه بالاستيراد ، أي. بين بند الحزمة وأي var أو type أو ثابت أو وظيفي TLD.

سيتم الإعلان عن معرفات إعادة التسمية في نطاق الحزمة ، ومع ذلك ، فإن الأسماء الجديدة غير مرئية في الحزمة التي تعلن عنها (newfmt في المثال أعلاه) فيما يتعلق بإعادة الإعلان ، وهو غير مسموح به كالمعتاد. بالنظر إلى المثال السابق ، TLDs

var v = Printf // undefined: Printf.
var Printf int // Printf redeclared, previous declaration at newfmt.go:8.

في حزمة الاستيراد ، تظهر معرّفات إعادة التسمية بشكل طبيعي ، مثل أي معرّف آخر مُصدَّر لكتلة الحزمة (newftm's).

package foo

import "newfmt"

type bar interface {
    baz(qux newfmt.Qux) // qux type is identical to fmt.State.
}

في الختام ، لا يقدم هذا النهج أي ارتباط باسم محلي جديد في newfmt ، والذي أعتقد أنه يتجنب على الأقل بعض المشكلات التي تمت مناقشتها في # 17746 ويحل # 17784 تمامًا.

المفضل الأول بالنسبة لي هو النوع فقط type NewFoo = old.Foo .

إذا كان هناك حاجة إلى حل أكثر عمومية ، فأنا أتفق مع cznic على أن الكلمة الأساسية المخصصة أفضل من عامل التشغيل الجديد (خاصة عامل التشغيل غير المتماثل ذي الاتجاه المربك [1]). ومع ذلك ، لا أعتقد أن الكلمة الرئيسية export تنقل المعنى الصحيح. لا الصيغة ولا الدلالات تعكس import . ماذا عن alias ؟

أتفهم سبب عدم رغبةcznic في أن تكون الأسماء الجديدة

[1] أستخدم Unix منذ ما يقرب من 20 عامًا ، وما زلت لا أستطيع إنشاء رابط رمزي في المحاولة الأولى. وعادة ما أفشل حتى في المحاولة الثانية ، بعد أن قرأت الدليل.

أود أن أقترح قيدًا إضافيًا: لا يجوز الإعلان عن الأسماء المستعارة لأنواع المكتبات القياسية إلا في المكتبة القياسية.

منطقتي هي أنني لا أريد العمل مع التعليمات البرمجية التي أعادت تسمية مفاهيم المكتبة القياسية لتلائم اصطلاح التسمية المخصص. لا أرغب أيضًا في التعامل مع سلاسل السباغيتي الطويلة من الأسماء المستعارة عبر حزم متعددة تنتهي في المكتبة القياسية.

iand : Context إلى المكتبة القياسية. يجب أن يصبح المنزل القديم لـ Context اسمًا مستعارًا لـ Context في المكتبة القياسية.

quentinmit هذا صحيح للأسف. كما أنه يحد من حالة استخدام golang.org/x/image/draw في هذا CL https://go-review.googlesource.com/#/c/32145/

قلقي الحقيقي هو أن الأشخاص يستخدمون أسماء مستعارة لأشياء مثل interface{} و error

إذا تقرر تقديم مشغل جديد ، أود أن أقترح ~ . في اللغة الإنجليزية ، يُفهم عمومًا على أنه يعني "مشابه" أو "تقريبًا" أو "حول" أو "حول". كما ذكر @ 4ad أعلاه ، فإن => عامل غير متناسق ذو اتجاه محير.

على سبيل المثال:

const OldAPI ~ NewPackage.API
func  OldAPI ~ NewPackage.API
var   OldAPI ~ NewPackage.API
type  OldAPI ~ NewPackage.API

iand إذا فسيؤدي ذلك إلى التخلص من مخاوفك المحددة.

قد يعني ذلك أيضًا أنه لا يمكنك الحصول على أسماء مستعارة لأي أنواع في الحزمة الحالية ، أو لتعبيرات كتابة طويلة مثل map[string]map[int]interface{} . لكن هذه الاستخدامات لا علاقة لها بالهدف الرئيسي المتمثل في الإصلاح التدريجي للكود ، لذلك ربما لا تكون خسارة كبيرة.

cznic و iand و others: يُرجى ملاحظة أن _ القيود تضيف التعقيد_. إنهم يعقدون شرح الميزة ، ويضيفون عبئًا معرفيًا لأي مستخدم للميزة: إذا نسيت أحد القيود ، فعليك أن تفكر في سبب عدم نجاح شيء كنت تعتقد أنه يجب أن يعمل.

غالبًا ما يكون من الخطأ تطبيق القيود على تجربة التصميم فقط بسبب سوء الاستخدام الافتراضي. حدث ذلك في مناقشات اقتراح الاسم المستعار ، وجعل الأسماء المستعارة في التجربة غير قادرة على معالجة تحويل io.ByteBuffer => bytes.Buffer من المقالة. جزء من الهدف من كتابة المقالة هو تحديد بعض الحالات التي نعلم أننا نريد أن نكون قادرين على التعامل معها ، حتى لا نقوم بتقييدها دون قصد.

كمثال آخر ، سيكون من السهل تقديم حجة إساءة الاستخدام لعدم السماح بالمستقبلات التي لا تحتوي على مؤشر ، أو عدم السماح بالطرق على الأنواع غير الهيكلية. إذا فعلنا أيًا من هذين الأمرين ، فلن تتمكن من إنشاء تعدادات باستخدام أساليب String () لطباعة نفسها ، ولا يمكن أن يكون كل من http.Headers خريطة بسيطة وتوفر طرقًا مساعدة. غالبًا ما يكون من السهل تخيل إساءة الاستخدام ؛ يمكن أن تستغرق الاستخدامات الإيجابية المقنعة وقتًا أطول للظهور ، ومن المهم توفير مساحة للتجربة.

كمثال آخر ، لم يميز التصميم الأصلي والتنفيذ لأساليب المؤشر مقابل أساليب القيمة بين مجموعات الطريقة على T و * T: إذا كان لديك * T ، يمكنك استدعاء طرق القيمة (المتلقي T) ، وإذا كان لديك أ T ، يمكنك استدعاء طرق المؤشر (المتلقي * T). كان هذا بسيطًا ، دون أي قيود للتوضيح. ولكن بعد ذلك أظهرت لنا التجربة الفعلية أن السماح باستدعاءات طريقة المؤشر على القيم أدت إلى فئة معينة من الأخطاء المربكة والمفاجئة. على سبيل المثال ، يمكنك كتابة:

var buf bytes.Buffer
io.Copy(buf, reader)

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

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

أيضًا ، آمل أن نتمكن من التوصل إلى قرار مبدئي بشأن ما يجب تجربته ثم الحصول على شيء جاهز للتجربة في بداية دورة Go 1.9 (من الناحية المثالية في اليوم الذي تفتح فيه الدورة). الحصول على مزيد من الوقت للتجربة سيكون له العديد من الفوائد ، من بينها فرصة لمعرفة ما إذا كان تقييد معين مقنعًا. كان أحد الأخطاء في الاسم المستعار هو عدم الالتزام بالتنفيذ الكامل حتى قرب نهاية دورة Go 1.8.

شيء واحد حول اقتراح الاسم المستعار الأصلي هو أنه في حالة الاستخدام المقصودة (تمكين إعادة البناء) ، يجب أن يكون الاستخدام الفعلي لنوع الاسم المستعار مؤقتًا فقط. في مثال protobuffer ، تم حذف كعب io.BytesBuffer بمجرد الانتهاء من الإصلاح التدريجي.

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

لن أشارك في bikeshedding حول بناء الجملة (أنا في الأساس لا أهتم) ، مع استثناء واحد: إذا تقرر إضافة أسماء مستعارة وإذا تقرر قصرها على الأنواع ، فيرجى استخدام بناء جملة يمكن توسيعه باستمرار إلى var على الأقل var ، إن لم يكن أيضًا func و const (تسمح جميع التركيبات التركيبية المقترحة للجميع ، باستثناء type Foo = pkg.Bar ). والسبب هو أنه على الرغم من أنني أوافق على أن الحالات التي تحدث فيها الأسماء المستعارة لـ var الفرق قد تكون نادرة ، لا أعتقد أنها غير موجودة ، وبالتالي أعتقد أننا قد نقرر في وقت ما إضافة هم أيضا. في هذه المرحلة ، سنريد بالتأكيد أن تكون جميع تصريحات الاسم المستعار متسقة ، وسيكون أمرًا سيئًا إذا كانت type Foo = pkg.Bar و var Foo => pkg.Bar .

أود أيضًا أن أجادل قليلاً لامتلاك الأربعة. الأسباب

1) هناك تمييز ل var وأنا لا أحيانا استخدامها. على سبيل المثال ، غالبًا ما أكشف عن var Debug *log.Logger عالميًا ، أو أعد تعيين مفردات عالمية مثل http.DefaultServeMux لاعتراض / إزالة تسجيلات الحزم التي تضيف معالجات إليها.

2) أعتقد أيضًا أنه بينما يقوم func Foo() { pkg.Bar() } بنفس الشيء مثل func Foo => pkg.Bar ، فإن نية الأخير أكثر وضوحًا (خاصة إذا كنت تعرف بالفعل الأسماء المستعارة). تنص بوضوح على أن "هذا ليس المقصود حقًا أن أكون هنا". لذلك ، في حين أنه متطابق تقنيًا ، قد يعمل بناء جملة الاسم المستعار كتوثيق.

إنه ليس التلة التي سأموت عليها ؛ ستكون الأسماء المستعارة لوحدها الآن مناسبة لي ، طالما أن هناك خيارًا لتمديدها لاحقًا.

أنا أيضًا سعيد جدًا لأن هذا تمت كتابته كما كان. إنه يلخص مجموعة من الآراء التي كانت لدي حول تصميم واجهة برمجة التطبيقات واستقرارها لفترة من الوقت ، وسيعمل في المستقبل كمرجع بسيط لربط الأشخاص أيضًا :)

ومع ذلك ، أود أيضًا التأكيد على أنه توجد حالات استخدام إضافية مغطاة بأسماء مستعارة تختلف عن المستند (و AIUI ، فإن النية العامة لهذه المشكلة هي إيجاد بعض الحلول لحل الإصلاح التدريجي). يسعدني جدًا أن يتفق المجتمع على مفهوم تمكين الإصلاح التدريجي ، ولكن إذا تم اتخاذ قرار مختلف عن الأسماء المستعارة للوصول إليه ، فأنا أعتقد أيضًا أنه في هذه الحالة يجب أن يكون هناك حديث في نفس الوقت حول ما إذا كان سيتم تقديم الدعم وكيفية ذلك أشياء مثل واردات الجمهور protobuf أو x/image/draw استخدام حالة انخفاض في الحزم استبدال (على حد سواء إلى حد ما بالقرب من قلبي جدا) مع حلا مختلفا. إن اقتراح btracey الخاص بعلامة go-tool / gc x/image/draw لتمرير تلك العلامات ، يجب أن يكونوا قادرين فقط على go get .

تضمين التغريدة

iand إذا فسيؤدي ذلك إلى التخلص من مخاوفك المحددة.

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

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

أعتقد أن تقييد الأسماء المؤهلة للحزمة فقط سيكون خطأ. (قد يكون التقييد على الأسماء المصدرة فقط أكثر قبولاً.)

تضمين التغريدة

ربما بدلاً من ذلك يمكن أن تكون هناك آلية لتزويد gc بقائمة من "الأسماء المستعارة". يمكن أن يقوم gc بإجراء الاستبدالات مؤقتًا ، ويمكن لمؤلف مصدر الشفرة النهائية إزالة العناصر الموجودة في هذا الملف تدريجيًا عند دمج الإصلاحات.

قد تعني آلية gc إما أن الكود قابل للإنشاء فقط باستخدام gc أثناء عملية الإصلاح ، أو أن الآلية يجب أن تكون مدعومة من قبل المجمعين الآخرين (على سبيل المثال ، gccgo و llgo ) أيضًا. إن آلية "عدم تغيير اللغة" التي يجب أن تدعمها جميع عمليات التنفيذ هي تغيير فعلي للغة - إنها مجرد آلية ذات وثائق ضعيفة.

btracey و bcmills ، وليس المجمّعين فقط: أي أداة تحلل كود المصدر ، مثل المعلم أو أي شيء آخر أنشأه الناس. إنه بالتأكيد تغيير في اللغة بغض النظر عن كيفية تقسيمه.

حسنا شكرا.

الاحتمال الآخر هو الأسماء المستعارة لكل شيء باستثناء consts (و rsc ، يرجى

بالنسبة إلى الثوابت ، يعد => طريقة أطول لكتابة = . لا توجد دلالات جديدة ، كما هو الحال مع الأنواع و vars. لا توجد ضغطات مفاتيح محفوظة كما هو الحال مع funcs.

هذا من شأنه أن يحل رقم 17784 على الأقل.

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

ومع ذلك ، فأنا على ما يرام مع كتابة الأسماء المستعارة في الوقت الحالي ، فهي بالتأكيد الأكثر أهمية. أتفق بالتأكيد مع Merovius على أننا يجب أن نفكر بشدة في الاحتفاظ بخيار إضافة الأسماء المستعارة var و func في المستقبل ، حتى لو لم يحدث ذلك لبعض الوقت.

ماذا عن إخفاء الأسماء المستعارة خلف استيراد ، تمامًا مثل "C" و "غير آمن" ، لزيادة تثبيط استخدامها؟ وعلى نفس المنوال ، أود أن تكون صيغة الأسماء المستعارة مطولة وتبرز كدعامة لاستمرار إعادة البناء.

كمحاولة لفتح مساحة التصميم قليلاً ، إليك بعض الأفكار. لم يتم تجسيدها. ربما تكون سيئة و / أو مستحيلة ؛ الأمل هو بشكل أساسي إطلاق أفكار جديدة / أفضل في الآخرين. وإذا كان هناك أي اهتمام ، فيمكننا استكشاف المزيد.

الفكرة المحفزة لـ (1) و (2) هي استخدام التحويل بطريقة ما بدلاً من الأسماء المستعارة. في # 17746 ، واجهت الأسماء المستعارة مشكلات حول وجود أسماء متعددة لنفس النوع (أو طرق متعددة لتهجئة نفس الاسم ، اعتمادًا على ما إذا كنت تعتقد أن الأسماء المستعارة مثل # تعريف أو روابط صلبة). استخدام التحويل الجانبي عن طريق الحفاظ على الأنواع مميزة.

  1. إضافة المزيد من التحويل التلقائي.

عند استدعاء fmt.Println("abc") أو كتابة var e interface{} = "abc" ، يتم تحويل "abc" تلقائيًا إلى interface{} . يمكننا تغيير اللغة بحيث عندما تعلن عن type T struct { S } ، و T ليس لها طرق غير مروج لها ، سيقوم المترجم بالتحويل تلقائيًا بين S و T حسب الضرورة ، بما في ذلك بشكل متكرر داخل البنى الأخرى. يمكن بعد ذلك استخدام T كاسم مستعار بحكم الواقع لـ S (أو العكس) لأغراض إعادة البناء التدريجي.

  1. إضافة نوع جديد من نوع "يشبه".

دع type T ~S يعلن عن نوع جديد T وهو نوع "يشبه S". بتعبير أدق ، T هو "أي نوع قابل للتحويل من وإلى النوع S". (كما هو الحال دائمًا ، يمكن مناقشة بناء الجملة لاحقًا.) مثل أنواع الواجهة ، لا يمكن أن يكون لدى T طرق ؛ لفعل أي شيء على الإطلاق باستخدام T ، فأنت بحاجة إلى تحويله إلى S (أو نوع قابل للتحويل إلى / من S). على عكس أنواع الواجهة ، لا يوجد "نوع ملموس" ، والتحويل بين S إلى T و T إلى S لا يتضمن أي تغييرات في التمثيل. لإعادة الهيكلة التدريجية ، ستسمح هذه الأنواع "التي تشبه" للمؤلفين بكتابة واجهات برمجة التطبيقات (API) التي تقبل كلا النوعين القديم والجديد. (أنواع "يبدو مثل" هي في الأساس نوع اتحاد مبسط ومقيّد للغاية.)

  1. اكتب العلامات

فكرة المكافأة الفائقة البشعة. (من فضلك لا تكلف نفسك عناء إخباري أن هذا أمر مروع - أنا أعرف ذلك. أنا أحاول فقط تحفيز الأفكار الجديدة في الآخرين.) ماذا لو قدمنا ​​علامات الكتابة (مثل علامات البنية) ، واستخدمنا علامات كتابة خاصة لإعداد والتحكم في الأسماء المستعارة ، مثل قل type T S "alias:\"T\"" . سيكون لعلامات الكتابة استخدامات أخرى أيضًا وتوفر مجالًا لمواصفات أكثر للأسماء المستعارة بواسطة مؤلف الحزمة بدلاً من مجرد "هذا النوع هو اسم مستعار" ؛ على سبيل المثال ، يمكن لمؤلف الكود تحديد سلوك التضمين.

إذا حاولنا استخدام الأسماء المستعارة مرة أخرى ، فقد يكون من المفيد التفكير في "ما الذي يفعله godoc" ، على غرار قضايا "ما الذي تفعله ذرة" و "ما يفعله التضمين".

على وجه التحديد ، إذا كان لدينا

type  OldAPI => NewPackage.API

و NewPackage.API لديه تعليق مستند ، هل نتوقع نسخ / لصق هذا التعليق بجوار "اكتب OldAPI" ، هل نتوقع تركه بدون تعليق (مع قيام godoc تلقائيًا بتوفير رابط أو نسخ / لصق تلقائيًا) ، أو هناك اتفاقية أخرى؟

عرضي إلى حد ما ، في حين أن الدافع الأساسي هو ويجب أن يدعم إصلاح الكود التدريجي ، فإن حالة الاستخدام البسيطة (العودة إلى اقتراح الاسم المستعار ، نظرًا لأن هذا اقتراح ملموس) يمكن أن يكون لتجنب الحمل المزدوج لاستدعاء الوظيفة عند تقديم وظيفة واحدة مدعومة بتطبيقات متعددة تعتمد على علامة البناء. أنا ألوح باليد فقط في الوقت الحالي ، ولكني أشعر أن الأسماء المستعارة قد تكون مفيدة في https://groups.google.com/d/topic/golang-nuts/wb5I2tjrwoc/discussion "تجنب زيادة استدعاءات الوظائف في الحزم مع تطبيقات go + asm ".

nigeltao re godoc ، أعتقد:

يجب أن يرتبط دائمًا بالأصل ، بغض النظر.

إذا كانت هناك مستندات على الاسم المستعار ، فيجب عرضها بغض النظر.

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

ومع ذلك ، ربما لا يهم ذلك بالنسبة لغالبية حالات الاستخدام. هذه هي الأشياء التي يمكن أن تسوء ، وليست الأشياء التي سوف تسوء. ويمكن الكشف عن بعضها عن طريق الفحص ، مثل الأسماء المستعارة التي أعيدت تسميتها ونسخ تحذيرات الإهمال عن طريق الخطأ.

لست متأكدًا مما إذا كانت الفكرة التالية قد تم نشرها من قبل ، ولكن ماذا عن نهج مثل "gofix" / "gorename" المستند إلى الأدوات في الغالب؟ للتوضيح:

  • يمكن أن تحتوي أي حزمة على مجموعة من قواعد إعادة الكتابة (مثل تعيين pkg.Ident => otherpkg.Ident )
  • يمكن تحديد قواعد إعادة الكتابة هذه بعلامات //+rewrite ... داخل ملفات go التعسفية
  • لا تقتصر قواعد إعادة الكتابة هذه على التغييرات المتوافقة مع ABI ، بل من الممكن أيضًا القيام بأشياء أخرى (مثل pkg.MyFunc(a) => pkg.MyFunc(context.Contex(), a) )
  • يمكن استخدام أداة مثل gofix لتطبيق جميع التحويلات على المستودع الحالي. هذا يجعل من السهل على مستخدمي الحزمة تحديث التعليمات البرمجية الخاصة بهم.
  • ليس من الضروري استدعاء أداة gofix من أجل التحويل البرمجي بنجاح. لا يزال بإمكان المكتبة التي لا تزال ترغب في استخدام واجهة برمجة التطبيقات القديمة للاعتماد X (للبقاء متوافقة مع الإصدارات القديمة والجديدة من X) القيام بذلك. يجب أن يطبق الأمر go build التحويلات (المحددة في علامات إعادة الكتابة الخاصة بالحزمة X) أثناء التنقل دون تغيير الملفات الموجودة على القرص.

قد تؤدي الخطوات الأخيرة إلى تعقيد / إبطاء المترجم قليلاً ، ولكنها في الأساس مجرد معالج مسبق ويجب أن تظل كمية قواعد إعادة الكتابة صغيرة على أي حال. لذا ، يكفي العصف الذهني لهذا اليوم :)

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

josharian @ بينما لم يلهمك أن يأخذ النقد الفوري في الاعتبار):

  1. لا يحل المشكلة حقًا ، لأن التحويلات ليست هي المشكلة حقًا. x/net/context.Context قابل للتخصيص / قابل للتحويل / مهما كان إلى context.Context . المشكلة هي أنواع ذات ترتيب أعلى ؛ أي أن النوعين func (ctx x/net/context.Context) و func (ctx context.Context) ليسا متماثلين ، على الرغم من أن الوسيطات قابلة للتخصيص. لذلك ، بالنسبة إلى 1 لحل المشكلة ، يجب أن يعني type T struct { S } أن T و S نوعان متطابقان. مما يعني أنك ببساطة تستخدم صيغة مختلفة للأسماء المستعارة بعد كل شيء (فقط أن بناء الجملة هذا له بالفعل معنى مختلف).

  2. مرة أخرى توجد مشكلة مع الأنواع ذات الترتيب الأعلى ، لأن الأنواع القابلة للتخصيص / التحويل لا تحتوي بالضرورة على نفس تمثيل الذاكرة (وإذا كانت كذلك ، فقد يتغير التفسير بشكل كبير). على سبيل المثال ، uint8 قابل للتحويل إلى uint64 والعكس صحيح. ولكن هذا يعني ، على سبيل المثال ، مع type T ~uint8 ، لا يستطيع المترجم معرفة كيفية استدعاء func(T) ؛ هل تحتاج إلى دفع 1 أو 2،4 أو 8 بايت على المكدس؟ قد تكون هناك طرق لحل هذه المشكلة ، لكنها تبدو معقدة جدًا بالنسبة لي (وأصعب في الفهم من الأسماء المستعارة).

شكرا ، ميروفيوس.

  1. نعم ، فاتني الرضا عن الواجهة هنا. أنت على حق ، هذا لا يؤدي المهمة.

  2. كان يدور في خلدي "لدي نفس تمثيل الذاكرة". من الواضح أن التحويل ذهابًا وإيابًا ليس التوضيح الصحيح لذلك - شكرًا.

uluyol نعم ، يتعلق الأمر إلى حد كبير بعدم قدرة المترجم على وظائف مضمنة غير

على أي حال ، كما قلت ، إنه ظل طفيف.

josharian مشكلة مماثلة: [2]uintptr و interface{} لهما نفس تمثيل الذاكرة ؛ لذا فإن الاعتماد فقط على تمثيل الذاكرة سيسمح بالتحايل على أمان النوع. uint64 و float64 يكون كل نفس التمثيل الذاكرة وقابلة للتحويل ذهابا وإيابا، ولكن لا تزال تؤدي إلى نتائج غريبة حقا على الأقل، إذا كنت لا تعرف ما هو الذي.

ومع ذلك ، قد تفلت من "نفس النوع الأساسي". لست متأكدا ما هي الآثار المترتبة على ذلك. خارج الجزء العلوي من قبعتي ، قد يؤدي ذلك إلى الخطأ إذا تم استخدام نوع في الحقول ، على سبيل المثال. إذا كان لديك type S1 struct { T1 } و type S2 struct { T2 } (مع T1 و T2 من نفس النوع الأساسي) ، فعندئذٍ أقل من type L1 ~T1 قد يعمل كلاهما type S struct { L1 } ، ولكن مثل T1 و T2 لا يزال لديهم نوع أساسي مختلف (على الرغم من أنه يبدو متشابهًا) ، مع type L2 ~S1 لن يكون لديك S2 تبدو متشابهة S1 ولا يمكن استخدامها كـ L2 .

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

إذا كان بإمكان أي شخص أن يتوصل إلى قاعدة بسيطة لذلك ، على الرغم من أنه لا توجد به هذه المشكلات ، فيجب بالتأكيد اعتباره بديلاً :)

متابعة من

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

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

على العكس من ذلك ، هناك بعض المزايا لعكس الاتجاه:

  • يتم التحكم في مجموعة الأنواع القابلة للاستبدال من قبل مؤلف الحزمة الجديدة الذي هو في وضع أفضل لضمان الدلالات
  • لا يلزم إجراء تغييرات على الكود في الحزمة الأصلية ، لذلك لا يتعين على العملاء التحديث حتى يبدأوا في استخدام الحزمة الجديدة

تطبيق هذا على إعادة بناء السياق: ستعلن حزمة سياق المكتبة القياسية أنه يمكن استبدال context.Context بـ golang.org/x/net/context.Context . هذا يعني أي استخدام يقبل السياق. قد يقبل المحتوى أيضًا golang.org/x/net/context.Context في مكانه. ومع ذلك ، فإن الدالات في حزمة السياق التي تُرجع السياق ستُرجع دائمًا context.Context .

يتحايل هذا الاقتراح على مشكلة التضمين (# 17746) لأن اسم النوع المضمن لا يتغير أبدًا. ومع ذلك ، يمكن تهيئة النوع المضمن باستخدام قيمة من النوع البديل.

iandjosharian تسألون عن البديل معين من أنواع التغاير.

@ josharian ، شكرا على الاقتراحات.

Re type T struct { S } ، يبدو وكأنه صيغة مختلفة للاسم المستعار ، وليس بالضرورة صيغة أوضح.

بالنسبة إلى type T ~S ، لست متأكدًا من اختلافه عن الاسم المستعار أو لست متأكدًا من كيفية مساعدته في إعادة البناء. أعتقد أنه في إعادة بناء ديون (على سبيل المثال ، io.ByteBuffer -> bytes.Buffer) ، ستكتب:

package io
type ByteBuffer ~bytes.Buffer

ولكن بعد ذلك ، كما تقول ، "لفعل أي شيء على الإطلاق باستخدام T ، فأنت بحاجة إلى تحويله إلى S" ، فسيظل كل الكود الذي يفعل أي شيء مع io.ByteBuffer معطلاً.

Re type T S "alias" : النقطة الأساسية التي ذكرهاbcmills أعلاه هي أن وجود عدة أسماء مكافئة للأنواع يعد تغييرًا في اللغة ، بغض النظر عن كيفية تهجئتها. يحتاج جميع المترجمين إلى معرفة ذلك ، على سبيل المثال ، io.ByteBuffer و bytes.Buffer ، كما هو الحال مع أي أدوات تحلل أو حتى تحقق من التعليمات البرمجية. يبدو لي الجزء الرئيسي من اقتراحك شيئًا مثل "ربما ينبغي أن نخطط مسبقًا لإضافات أخرى". ربما ، لكن من غير الواضح أن السلسلة ستكون أفضل طريقة لوصف ذلك ، كما أنه من غير الواضح أيضًا أننا نريد تصميم بناء الجملة (مثل تعليقات Java التوضيحية المعممة) دون الحاجة إلى ذلك. حتى لو كان لدينا نموذج عام ، فسنظل بحاجة إلى النظر بعناية في جميع الآثار المترتبة على أي دلالات جديدة قدمناها ، وسيظل معظمها عبارة عن تغييرات لغوية تتطلب تحديث جميع الأدوات (باستثناء gofmt ، باعتراف الجميع). بشكل عام ، يبدو من الأسهل الاستمرار في العثور على أوضح طريقة لكتابة النماذج التي نحتاجها واحدة تلو الأخرى بدلاً من إنشاء لغة وصفية من نوع أو آخر.

Merovius FWIW ، أود أن أقول إن [2] uintptr والواجهة {} لا يمتلكان نفس تمثيل الذاكرة. الواجهة {} هي [2] غير آمنة.المؤشر ليس uintptr [2]. uintptr والمؤشر هما تمثيلان مختلفان. لكنني أعتقد أن وجهة نظرك العامة صحيحة ، وهي أننا لا نريد بالضرورة السماح بالتحويل المباشر لهذا النوع من الأشياء. أعني ، هل يمكنك التحويل من الواجهة {} إلى [2] * بايت أيضًا؟ إنه أكثر بكثير مما هو مطلوب هنا.

jimmyfrasche و nigeltao ، إعادة godoc: أوافق على أننا بحاجة إلى هذا العمل مبكرًا أيضًا. أوافق على أنه لا ينبغي لنا ترميز الافتراض بأن "الميزة الجديدة - مهما كانت - ستُستخدم فقط لإعادة هيكلة قاعدة التعليمات البرمجية". قد يكون لها استخدامات مهمة أخرى ، مثل Nigel الذي وجده للمساعدة في كتابة حزمة تمديد السحب بأسماء مستعارة. أتوقع أنه سيتم وضع علامة على الأشياء المهملة في تعليقات المستندات بشكل صريح ، كما قال جيمي. لقد فكرت في إنشاء تعليق مستند تلقائيًا إذا لم يكن هناك أحد ، ولكن لا يوجد شيء واضح يمكن قوله أنه لا ينبغي أن يكون واضحًا بالفعل من بناء الجملة (بشكل عام). لتقديم مثال محدد ، ضع في اعتبارك الأسماء المستعارة القديمة لـ Go 1.8. منح

type ByteBuffer => bytes.Buffer

يمكننا تجميع تعليق مستند يقول "ByteBuffer هو اسم مستعار لـ bytes.Buffer" ، ولكن يبدو أن هذا لا لزوم له عند عرض التعريف. إذا كتب شخص ما "type X structure {}" اليوم ، فإننا لا نقوم بتوليف "X هو نوع مسمى لبنية {}".

iand ، شكرًا. يبدو أن اقتراحك يتطلب من مؤلف الحزمة الجديدة كتابة التعريف الدقيق من الحزمة القديمة ثم أيضًا إعلان يربط بين الاثنين ، مثل (تكوين الجملة):

package old
type T { x int }

package new
import "old"
type T1 { x int }
substitutable T1 <- old.T

أوافق على أن انعكاس الاستيراد يمثل مشكلة وقد يكون بمثابة توقف للعرض في حد ذاته ، ولكن دعنا نتخطى ذلك. في هذه المرحلة ، يبدو أن قاعدة الكود في حالة هشة: الآن يمكن كسر الحزمة الجديدة بتغيير لإضافة حقل هيكلي في الحزمة القديمة. بالنظر إلى السطر القابل للاستبدال ، لا يوجد سوى تعريف واحد ممكن لـ T1: تمامًا مثل T القديم. إذا كان لا يزال لدى النوعين تعريفات مميزة ، فعليك أيضًا القلق بشأن الطرق: هل يجب أن تتطابق عمليات تنفيذ الطريقة أيضًا؟ إذا لم يكن الأمر كذلك ، فما الذي يحدث عندما تضع حرف T في واجهة {} ثم تسحبه للخارج باستخدام تأكيد نوع مثل T1 واستدعاء M ()؟ هل تحصل على T1.M؟ ماذا لو أخرجته كواجهة {M ()} ، دون تسمية T1 مباشرة ، واستدعيت M ()؟ هل تحصل على TM؟ هناك الكثير من التعقيد الناجم عن غموض وجود كلا التعريفين في شجرة المصدر.

بالطبع ، يمكنك القول أن السطر القابل للاستبدال يجعل الباقي زائدة عن الحاجة ولا يتطلب تعريفًا للنوع T1 أو أي طرق. ولكن هذا هو في الأساس نفس الكتابة (في صيغة الاسم المستعار القديم) type T1 => old.T .

بالعودة إلى مشكلة الرسم البياني للاستيراد ، على الرغم من أن جميع الأمثلة في المقالة جعلت الكود القديم مُعرَّفًا من حيث الكود الجديد ، إذا كان الرسم البياني للحزمة يجب أن يستورد القديم بدلاً من ذلك ، فمن الفعال أيضًا وضع إعادة التوجيه في حزمة جديدة أثناء الانتقال.

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

يشيرrsc إلى أن أي نوع مستعار يجب أن يكون ثنائي الاتجاه. حتى في ظل اقتراح الاسم المستعار السابق ، فإن أي تغيير في الحزمة الجديدة يمكن أن يكسر أي عدد من الحزم التي تصادف أنها تحمل اسمًا مستعارًا للنوع.

iand إذا كان هناك تعريف واحد فقط (لأن الآخر يقول "نفس تعريف _هذا") فلا داعي للقلق بشأن عدم تزامنهما.

في # 13467 ، يشير joegrasse إلى أنه سيكون من الجيد أن يقدم هذا الاقتراح آلية للسماح لأنواع C متطابقة بأن تصبح أنواع Go متطابقة عند استخدام cgo في حزم متعددة. هذه ليست على الإطلاق نفس المشكلة التي تتعلق بها هذه المشكلة ، ولكن كلتا المشكلتين مرتبطة بنوع مستعار.

هل هناك أي ملخص للقيود / القيود المقترحة / المقبولة / المرفوضة على الأسماء المستعارة؟ بعض الأسئلة التي تخطر ببالنا هي:

  • هل RHS مؤهل بشكل كامل دائمًا؟
  • إذا تم السماح باستخدام الأسماء المستعارة ، فكيف نتعامل مع دورات الاسم المستعار؟
  • هل يجب أن تكون الأسماء المستعارة قادرة على تصدير المعرفات غير المُصدرة؟
  • ماذا يحدث عندما تقوم بتضمين اسم مستعار؟ (كيف يمكنك الوصول إلى الحقل المضمن)
  • هل الأسماء المستعارة متوفرة كرموز في البرنامج المبني؟
  • ldflags string injection: ماذا لو أشرنا إلى اسم مستعار؟

rsc لا أريد تحويل المحادثة كثيرًا ولكن في ظل اقتراح الاسم المستعار إذا أزال "جديد" حقلاً يعتمد عليه "قديم" يعني أنه لا يمكن لعملاء "قديم" الآن تجميعه.

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

thwd لا أعتقد أن هناك كتابة جيدة حتى الآن. ملاحظاتي:

  • دورات الاسم المستعار ليست مشكلة. في حالة الأسماء المستعارة لعبور الحزمة ، فإن الدورة غير مسموح بها بالفعل بسبب دورة الاستيراد. في حالة وجود أسماء مستعارة غير عبور الحزمة ، من الواضح أنها بحاجة إلى عدم السماح بها ، وهو ما يشبه إلى حد بعيد الدورات في ترتيب التهيئة. أنا شخصياً أرغب في الحصول على أسماء مستعارة للأسماء المستعارة ، لأنني لا أعتقد أنه يجب قصرها على حالات استخدام الإصلاح التدريجي (انظر تعليقي أعلاه) وسيكون الأمر محزنًا ، إذا كانت الحزمة A قد تنفصل عن طريق شخص ما ينقل نوعًا إلى الحزمة B مع اسم مستعار (تخيل x/image/draw.Image المستعار draw.Image ثم قرر شخص ما نقل draw.Image إلى image.Draw عبر اسم مستعار ، بافتراض أنه آمن. فجأة x/image/draw فواصل ، لأن الأسماء المستعارة للأسماء المستعارة غير مسموح بها).
  • أعتقد أن أنصار الأسماء المستعارة سابقًا قد اتفقوا على أن تصدير الاسم المستعار للمعرفات غير المُصدرة ربما يكون فكرة سيئة بسبب الغرابة التي قد تسببها. وهذا يعني فعليًا أن الأسماء المستعارة للمعرفات غير المُصدرة غير مجدية وقد لا يُسمح بها تمامًا.
  • سؤال التضمين AFAIK لم يتم حله بعد. هناك نقاش كامل في # 17746 ، أتوقع أن تستمر هذه المناقشة إذا / عندما / قبل اتخاذ قرار بالمضي قدمًا مع الأسماء المستعارة (ولكن لا تزال هناك إمكانية لحل بديل أو قرار عدم جعل الإصلاحات التدريجية هدفًا على الاطلاق)

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

هناك شيء آخر يجب مراعاته ، ولم أره مذكورًا في مكان آخر حتى الآن:

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

بدون أي نوع من تحذير المترجم ، كيف يمكن لمالك المكتبة أن يكون واثقًا من أنه من الآمن إكمال إعادة البناء؟

قد تكون إحدى الإجابات نوعًا من مخطط الإصدار - إنه إصدار جديد من المكتبة بواجهة برمجة تطبيقات جديدة غير متوافقة. في هذه الحالة ، ربما يكون تعيين الإصدار هو الإجابة الكاملة ، وليس كتابة الأسماء المستعارة.

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

Error: os.time is obsolete, use time.time instead. Run "go upgrade" to fix this.

بالنسبة للأسماء المستعارة من النوع ، أعتقد أن خوارزمية إعادة البناء ستكون "استبدال جميع مثيلات OldType بـ NewType" ، ولكن قد تكون هناك بعض التفاصيل الدقيقة ، لست متأكدًا.

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

iainmerrick هناك أخطاء مفتوحة لهذه: golang / lint # 238 و golang / gddo # 456

حل مشكلة إصلاح الكود التدريجي ، كما هو موضح في مقالةrsc ، يقلل من الحاجة إلى طريقة لنوعين قابلين للتبادل (حيث توجد حلول بديلة لـ vars و funcs و consts).

هذا يحتاج إما إلى أداة أو تغيير في اللغة.

نظرًا لأن جعل نوعين قابلين للتبديل هو ، بحكم التعريف ، تغيير كيفية عمل اللغة ، فإن أي أداة ستكون آلية لمحاكاة التكافؤ خارج المترجم ، على الأرجح عن طريق إعادة كتابة جميع مثيلات النوع القديم إلى النوع الجديد. ولكن هذا يعني أن مثل هذه الأداة يجب أن تعيد كتابة التعليمات البرمجية التي لا تملكها ، مثل الحزمة الموردة التي تستخدم golang.org/x/net/context بدلاً من حزمة سياق stdlib. يجب أن تكون مواصفات التغيير إما في ملف بيان منفصل أو تعليق يمكن قراءته آليًا. إذا لم تقم بتشغيل الأداة ، فستتلقى أخطاء في الإنشاء. كل هذا يصبح فوضويًا للتعامل معه. يبدو أن الأداة ستخلق العديد من المشاكل بقدر ما تحلها. ستظل مشكلة يتعين على كل شخص يستخدم هذه الحزم التعامل معها ، وإن كان ذلك أفضل إلى حد ما نظرًا لأن الجزء مؤتمت.

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

كما أشار griesemer (لا أتذكر أين ، كان هناك الكثير من المواضيع حول هذا) انتقل بالفعل له اسم مستعار ، لأشياء مثل byteuint8 ، وعندما تقوم باستيراد حزمة مرتين ، بأسماء محلية مختلفة ، في نفس الملف المصدر.

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

لا يزال تغيير اللغة يمثل مشكلة كبيرة وتحتاج الكثير من الأشياء إلى العمل بها ، لكنني أعتقد أنه في النهاية الشيء الصحيح الذي يجب القيام به هنا.

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

سؤال إضافي: يرجى توضيح ما إذا كانت الأسماء المستعارة للنوع ستسمح بإضافة طرق إلى النوع في الحزمة الجديدة (يمكن القول إنها كسر التغليف) أم لا؟ أيضا ، هل ستتمكن الحزمة الجديدة من الوصول إلى الحقول الخاصة للبنى القديمة أم لا؟

سؤال إضافي: يرجى توضيح ما إذا كانت الأسماء المستعارة للنوع ستسمح بإضافة طرق إلى النوع في الحزمة الجديدة (يمكن القول إنها كسر التغليف) أم لا؟ أيضا ، هل ستتمكن الحزمة الجديدة من الوصول إلى الحقول الخاصة للبنى القديمة أم لا؟

الاسم المستعار هو مجرد اسم آخر لنوع. لا يغير حزمة النوع. لذا لا لكلا السؤالين (ما لم تكن الحزمة الجديدة == الحزمة القديمة).

akavel حتى الآن ، لا يوجد أي اقتراح على الإطلاق. لكننا نعرف اثنين من الاحتمالات المثيرة للاهتمام التي ظهرت خلال تجارب الاسم المستعار Go 1.8.

  1. ستمكّن الأسماء المستعارة (أو مجرد كتابة الأسماء المستعارة) من إنشاء بدائل منسدلة تعمل على توسيع الحزم الأخرى. على سبيل المثال ، راجع https://go-review.googlesource.com/#/c/32145/ ، وخاصة الشرح الوارد في رسالة الالتزام.

  2. ستمكّن الأسماء المستعارة (أو الأسماء المستعارة فقط) من هيكلة حزمة بسطح API صغير ولكن تنفيذ كبير كمجموعة من الحزم لهيكل داخلي أفضل ولكن لا تزال تقدم حزمة واحدة فقط ليتم استيرادها واستخدامها من قبل العملاء. يوجد مثال مجرد إلى حد ما موصوف في https://github.com/golang/go/issues/16339#issuecomment -232813695.

الهدف الأساسي من الأسماء المستعارة رائع ، لكن لا يزال يبدو أننا لا نتحلى بالصدق التام في هدف إعادة بناء الكود ، على الرغم من كونه الحافز الأول لهذه الميزة. تقترح بعض المقترحات إغلاق الاسم ، ولم أره مذكورًا بعد أن الأنواع عادة ما تغير سطحها مع عمليات إعادة البناء هذه أيضًا. حتى مثال os.Error => error يُشار إليه غالبًا حول الأسماء المستعارة يتجاهل حقيقة أن os.Error يحتوي على طريقة String وليس Error . إذا نقلنا النوع وأعدنا تسميته ، فسيتم كسر جميع رموز معالجة الأخطاء بغض النظر. هذا مكان شائع أثناء إعادة البناء .. يتم إعادة تسمية الطرق القديمة ونقلها وإسقاطها ، ولا نريدها في النوع الجديد لأنها ستحافظ على عدم التوافق مع الكود الجديد.

من أجل المساعدة ، إليك فكرة أولية: ماذا لو نظرنا إلى المشكلة من حيث المحولات ، بدلاً من الأسماء المستعارة؟ سيعطي المحول اسمًا بديلاً للنوع الحالي _ وواجهة_ ، ويمكن استخدامه بدون زخرفة في الأماكن التي شوهد فيها النوع الأصلي من قبل. سيحتاج المحول إلى تحديد الطرق التي يدعمها بشكل صريح ، بدلاً من افتراض وجود نفس الواجهة من النوع الأساسي المعدّل. قد يكون هذا يشبه إلى حد كبير سلوك type foo bar ، ولكن مع بعض الدلالات الإضافية.

io.ByteBuffer

على سبيل المثال ، إليك مثال على الهيكل العظمي الذي يتناول حالة io.ByteBuffer ، باستخدام الكلمة الرئيسية المؤقتة "adapts" في الوقت الحالي:

type ByteBuffer adapts bytes.Buffer

func (old *ByteBuffer) Write(b []byte) (n int, err error) {
        buf := (*bytes.Buffer)(old)
        return buf.Write(b)
}

(... etc ...)

لذلك ، مع وجود هذا المحول في مكانه ، سيكون هذا الرمز صالحًا:

func newfunc(b *bytes.Buffer) { ... }
func oldfunc(b *io.ByteBuffer) { ... }

func main() {
        var newvar bytes.Buffer
        var oldvar io.BytesBuffer

        // New code using the new type obviously just works.
        newfunc(&newvar)

        // New code using the old type receive the underlying value that was adapted.
        newfunc(&oldvar)

        // Old code using the old type receive the adapted value unchanged.
        oldfunc(&oldvar)

        // Old code gets new variable adapted on the way in. 
        oldfunc(&newvar)
}

واجهات newfunc و oldfunc متوافقة. كلاهما يقبل في الواقع *bytes.Buffer ، مع oldfunc يتكيف مع *io.BytesBuffer في الطريق. نفس المفهوم يعمل مع المهام والنتائج وما إلى ذلك.

نظام التشغيل خطأ

من المحتمل أن يتم عمل نفس المنطق للعمل على الواجهة أيضًا ، على الرغم من أن تنفيذ المترجم لها أصعب قليلاً. فيما يلي مثال لـ os.Error => error ، والذي يعالج حقيقة إعادة تسمية الطريقة:

package os

type Error adapts error

func (e Error) String() string { return error(e).Error() }

تحتاج هذه الحالة إلى مزيد من التفكير ، على الرغم من ذلك ، لأن طرقًا مثل:

func (v *T) Read(b []byte) (int, os.Error) { ... }`

سنقوم بإرجاع نوع له طريقة String ، لذلك نرغب عادةً في التكيف في الاتجاه المعاكس حتى يمكن إصلاح الكود تدريجيًا.

_ محدث: يحتاج إلى مزيد من التفكير.

مشكلة التضمين

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

kubernetes ، عامل ميناء

المشاكل الواردة في المنشور تبدو وكأنها اختلافات في القضايا المذكورة أعلاه ، وحلها الاقتراح.

فارز ، ثوابت

لن يكون من المنطقي تكييف المتغيرات أو الثوابت في ظل هذا السيناريو ، نظرًا لأنه لا يمكننا حقًا ربط الطرق بها بشكل مباشر. إنها أنواعها التي سيتم تكييفها أم لا.

جودوك

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

بناء الجملة

الرجاء اختيار شيء لطيف. ؛)

تضمين التغريدة

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

ملاحظتان:

1. تعتمد دلالات مراجع النوع على حالة استخدام إعادة البناء المدعومة

يوضح اقتراح Gustavo أن هناك حاجة إلى مزيد من العمل على حالة الاستخدام لمراجع النوع والدلالات الناتجة.

يتضمن اقتراح روس الجديد بناء جملة جديدًا type OldAPI = newpkg.newAPI . لكن ما هي الدلالات؟ هل من المستحيل توسيع OldAPI بأساليب أو حقول عامة قديمة؟ بافتراض "نعم" كإجابة تتطلب من واجهة برمجة التطبيقات الجديدة دعم كافة الأساليب العامة وحقول OldAPI للحفاظ على التوافق. يرجى ملاحظة أنه يجب إعادة كتابة أي كود في الحزمة مع OldAPI يعتمد على الحقول والأساليب الخاصة لاستخدام واجهة برمجة التطبيقات الجديدة العامة فقط على افتراض أن تعديل قيود الرؤية للحزم خارج الجدول.

سيكون المسار البديل هو السماح بتعريف أساليب إضافية لـ OldAPI. يمكن أن يخفف ذلك العبء على NewAPI لتوفير جميع الأساليب القديمة العامة. ولكن هذا من شأنه أن يجعل OldAPI نوعًا مختلفًا عن NewAPI. يجب الحفاظ على شكل من أشكال التخصيص بين قيم النوعين ، لكن القواعد ستصبح معقدة. قد يؤدي السماح بإضافة الحقول إلى مزيد من التعقيد.

2. لا يمكن للحزمة مع NewAPI استيراد الحزمة مع OldAPI

تتطلب إعادة تعريف OldAPI تلك الحزمة O التي تحتوي على تعريف OldAPI يستورد الحزمة N مع NewAPI. يشير هذا إلى أن الحزمة N لا يمكنها استيراد O. ربما يكون من الواضح جدًا أنه لم يتم ذكرها ، ولكن يبدو لي أنها قيد مهم في حالة استخدام إعادة البناء.

تحديث: لا يمكن أن يكون للحزمة N أي اعتماد على الحزمة O. على سبيل المثال ، لا يمكنها استيراد حزمة تستورد O.

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

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

أعتقد أنه في مرحلة ما يجب أن نناقش ما إذا كنا نفكر بالفعل في حزم الغلاف أو الواردات العامة protobuf أو فضح واجهات برمجة التطبيقات الداخلية للحزم مثل هذا الشيء السيئ (ولا أعرف أفضل طريقة لمناقشة شيء ذاتي دون أن يكرر جانب واحد فقط مرارًا وتكرارًا أنها غير قابلة للقراءة والآخر يقول "لا ليسوا كذلك". لا يوجد الكثير من الحجة الموضوعية هنا ، يبدو لي).

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

أرغب في وضع حالة استخدام أخرى مختلفة قليلاً في الاعتبار حيث يتم تطوير أي اقتراح اسم مستعار.

على الرغم من أن حالة الاستخدام الرئيسية التي نناقشها في هذه المشكلة هي النوع _replacement_ ، إلا أن الأسماء المستعارة للنوع ستكون مفيدة جدًا أيضًا لفطام مجموعة من التعليمات البرمجية عن التبعية للنوع.

على سبيل المثال ، افترض أن نوعًا ما تبين أنه "غير مستقر" (أي أنه يتغير باستمرار ، ربما بطرق غير متوافقة). ثم قد يرغب بعض المستخدمين في الترحيل إلى نوع بديل "ثابت". أفكر في التطوير على github وما إلى ذلك حيث لا يعمل مالكو النوع ومستخدميه بالضرورة عن كثب أو يتفقون على هدف الاستقرار.

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

لذلك ستكون العملية هنا:

  1. حدد الاسم المستعار للنوع
  2. قم بتغيير نص التعليمات البرمجية ذي الصلة لاستخدام الاسم المستعار للنوع
  3. استبدل الاسم المستعار للنوع بتعريف النوع.

في نهاية هذه العملية ، سيكون هناك نوعان مستقلان يمكن أن يتطوروا في اتجاهاتهم الخاصة.

لاحظ أنه في حالة الاستخدام هذه:

  • لا يوجد خيار لتغيير الحزمة التي تحتوي على تعريف النوع الأصلي لإضافة اسم مستعار للنوع هناك (حيث من غير المرجح أن يوافق الملاك على ذلك)
  • لم يتم إهمال النوع الأصلي (على الرغم من أنه يمكن اعتباره كذلك في نص الكود أثناء عملية "التخلص من" النوع).

Merovius في اللحظة التي تحذف فيها الطريقة القديمة أو تعيد تسميتها ، تقتل كل عميل كان يستخدمها مرة واحدة. إذا كنت على استعداد للقيام بذلك ، فإن التمرين غير التافه لإضافة ميزة اللغة لمنع الانكسار الكل في وقت واحد هو موضع نقاش. يمكننا أيضًا أن نقول نفس الشيء تمامًا لنقل الكود: فقط أعد تسمية النوع في كل موقع اتصال مرة واحدة. منتهي. كلا الإجراءين عبارة عن إعادة تسمية ذرية ، وهما يشتركان في حقيقة أنهما يفترضان الوصول الكامل إلى كل سطر من التعليمات البرمجية في مواقع الاتصال. قد يكون هذا هو الحال بالنسبة لـ Google ، ولكن بصفتي مشرفًا على التطبيقات والمكتبات الكبيرة مفتوحة المصدر ، فهذا ليس العالم الذي أعيش فيه.

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

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

  1. إضافة واجهة برمجة تطبيقات جديدة قابلة للتبديل مع واجهة برمجة التطبيقات القديمة
  2. قم بتغيير المستهلكين تدريجيًا إلى واجهة برمجة تطبيقات جديدة
    3 أ. بمجرد ترحيل كل شيء أو انتهاء فترة الإيقاف ، احذف واجهة برمجة التطبيقات القديمة
    3 ب. توفير استقرار غير محدد عن طريق الاحتفاظ بكل من واجهات برمجة التطبيقات (API) إلى الأبد (انظر على سبيل المثال هذا الجزء من المقالة )

يبدو أنك تتجادل حول عمل 3a مقابل 3b. ولكن ما أشرت إليه ، أن 1. ممكن بالفعل لأسماء الطرق ولكن ليس ممكنًا للأنواع ، وهو ما يدور حوله هذا.

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

ربما يمكنك توضيح شيء ما عن فكرة المحول الخاص بك بالنسبة لي ، على الرغم من: ألا يسمح ذلك أيضًا باستخدام (على سبيل المثال ، في حالة خطأ نظام التشغيل) أي fmt.Stringer كخطأ في نظام التشغيل؟

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

niemeyer نعم ، لقد

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

rsc نعم ، سيتحول المحول تلقائيًا باستمرار في كل حالة ، لذلك لن تكون مفاتيح الكتابة مختلفة. كنا نمنع مفاتيح التبديل التي تحتوي على كل من المحول والنوع الأساسي ، لأن ذلك سيكون غامضًا. قد أفتقد شيئًا ما ، لكن لا يمكنني رؤية مشكلة الانعكاس ، نظرًا لأن كل سياق رمز يحتاج بالضرورة إما إلى استخدام النوع المعدل ، أو نوعه الأساسي ، بشكل صريح. تمامًا مثل اليوم ، لا يمكننا الدخول في interface{} دون معرفة كيف وصلنا إلى هناك ، إذا كان ذلك منطقيًا.

Merovius تعالج تعليقي أعلاه بدقة النقاط التي ما زلت

niemeyer من شأنه أن يتعامل مع الأنواع الخرسانية. ماذا عن تأكيد النوع لـ .(interface{String() string}) مقابل .(interface{Error() string}) أو تغيير أي جزء محدد من الواجهة؟ هل يجب أن يأخذ الشيك في الاعتبار كلا النوعين الأساسيين المحتملين بطريقة أو بأخرى؟

niemeyer لا. إعادة تسمية طريقة ممكنة بطريقة غير ذرية. على سبيل المثال ، لنقل طريقة من A.Foo إلى A.Bar ، افعل

  1. أضف الطريقة A.Bar كمغلف حول A.Foo
  2. قم بترحيل المستخدمين للاتصال فقط بـ A.Bar عبر العديد من الالتزامات بشكل تعسفي
  3. إما أن تحذف A.Foo ، أو لا تحذفه ، اعتمادًا على ما إذا كنت على استعداد لفرض الإهمال.

تغيير دوال الوسيطات ممكن غير ذري. على سبيل المثال لإضافة معلمة x int إلى func Foo() ، افعل

  1. أضف func FooWithInt(x int) { Foo(); // use x somehow; }
  2. قم بترحيل المستخدمين لإضافة المعلمة عبر العديد من عمليات الإيداع العشوائية
  3. إذا لم تكن على استعداد لفرض إهمال (أو لم تزعجك بالحصول على WithInt) ، تكون قد انتهيت. وإلا فقم بتعديل Foo ليصبح func Foo(x int) { FooWithInt(x) } .
  4. قم بترحيل المستخدمين بـ s/FooWithInt/Foo/g عبر العديد من الالتزامات بشكل تعسفي.
  5. حذف FooWithInt .

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

الفرق بين الأنواع المتحركة اليوم وإعادة تسمية الوظيفة اليوم هو أنه في الحالة الأولى ، تحتاج فعلاً إلى تغيير ذري ، بينما بالنسبة للأخيرة ، يمكنك إجراء التغيير تدريجيًا ، عبر عمليات إعادة الشراء والالتزامات المستقلة. ليس كـ "سأقدم التزامًا لا يفعل ذلك / Foo / Bar /" ، ولكن هناك عملية للقيام بذلك.

على أي حال. لا أعرف أين نحن ، على ما يبدو ، نتحدث عن بعضنا البعض. أجد وثيقةrsc واضحة جدًا لنقل

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

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

niemeyer لكن مرة أخرى: بالنسبة للأنواع ، لا يمكنك حتى إضافة أشياء بطريقة لائقة. انظر x / image / draw. وقد لا يكون لدى الجميع مثل هذه النظرة المطلقة للاستقرار ؛ أنا ، نفسي ، لا مانع من قول "في 6،12 ، ... أشهر $ function ، $ type ، ... ستذهب بعيدًا ، تأكد من أنك قد تم ترحيلك بعيدًا عنها في تلك المرحلة" ثم قم فقط بكسر التعليمات البرمجية التي لم يتم الحفاظ عليها والتي لا تمكن من اتباع إشعار الإيقاف هذا (إذا اعتقد شخص ما أنه بحاجة إلى دعم طويل الأجل لواجهات برمجة التطبيقات ، فيمكنه بالتأكيد العثور على شخص ما للدفع لتوفير ذلك). حتى أنني أدعي أن معظم الناس ليس لديهم وجهة النظر المطلقة بشأن الاستقرار ؛ انظر إلى الدفع الأخير للإصدارات الدلالية ، وهو أمر منطقي حقًا فقط إذا كنت ترغب في الحصول على خيار كسر التوافق. ويجادل المستند جيدًا ، كيف ، حتى في هذه الحالة ، ستستمر في الاستفادة من القدرة على إجراء إصلاحات تدريجية وكيف يمكن أن تخفف ، إن لم يكن حل مشكلة اعتماد الماس بشكل أساسي.

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

nemeyerrscMerovius لقد كنت أتابع مناقشتك (والمناقشة بأكملها) وأود أن أصفق هذا المنشور بشكل صارخ في منتصفه.

كلما كررنا أكثر بشأن المشكلة ، كلما اقتربنا من شكل من أشكال دلالات التغاير الموسعة. إذن ، هنا تفكر: لدينا بالفعل دلالات من النوع الفرعي ("is-a") محددة من الأنواع الملموسة إلى الواجهات وبين الواجهات. اقتراحي هو جعل الواجهات متغيرة التكرار (وفقًا لقواعد التباين الحالية) وصولاً إلى حجج أساليبها.

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

أعتقد أنه يمكننا حل جميع المتطلبات عن طريق (ab) باستخدام الواجهات بهذه الطريقة. هل نكسر Go 1.0؟ لا أعلم ولكن أعتقد أننا لسنا كذلك.

thwd أعتقد أنك بحاجة إلى تعريف أكثر دقة لما

thwd لا أوافق ، أن الواجهات (حتى تلك المتغايرة) هي حل جيد لأي من هذه المشاكل (فقط لحالات محددة جدًا منها). لجعلها واحدة ، ستحتاج إلى جعل كل شيء في واجهة برمجة التطبيقات الخاصة بك واجهة (لأنك لا تعرف أبدًا ما قد ترغب في نقله / تغييره في وقت ما) ، بما في ذلك vars / consts / funcs / ... ولا أفكر في كل ذلك ، هذا تصميم جيد (لقد رأيت ذلك في جافا. إنه يزعجني). إذا كان هناك شيء ما عبارة عن هيكل ، فقط اجعله هيكلاً. كل شيء آخر يضيف فقط عبء نحوي غريب في الحزمة الخاصة بك وكل تبعية عكسية بدون فائدة تقريبًا. إنها أيضًا الطريقة الوحيدة للبقاء عاقلاً عندما تبدأ ؛ ابدأ بسيطًا وانتقل إلى شيء أكثر عمومية لاحقًا. الكثير من التعقيدات في API التي رأيتها حتى الآن تأتي من الأشخاص الذين يفكرون في تصميم واجهة برمجة التطبيقات (API) والتخطيط لطريقة أكثر عمومية مما ستكون هناك حاجة إليه في أي وقت مضى. وبعد ذلك ، في 80٪ (هذا الرقم كذبة واضحة) من الحالات ، لا يحدث شيء على الإطلاق ، لأنه لا يوجد "تصميم نظيف لواجهة برمجة التطبيقات".

(لكي أكون واضحًا: أنا لا أقول أن الواجهات المتغيرة ليست فكرة جيدة. أنا أقول فقط إنها ليست حلاً جيدًا لهذه المشكلات)

للإضافة إلى نقطة Merovius ،

package foo

type Authority struct {
  Host string
  Port int
}

بمرور الوقت ، تنمو حزمة foo وينتهي بها الأمر باكتساب المزيد من المسؤولية (وحجم الكود) أكثر من شخص يحتاج فقط إلى النوع Authority يريده حقًا. لذا فإن وجود طريقة لإنشاء حزمة fooauthority تحتوي فقط على Authority ووجود مستخدمين حاليين من foo.Authority لا يزالون يعملون هو حالة استخدام مرغوبة. لاحظ أن أي حل يأخذ في الاعتبار أنواع الواجهة فقط لن يساعد هنا.

Merovius كان تعليقك الأخير شخصيًا تمامًا وهو

تضمين التغريدة لإغلاق الحلقة ، إذن ، يمكننا أن نتفق على أن المناقشة حتى الآن قد قادتنا إلى بعض فكرة الأنواع الفرعية / التغاير. أيضًا ، يجب ألا يؤدي أي تطبيق له إلى أي مراوغات في وقت التشغيل. هذا نوع من ما كان يقترحه niemeyer (إذا فهمته بشكل صحيح). لكني أحب قراءة المزيد من الأفكار. سأفكر في المشكلة أيضًا.

niemeyer لم يكن هناك شيء _ad hominem_ في تعليقات @ Mirovius . ادعائه بأن "موقفك من الاستقرار مطلق" هو ​​ملاحظة حول موقفك وليس أنت ، وهو استنتاج معقول من بعض أقوالك ، مثل

في اللحظة التي تحذف فيها الطريقة القديمة أو تعيد تسميتها ، تقتل كل عميل كان يستخدمها مرة واحدة.

و

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

لدي نفس الانطباع مثل Merovius من تلك العبارات - بأنك لا تتعاطف مع الانتقاص من شيء ما لفترة ، ثم إزالته في النهاية ؛ أنك ملتزم بالحفاظ على التعليمات البرمجية في البرية تعمل إلى أجل غير مسمى ؛ أن "موقفك من الاستقرار مطلق". (ولتجنب المزيد من سوء الفهم ، أستخدم كلمة "أنت" للإشارة إلى أفكارك ، وليس شخصيتك.)

niemeyer يبدو adapts وثيق الصلة بـ instance من فئات كتابة هاسكل. عند ترجمة ذلك إلى Go بشكل فضفاض ، قد يبدو الأمر كما يلي:

package os

type Error interface {
  String() string
}

instance error Error (
  func (e error) String() string { return e.Error() }
)

لسوء الحظ (كما تلاحظ @ zombiezen ) ، ليس من الواضح كيف يمكن أن يساعد ذلك في الأنواع التي لا تحتوي على واجهة.

كما أنه ليس واضحًا بالنسبة لي كيف ستتفاعل مع أنواع الوظائف (الوسائط وقيم الإرجاع) ؛ على سبيل المثال ، كيف يمكن أن تساعد دلالات adapts ترحيل Context إلى المكتبة القياسية؟

لدي انطباع مماثل مثل Merovius من تلك العبارات - بأنك لست متعاطفًا مع الانتقاص من شيء لفترة من الوقت

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

تضمين التغريدة

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

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

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

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

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

أقترح أيضًا أن نوحد مفهوم الإهمال في مناقشة موازية ودعم الأرض لهم في أدواتنا الأساسية (golint ، godoc ، إلخ). يعد عدم وجود إشعارات الإهمال أكبر مشكلة في نظام Go وهو أكثر انتشارًا من مشكلة الإصلاح التدريجي للكود.

rakyll أنا متعاطف مع حالة استخدام إشعارات إهمال يمكن قراءتها بواسطة الكمبيوتر ؛ أنا فقط أعترض على فكرة أ) الأسماء المستعارة هي ذلك و ب) إرسالها كتحذيرات للمترجم.

بالنسبة إلى أ) ، بصرف النظر عن حقيقة أنني أرغب في استخدام الأسماء المستعارة بشكل منتج لأشياء أخرى غير الحركات ، فإنها تنطبق أيضًا فقط على مجموعة صغيرة جدًا من الإهمال. على سبيل المثال ، لنفترض أنني أريد إزالة بعض المعلمات من وظيفة في إصدارين ؛ لا يمكنني استخدام الأسماء المستعارة ، حقًا ، لأن توقيع واجهة برمجة التطبيقات الجديدة سيختلف ، لكنني ما زلت أريد الإعلان عن ذلك. ل) ، تحذيرات المترجم IMHO سيئة عالميا. أعتقد أن هذا يتماشى في الغالب مع ما تفعله شركة go بالفعل ، لذلك لا أعتقد أنه يتطلب تبريرًا.

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

rakyll أوافق على أن stdlib يجب أن يبدأ باستخدام محافظ للأسماء المستعارة من النوع ، في حالة تقديمها.


الشريط الجانبي:

خلفية لمن ليسوا على دراية بحالة تعليقات الإهمال في Go والأدوات ذات الصلة ، حيث إنها منتشرة إلى حد ما:

كما ذكر Merovius أعلاه ، هناك اصطلاح قياسي https://blog.golang.org/godoc-documenting-go-code

TL؛ DR: أنشئ فقرة في مستندات العنصر الموقوف تبدأ بعبارة "مهمل:" وتشرح ما هو الاستبدال.

هناك اقتراح مقبول لـ godoc لعرض العناصر المهملة بطريقة أكثر فائدة: # 17056.

اقترح rakyll أن يحذر golint عند استخدام العناصر المهملة: golang / lint # 238.


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

القيام بذلك يعني واحدًا مما يلي:

  • فحص pakages stdlib الأخرى لمعرفة ما إذا كان أي نوع ، لم يتم وضع علامة صراحة على أنه مهمل ، يحمل اسمًا مستعارًا في مكان آخر
  • تشفير جميع الأسماء المستعارة stdlib في أدوات آلية
  • فقط الإبلاغ عن إهمال النوع القديم عندما تبحث بالفعل عن البديل ، وهذا لا يساعد في الاكتشاف

عندما يتم تقديم اسم مستعار للنوع بسبب إهمال النوع القديم ، يجب التعامل معه عن طريق وضع علامة على النوع القديم الذي تم إهماله ، مع الإشارة إلى النوع الجديد ، بغض النظر.

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

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

niemeyer ضاع ردي السابق بسبب فقدان الطاقة :( خارج الترتيب:

لكني أكرر نفسي ..

FWIW ، لقد وجدت ردك الأخير مفيدًا جدًا. لقد أقنعتني ، بأننا متفقون أكثر مما بدا في السابق (وربما لا يزال يبدو لك). ومع ذلك ، يبدو أنه لا يزال هناك سوء تفاهم في مكان ما.

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

هذا غير مثير للجدل ، على ما أعتقد. :) لقد وافقت ، منذ البداية ، على أن اقتراحك هو بديل مثير للاهتمام يجب مراعاته لمعالجة المشكلة. ما يحيرني هو عبارات مثل هذا:

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

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

ولكي نكون منصفين: لا تساعد جمل مثل الارتباك حقًا

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

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

على أي حال. أجد هذا استنزافًا مثلك تمامًا. آمل أن أفهم موقفك في مرحلة ما ، رغم ذلك.

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

nit: لا أعتقد أنه يجب ذكر الواردات العامة protobuf كحالة استخدام ثانوية خاصة. لقد تم تصميمها للإصلاح التدريجي للكود ، كما هو مذكور صراحة في كل من مستند التصميم الداخلي وحتى الوثائق العامة ، لذا فهي تقع بالفعل تحت مظلة المشكلات الموضحة في هذه المشكلة. أيضًا ، أعتقد أن الأسماء المستعارة من النوع ستكون كافية لتنفيذ عمليات الاستيراد العامة protobuf. (يقوم المترجم الأولي بإنشاء متغيرات ، لكنها ثابتة منطقيًا ، لذلك يجب أن يكون "var Enum_name = import.Enum_name" كافياً.)

@ Merovius شكرا على الاستجابة المثمرة. اسمحوا لي أن أحاول تقديم بعض السياق:

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

لم يقل أبدا أنه لا يمكن أن يحدث. اسمحوا لي أن أعود خطوة إلى الوراء وأعيد التأكيد بشكل أكثر وضوحًا.

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

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

هناك طريقة أخرى للنظر إلى هذا المكان الجميل وهي احتمال وجود برامج تعمل. لا يهم كم نطلب من الناس عدم استخدام طريقة مهملة. إذا كان من السهل الوصول إليه وحل مشكلتهم هنا والآن ، فسيستخدمه معظم المطورين. الحجة المضادة الشائعة هنا هي: أوه ، لكن هذه هي مشكلتهم عند تعطلها! _ لكن هذا يتعارض مع الهدف المعلن: نريد برنامجًا يعمل ، لا أن نكون على صواب.

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

شكرا على الشرح. أعتقد أنني أفهم موقفك بشكل أفضل الآن وأتفق مع افتراضاتك (أي أن الناس سيستخدمون أشياء مهملة بغض النظر عن السبب ، لذا فإن تقديم أي مساعدة ممكنة لإرشادهم إلى الاستبدال أمر بالغ الأهمية). FWIW ، خطتي الساذجة للتعامل مع هذه المشكلة (بغض النظر عن الحل للإصلاح التدريجي الذي سنتناوله) هي أداة تشبه go-fix لترحيل حزمة التعليمات البرمجية تلقائيًا حزمة تلو الأخرى في فترة الإيقاف ، لكنني أعترف بذلك بحرية. لم أجرب بعد كيف وما إذا كان ذلك يعمل في الممارسة.

niemeyer لا أعتقد أن اقتراحك قابل للتطبيق دون حدوث اضطراب خطير في نظام النوع Go.

ضع في اعتبارك المعضلة التي يقدمها هذا القانون:

package old
import "new"
type A adapts new.A
func (a A) NewA() {}

package new
type A struct{}
func (a A) OldA() {}

package main
import (
    "new"
    "old"
    "reflect"
)
func main() {
    oldv := reflect.ValueOf(old.A{})
    newv := reflect.ValueOf(new.A{})
    if oldv.Type() == newv.Type() {
        // The two types are equal, therefore they must
        // have exactly the same method set, so either
        // oldv doesn't have the OldA method or newv doesn't
        // have the NewA method - both of which imply a contradiction
        // in the type system.
    } else {
         // The two types are not equal, which means that the
         // old adapted type is not fully compatible with the old
         // one. Any type that includes either new.A or new.B will
         // be incompatible as one of its components will likewise be
         // unequal, so any code that relies on dynamic type checking
         // will fail when presented with the type that's not using the
         // expected version.
    }
 }

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

rogpeppe شاهد المحادثة مع rsc حول الانعكاس أعلاه. النوعان ليسا متماثلين ، لذا فإن الانعكاس سيقول الحقيقة فقط ويقدم تفاصيل للمحول عندما يُسأل عنه.

niemeyer إذا النوعان متماثلين ، فلا أعتقد أنه يمكننا دعم إصلاح الكود التدريجي أثناء نقل نوع بين الحزم. على سبيل المثال ، لنفترض أننا أردنا إنشاء حزمة صور جديدة تحافظ على توافق النوع.

قد نفعل:

package newimage
import "image"
type RGBA adapts image.RGB
func (r *RGBA) At(x, y) color.Color {
    return (*image.Buffer)(r).At(x, y)
}
etc for all the methods

بالنظر إلى هدف الإصلاح التدريجي للكود ، أعتقد أنه من المعقول توقع ذلك
صورة تم إنشاؤها في الحزمة الجديدة متوافقة مع الوظائف الحالية
التي تستخدم نوع الصورة القديم.

دعنا نفترض من أجل الجدل أن حزمة image / png بها
تم تحويلها لاستخدام newimage ولكن image / jpeg لم يتم تحويلها.

أعتقد أننا يجب أن نتوقع أن يعمل هذا الرمز:

img, err := png.Decode(r)
if err != nil { ... }
err = jpeg.Encode(w, img, nil)

ولكن ، نظرًا لأنه يقوم بتأكيد نوع مقابل * image.RGBA ليس * newimage.RGBA ،
سوف تفشل AFAICS ، لأن الأنواع مختلفة.

لنفترض أننا جعلنا تأكيد النوع أعلاه ينجح ، سواء كان النوع * image.RGBA
أم لا. هذا من شأنه كسر الثابت الحالي:

reflect.TypeOf (x) == reflect.TypeOf (x. (anyStaticType))

وهذا يعني أن استخدام تأكيد النوع الثابت لن يؤكد فقط النوع الثابت لملف
قيمة ولكن في بعض الأحيان قد يغيرها في الواقع.

لنفترض أننا قررنا أن ذلك كان جيدًا ، فمن المفترض أننا سنحتاج أيضًا
لجعل من الممكن تحويل نوع مكيف إلى أي واجهة متوافقة مع أي منها
تدعم الأنواع المُكيَّفة ، وإلا فسيتوقف الكود الجديد أو القديم
العمل عند التحويل إلى أنواع واجهات متوافقة مع
اكتب يستخدمونه.

هذا يؤدي إلى موقف متناقض آخر:

// oldInterface is some interface with methods that
// are only supported by the old type.
type oldInterface interface {
    OldMethod()
}
var x = interface{} = newpackage.Type{}
switch x.(type) {
case oldInterface:
    // This would fail because the newpackage.Type
    // does not implement OldMethod, even though we
    // we just supposedly checked that x implements OldMethod.
    reflect.TypeOf(x).Method("OldMethod")
}

بشكل عام ، أعتقد أن وجود نوعين متشابهين ولكن مختلفين
قد يؤدي إلى صعوبة بالغة في شرح نظام الكتابة وعدم التوافق غير المتوقع
في التعليمات البرمجية التي تستخدم أنواعًا ديناميكية.

أنا أؤيد اقتراح "النوع X = Y". من السهل أن تشرح ولا تفعل ذلك
تعطل نظام الكتابة كثيرًا.

rogpeppe : أعتقد أن اقتراحniemeyer هو تحويل نوع معدّل ضمنيًا إلى نوعه الأساسي ، على غرار اقتراحاتjosharian السابقة .

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

يؤدي هذا إلى بعض حالات الحافة المثيرة للاهتمام ، كما لاحظت ، لكنها ليست بالضرورة "متناقضة" في حد ذاتها:

type oldInterface interface {
    OldMethod()
}
var x = interface{} = newpackage.Type{}
switch y := x.(type) {
case oldInterface:
    reflect.TypeOf(y).Method("OldMethod")  // ok
    reflect.TypeOf(x).Method("NewMethod")  // ok

    // This would fail because y has been implicitly converted to oldInterface.
    reflect.TypeOf(y).Method("NewMethod")

    // This would fail because accessing OldMethod on newpackage.Type requires
    // a conversion to oldInterface.
    reflect.TypeOf(x).Method("OldMethod")
}
// This would fail because accessing OldMethod on newpackage.Type requires
// a conversion to oldInterface.

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

يبدو أن المناقشة هنا تنتهي. بناءً على اقتراح من egonelbre في https://github.com/golang/go/issues/16339#issuecomment -247536289 ، لقد قمت بتحديث تعليق المشكلة الأصلي (في الجزء العلوي) لتضمين ملخص مرتبط بالمناقشة لذلك بعيد. سوف أنشر تعليقًا جديدًا ، مثل هذا ، في كل مرة أقوم فيها بتحديث الملخص.

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

ما زلت غير مقتنع بأن المتغيرات العالمية المتغيرة "عادة ما تكون خطأ" (وفي الحالات التي تكون فيها خلل ، فإن كاشف السباق هو الأداة المفضلة للعثور على هذا النوع من الأخطاء). أود أن أطلب ، إذا تم استخدام هذه الوسيطة لتبرير عدم وجود بناء جملة قابل للتوسيع ، يتم تنفيذ فحص بيطري - على سبيل المثال - يتحقق من التخصيصات للمتغيرات العالمية في التعليمات البرمجية التي لا يمكن الوصول إليها حصريًا عن طريق init () أو إعلاناتها. أعتقد بسذاجة أن هذا ليس صعب التنفيذ بشكل خاص ولا ينبغي أن يكون هناك الكثير من العمل لتشغيله - لنقل - جميع الحزم المسجلة في godoc.org لمعرفة حالات استخدام المتغيرات العالمية القابلة للتغيير وما إذا كنا نقوم بذلك اعتبر كل منهم البق.

(أود أيضًا أن أصدق أنه إذا كان go ينمو متغيرات عالمية ثابتة ، فيجب أن يكونوا جزءًا من تصريحات ثابتة ، لأن هذا هو ما هي عليه من الناحية المفاهيمية ولأن ذلك سيكون متوافقًا مع الإصدارات السابقة ، لكنني أقر بأن هذا سيؤدي على الأرجح إلى تعقيدات حول نوع التعبيرات التي يمكن استخدامها في أنواع المصفوفات ، على سبيل المثال وستحتاج إلى مزيد من التفكير)

إعادة "تقييد؟ لا يمكن التصريح عن الأسماء المستعارة لأنواع المكتبات القياسية إلا في المكتبة القياسية." - على وجه الخصوص ، من شأن ذلك أن يمنع حالة الاستخدام المنسدلة لـ x/image/draw ، وهي حزمة موجودة أبدت اهتمامًا باستخدام الأسماء المستعارة. يمكنني أيضًا أن أتخيل جيدًا ، على سبيل المثال ، حزم أجهزة التوجيه أو ما شابه ذلك باستخدام الأسماء المستعارة إلى net/http بطريقة مماثلة ( عقارب موجات ).

كما أنني أتفق مع الحجج المضادة Re جميع القيود ، أي أنا أؤيد عدم وجود أي من هؤلاء.

@ ميروفيوس ، ماذا عن المتغيرات العالمية _exported_ المتغيرة؟ صحيح أن العالم غير المُصَدَّر قد يكون جيدًا لأن كل التعليمات البرمجية الموجودة في الحزمة تعرف كيفية التعامل معها بشكل صحيح. من غير الواضح أن الكرة الأرضية المصدرة القابلة للتغيير تبدو منطقية على الإطلاق. لقد ارتكبنا هذا الخطأ بأنفسنا عدة مرات في المكتبة القياسية. على سبيل المثال ، لا توجد طريقة آمنة تمامًا لتحديث وقت التشغيل. أفضل ما يمكنك القيام به هو تعيينه في وقت مبكر من برنامجك وتأمل ألا تؤدي أي حزمة قمت باستيرادها إلى بدء تشغيل goroutine التهيئة الذي قد يخصص الذاكرة. قد تكون محقًا بشأن var vs const ، لكن يمكننا ترك ذلك ليوم آخر.

نقطة جيدة حول x / صورة / رسم. سيضيف إلى الملخص في التحديث القادم.

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

rsc تم نشر عرض GothamGo الخاص بك حول هذا الموضوع على موقع youtube https://www.youtube.com/watch؟v=h6Cw9iCDVcU وسيشكل إضافة جيدة للمنشور الأول.

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

Merovius يمكن محاكاة أي متغير متغير للحزمة العالمية المصدرة بواسطة وظائف getter و setter على مستوى الحزمة.

إعطاء الإصدار n من الحزمة p ،

package p
var Global = 0

في الإصدار n + 1 ، يمكن تقديم المحاضر والمحددات وإيقاف المتغير

package p
//Deprecated: use GetGlobal and SetGlobal.
var Global = 0
func GetGlobal() int {
    return Global
}
func SetGlobal(n int) {
   Global = n
}

والإصدار n + 2 يمكن إلغاء تصدير Global

package p
var global = 0
func GetGlobal() int {
    return global
}
func SetGlobal(n int) {
   global = n
}

(تمرين للقارئ: يمكنك أيضًا التفاف الوصول إلى global في كائن المزامنة في n + 2 وإيقاف GetGlobal() لصالح Global() الأكثر اصطلاحًا.)

هذا ليس حلاً سريعًا ، لكنه يقلل من المشكلة بحيث تكون الأسماء المستعارة func فقط (أو حلها الحالي) ضرورية تمامًا لإصلاح الكود التدريجي.

rsc أحد الاستخدامات

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

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

وافق @ Merovius على جميع النقاط. أنا لست سعيدًا بذلك أيضًا ولكن يجب اتباع المنطق v☹v

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

rogpeppe لاحظ أن هذا هو بالضبط ما يحدث اليوم:

type two one

هذا يجعل one و two نوعين مستقلين ، وسواء كانا يعكسان أو أقل من interface{} ، هذا ما تراه. يمكنك أيضًا التحويل بين one و two . اقتراح المحول أعلاه يجعل هذه الخطوة الأخيرة تلقائية للمحولات. قد لا يعجبك الاقتراح لأسباب متعددة ، ولكن لا يوجد أي تناقض في ذلك.

iand كما في حالة type two one ، يحتوي النوعان على مجموعات طرق مستقلة تمامًا ، لذلك لا يوجد شيء مميز حول مطابقة الأسماء. قبل ترحيل قواعد التعليمات البرمجية القديمة ، ستظل تستخدم التوقيع القديم تحت النوع السابق (الآن محول). سيستخدم الرمز الجديد باستخدام النوع الجديد التوقيع الجديد. يؤدي تمرير قيمة من النوع الجديد إلى التعليمات البرمجية القديمة إلى تعديلها تلقائيًا لأن المترجم يعرف أن الأخير هو محول من النوع الأول ، وبالتالي يستخدم مجموعة الأساليب المعنية.

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

jimmyfrasche فيما يتعلق

تم تحديث ملخص مناقشة المستوى الأعلى . التغييرات:

  • تمت إضافة رابط إلى فيديو GothamGo
  • تمت إضافة "اختصار الأسماء الطويلة" كاستخدام محتمل ، لكلjba.
  • تمت إضافة x / image / draw كوسيطة ضد قيود المكتبة القياسية ، وفقًا لـMerovius.
  • تمت إضافة المزيد من النص حول الأساليب على الأسماء المستعارة ، فيjimmyfrasche.

تمت إضافة مستند التصميم: golang.org/design/18130-type-alias

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

بعد عملية الاقتراح ، يرجى نشر التعليقات الموضوعية على الاقتراح _هنا_ حول هذه المسألة. يمكن أن تنتقل التهجئة / القواعد النحوية / إلخ إلى صفحة معاينة كود Gerrit https://go-review.googlesource.com/#/c/34592/. شكرا.

أود إعادة النظر في "التأثير على التضمين". إنه يحد من قابلية استخدام الأسماء المستعارة للإصلاح التدريجي للكود. أي ، إذا كان p1 يريد إعادة تسمية نوع type T1 = T2 وحزمة p2 embeds p1.T2 في بنية ، فلن يتمكنوا أبدًا من تحديث هذا التعريف إلى p1.T1 ، لأن المستورد p3 قد يشير إلى البنية المضمنة بالاسم. p2 إذن لا يمكن التبديل إلى p1.T1 بدون كسر p3 ؛ p3 تحديث الاسم إلى p1.T1 بدون كسر الاسم الحالي p2 .

قد تكون طريقة الخروج من ذلك ، إلى) أ) بشكل عام حد أي وعد بفترة توافق / إهمال للرمز الذي لا يشير إلى الحقول المضمنة بالاسم ، أو ب) إضافة مرحلة إهمال منفصلة ، لذلك p1 يضيف type T1 = T2 وإهمال T2 ، ثم p2 بالإشارة إلى (على سبيل المثال) s2.T2 بالاسم ، سيتم إصلاح جميع المستوردين البالغ عددهم p2 لعدم القيام بذلك ، ثم p2 يقوم بالتبديل.

الآن ، من الناحية النظرية ، يمكن أن تتكرر المشكلة إلى أجل غير مسمى. p4 باستيراد p3 ، والذي يقوم بدوره بتضمين النوع من p2 ؛ يبدو لي أن p3 يحتاج أيضًا إلى فترة إهمال ، للإشارة إلى الحقل المضمن مرتين بالاسم؟ في هذه الحالة ، تصبح فترة الإهمال الداخلية متناهية الصغر أو تصبح الأبعد لانهائية. ولكن حتى بدون اعتبار المشكلة متكررة ، يبدو لي أن ب) سيكون من الصعب جدًا تحديد الوقت (فترة الإيقاف البالغة p2 يجب احتواؤها بالكامل في فترة الإيقاف البالغة p1 . لذلك إذا كانت T هي "فترة إهمال قياسية" ، فسيتعين عليك اختيار 2 تيرابايت على الأقل عند إعادة تسمية الأنواع ، بحيث يتم ترتيب هذه الإصدارات).

أ) يبدو أيضًا غير عملي بالنسبة لي ؛ على سبيل المثال ، إذا كان النوع يتضمن *byte.Buffer وأريد تعيين هذا الحقل (أو تمرير ذلك المخزن المؤقت إلى وظيفة أخرى) ، فلا توجد طريقة للقيام بذلك ، دون الرجوع إليه بالاسم (باستثناء استخدام مُهيئ البنية بدون أسماء ، مما يفقد أيضًا ضمانات التوافق :)).

أتفهم جاذبية التوافق مع byte و rune كأسماء مستعارة. لكن ، شخصيًا ، سأضع ذلك ثانويًا للحفاظ على فائدة الأسماء المستعارة للإصلاحات التدريجية. مثال (ربما سيئًا) لفكرة الحصول على كليهما ، بالنسبة للأسماء المصدرة ، تسمح باستخدام أي اسم مستعار للإشارة إلى حقل مضمن وللأسماء غير المصدرة (مقيدة بطبيعتها بالحزمة نفسها ، وبالتالي تحت سيطرة أكبر للمؤلف ) الاحتفاظ بالدلالات المقترحة حاليًا؟ نعم ، أنا لا أحب هذا التمييز أيضًا. ربما شخص ما لديه فكرة أفضل.

أساليب rsc re على اسم مستعار

إذا كان لديك نوع S يعد اسمًا مستعارًا للنوع T ، وكلاهما معرف في نفس الحزمة ، وتسمح بتعريف الطرق على S ، فماذا لو كان T اسمًا مستعارًا لـ pF معرّف في حزمة مختلفة؟ في حين أنه من الواضح أن هذا يجب أن يفشل أيضًا ، إلا أن هناك بعض التفاصيل الدقيقة في التنفيذ والتنفيذ وقابلية القراءة للمصدر الذي يجب مراعاته (إذا كان T في ملف مختلف عن S ، فليس من الواضح على الفور ما إذا كان يمكنك تحديد طريقة على T من خلال النظر في تعريف T).

القاعدة - إذا كان لديك type T = S ، فلا يمكنك التصريح عن طرق على T - فهي مطلقة ويتضح من ذلك السطر الفردي في المصدر أنها تنطبق ، دون الحاجة إلى التحقق من مصدر S ، كما تفعل في الاسم المستعار لحالة الاسم المستعار.

علاوة على ذلك ، فإن السماح بالطرق الموجودة على اسم مستعار من النوع المحلي يخل بالتمييز بين الاسم المستعار للنوع وتعريف النوع. نظرًا لأنه سيتم تعريف الأساليب على كل من S و T على أي حال ، فإن القيد الذي يمكن كتابته فقط على واحد لا يقيد ما يمكن التعبير عنه. إنه يبقي الأمور أبسط وأكثر اتساقًا ..

jimmyfrasche إذا كنا نكتب type T1 = T2 وكان T2 في نفس الحزمة ، فمن المحتمل أننا سنقوم بإهمال الاسم T2. في هذه الحالة ، نريد أقل عدد ممكن من تكرارات T2 في Godoc. لذلك نود أن نعلن أن جميع الطرق هي func (T1) M() .

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

jba في هذه الحالة ، لماذا لا تقوم فقط بعكس اتجاه الاسم المستعار؟ يسمح لك type T2 = T1 بالفعل بتعريف الطرق على T1 بنفس بنية الحزمة ؛ الاختلاف الوحيد هو اسم النوع الذي تم الإبلاغ عنه بواسطة الحزمة reflect ، ويمكنك بدء الترحيل عن طريق إصلاح مواقع الاتصال الحساسة للاسم لتكون غير حساسة للاسم قبل إضافة الاسم المستعار.

jimmyfrasche من وثيقة الاقتراح :

"نظرًا لأن T1 هي مجرد طريقة أخرى لكتابة T2 ، فليس لها مجموعة خاصة بها من إعلانات الأسلوب. بدلاً من ذلك ، فإن مجموعة طرق T1 هي نفسها مجموعة T2. على الأقل بالنسبة للتجربة الأولية ، لا توجد قيود على إعلانات الطريقة باستخدام T1 كـ سيكون نوع جهاز الاستقبال ، المقدم باستخدام T2 في نفس الإعلان ، صالحًا "

استخدام pF كنوع من مستقبل الأسلوب ليس صالحًا أبدًا.

mdempsky لم أكن واضحًا جدًا ، لكنني قلت إنه غير صالح.

نقطتي هي أنه من غير الواضح ما إذا كان صحيحًا أم لا بمجرد النظر إلى هذا السطر المحدد من التعليمات البرمجية.

بالنظر إلى type S = T ، عليك أيضًا أن تنظر إلى T للتأكد من أنه ليس أيضًا اسمًا مستعارًا يسمي نوعًا في حزمة أخرى. المكسب الوحيد هو التعقيد.

دائمًا ما يكون عدم السماح بالطرق على الاسم المستعار أبسط وأسهل في القراءة ولن تفقد أي شيء. لا أتخيل أن حالة مربكة ستظهر في كثير من الأحيان ، ولكن لا داعي لتقديم الاحتمال عندما لا تكسب أي شيء لا يمكن التعامل معه بشكل أفضل في مكان آخر أو من خلال نهج مختلف ولكن مكافئ.

تضمين التغريدة

إذا أراد p1 إعادة تسمية نوع النوع T1 = T2 والحزمة p2 تضمّن p1.T2 في بنية ، فلن يتمكنوا أبدًا من تحديث هذا التعريف إلى p1.T1 ، لأن المستورد p3 قد يشير إلى البنية المضمنة بالاسم.

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

قد يكون الخيار الآخر هو إضافة ميزة ثانية للتعويض. إذا كان بإمكانك اعتماد مجموعة أساليب الحقل دون جعله مجهولاً (أو مع إعادة تسمية صريحة) ، فإن ذلك سيسمح لاسم الحقل بالبقاء دون تغيير حتى مع تغيير النوع الأساسي.

النظر في الإعلان من المثال الخاص بك:

package p2

type S struct {
  p1.T2
}

قد تكون إحدى الميزات التعويضية هي "الأسماء المستعارة للحقل" ، والتي تتبع بنية مماثلة لكتابة الأسماء المستعارة:

package p2

type S struct {
  p1.T1
  T2 = T1  // field T2 is an alias for field T1.
}

var s S  // &s.T2 == &s.T1

قد تكون الميزة التعويضية الأخرى هي "التفويض" ، والتي من شأنها أن تتبنى بشكل صريح مجموعة أساليب حقل مجهول:

package p2

type S struct {
  T2 p1.T1 delegated  // T2 is a field of type T1.
  // The method set of S includes the method set of T1 and forwards those calls to field T2.
}

أعتقد أنني أفضل الأسماء المستعارة للمجال بنفسي ، لأنها ستمكّن أيضًا من نوع آخر من الإصلاح التدريجي: إعادة تسمية حقول البنية دون إدخال أخطاء في التعرّف أو الاتساق.

Merovius المشكلة الرئيسية هي إعادة تسمية النوع بواسطة اسم مستعار.

لم أفكر في هذا بالكامل - بالكاد عابر ، مجرد فكرة عشوائية:

ماذا لو أدخلت اسمًا مستعارًا في الحزمة الخاصة بك يعيد تسميتها وتضمينها؟

لا أعرف ما إذا كان هذا سيؤدي إلى إصلاح أي شيء ولكن ربما يشتري بعض الوقت لكسر الحلقة؟

bcmills لم أفكر في هذا الحل ، شكرًا. أعتقد أن التحذير بشأن الأساليب غير المُصدرة (بالنسبة لي) نادرًا ما يظهر في الممارسة العملية بحيث لا يؤثر في رأيي بشكل عام (إلا إذا لم أفهمه تمامًا. لا تتردد في التوضيح ، إذا كنت تعتقد أن هذا مفيد ). لا أعتقد أن تكديس المزيد من التغييرات له ما يبرره (أو فكرة جيدة).

Merovius كلما فكرت في الأمر ، زادت

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

تضمين التغريدة

إذا أراد p1 إعادة تسمية نوع النوع T1 = T2 والحزمة p2 تضمّن p1.T2 في بنية ، فلن يتمكنوا أبدًا من تحديث هذا التعريف إلى p1.T1 ، لأن المستورد p3 قد يشير إلى البنية المضمنة بالاسم. p2 ثم لا يمكن التبديل إلى p1.T1 دون كسر p3 ؛ لا يمكن لـ p3 تحديث الاسم إلى p1.T1 دون الانفصال عن p2 الحالي.

إذا فهمت مثالك ، فلدينا:

package p1

type T2 struct {}
type T1 = T2
package p2

import "p1"

type S struct {
  p1.T2
  F2 string // see below
}

أعتقد أن هذا مجرد مثال محدد للحالة العامة حيث نرغب في إعادة تسمية حقل هيكلي ؛ تنطبق نفس المشكلة إذا أردنا إعادة تسمية S.F2 إلى S.F1.

في هذه الحالة المحددة ، قد نقوم بتحديث الحزمة p2 لاستخدام واجهة برمجة التطبيقات الجديدة لـ p1 باسم مستعار من النوع المحلي:

package p2

import "p1"

type T2 = p1.T1

type S struct {
  T2
}

هذا بالطبع ليس إصلاحًا جيدًا على المدى الطويل. لا أعتقد أن هناك أي طريقة للتغلب على حقيقة أن p2 ستحتاج إلى تغيير واجهة برمجة التطبيقات التي تم تصديرها لإزالة اسم T2 ، ومع ذلك ، والذي سيستمر بنفس طريقة إعادة تسمية أي حقل.

مجرد ملاحظة بخصوص "نقل الأنواع بين الحزم". أليست هذه الصيغة إشكالية بعض الشيء؟

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

لا يحرك تعريف الكائن ، أليس كذلك؟ (ما لم يكتب أحد الرموز باستخدام الأسماء المستعارة في المقام الأول ، وفي هذه الحالة ، يكون للمستخدم الحرية في تغيير المكان الذي يشير إليه الاسم المستعار ، تمامًا كما هو الحال في رسم pkg).

atdiar يمكن استخدام الإشارة إلى نوع في حزمة مختلفة كخطوة لنقل النوع. نعم ، الاسم المستعار لا يحرك الكتابة ، ولكن يمكن استخدامه كأداة للقيام بذلك.

Merovius القيام بذلك من المحتمل أن يكسر التفكير والإضافات.

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

أخيرًا ، اقتراح مفيد ومكتوب جيدًا. نحتاج إلى اسم مستعار من النوع ، لدي مشكلات كبيرة في إنشاء واجهة برمجة تطبيقات واحدة بدون اسم مستعار من النوع ، حتى الآن ، لا بد لي من كتابة الكود الخاص بي بطريقة لا أحبها كثيرًا لتحقيق ذلك. يجب تضمين هذا عند go v1.8 ولكن لم يفت الأوان أبدًا ، لذا استمر في 1.9.

تضمين التغريدة
أنا أتحدث صراحة عن "نقل الأنواع" بين الحزم. يغير تعريف الكائن. على سبيل المثال ، في انعكاس pkg ، يتم ربط بعض المعلومات بالحزمة التي تم تعريف الكائن فيها.
إذا قمت بنقل التعريف ، فقد ينكسر.

kataras لا يتعلق الأمر حقًا

atdiar مرة أخرى ، يرجى قراءة المقال من التعليق الأصلي والمناقشة حتى الآن. أنواع التنقل وكيفية معالجة مخاوفك هي الشغل الشاغل لهذا الموضوع. إذا كنت لا تشعر أن مقالة روس تتناول مخاوفك بشكل كافٍ ، فيرجى أن تكون محددًا بشأن سبب عدم رضاك ​​عن تفسيره. :)

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

Merovius لقد قرأت المستند. لا يجيب على سؤالي. أعتقد أنني كنت صريحًا بما فيه الكفاية. يتعلق الأمر بنفس المشكلة التي منعتنا من تنفيذ اقتراح الاسم المستعار السابق.

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

لا أرى أيضًا كيف أن أيًا من المشكلات التي تمنع الأسماء المستعارة من الدخول 1.8 تتصل بما قلته. العددان المعنيان ، على حد علمي ، هما # 17746 و # 17784. إذا كنت تشير إلى مشكلة التضمين (التي يمكن تفسيرها على أنها تتعلق بفواصل أو انعكاس ، على الرغم من أنني لا أوافق) ، فسيتم تناول ذلك في الاقتراح الرسمي (على الرغم من ذلك ، انظر أعلاه ، أعتقد أن الحل المقترح يستحق مزيدًا من المناقشة) ويجب أن تكون محددًا بشأن سبب عدم تصديقك.

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

Merovius إذاً في حالة التبعيات متعدية حيث تنظر إحدى هذه التبعيات إلى reflect.Type.PkgPath () ، ماذا يحدث؟
هذه هي نفس المشكلة التي تحدث في قضية التضمين.

atdiar أنا آسف ، لا أرى كيف يمكن فهم هذا بأي حال من الأحوال ، في ضوء المناقشة في هذا الموضوع حتى الآن وما هو هذا الاقتراح. سوف أخرج من هذا الموضوع الفرعي المحدد الآن وأعطي الآخرين ، الذين قد يفهمون اعتراضك بشكل أفضل ، الفرصة لمواجهته.

اسمحوا لي أن أعيد صياغتها بإيجاز:

المسألة تتعلق بالمساواة في النوع بالنظر إلى حقيقة أن تعريف النوع يشفر موقعه الخاص.
نظرًا لأنه يمكن اختبار المساواة بين النوع واختبارها في وقت التشغيل ، لا أرى كيف يسهل إجراء الأنواع المتحركة.

أنا ببساطة أرفع تحذيرًا من أن حالة استخدام "الأنواع المتحركة" يمكن أن تؤدي إلى كسر الكثير من الحزم في البرية ، على مسافة. قلق مماثل مع المكونات الإضافية.

(بالطريقة نفسها ، فإن تغيير نوع المؤشر في الحزمة من شأنه أن يكسر الكثير من الحزم الأخرى ، إذا كان هذا التوازي يمكن أن يجعل الأمور أكثر وضوحًا.)

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

تضمين التغريدة

هذا يجعل نوعًا واحدًا ونوعين مستقلين ، وسواء كان انعكاسًا أو تحت واجهة {} ، هذا هو ما يحدث
هل ترى. يمكنك أيضًا التحويل بين واحد واثنين. اقتراح المحول أعلاه يجعل ذلك أخيرًا
الخطوة التلقائية للمحولات. قد لا يعجبك الاقتراح لأسباب متعددة ، لكن لا يوجد شيء
متناقض حول ذلك.

لا يمكنك التحويل بين

 func() one

و

func() two

Merovius لا يمكنك التفكير في تغيير جميع برامج استيراد الحزمة التي تم إصلاحها بواسطة التعليمات البرمجية الموجودة في البرية. ولست حريصًا جدًا على البدء في الخوض في إصدار الحزمة هنا.

لأكون واضحًا ، أنا لست ضد اقتراح الاسم المستعار ولكن صيغة "نقل الأنواع بين الحزم" التي تشير إلى حالة استخدام لم يتم إثبات سلامتها بعد.

jimmyfrasche re إمكانية التنبؤ

إنها الحالة التي تكون فيها func (t T) M() صالحة أحيانًا ، وأحيانًا تكون غير صالحة. لا يحدث ذلك كثيرًا لأن الناس لا يدفعون بهذه الحدود كثيرًا. أي أنه يعمل بشكل جيد في الممارسة. https://play.golang.org/p/bci2qnldej. على أي حال ، هذا مدرج في قائمة القيود _المحتملة_. مثل كل القيود الممكنة ، فإنه يضيف تعقيدًا ونريد أن نرى أدلة واقعية ملموسة قبل إضافة هذا التعقيد.

@ Merovius ، إعادة تضمين الأسماء:

أوافق على أن الوضع ليس مثاليًا. ومع ذلك ، إذا كان لدي قاعدة بيانات مليئة بالإشارات إلى io.ByteBuffer وأريد نقلها إلى بايت.

package io
type ByteBuffer = bytes.Buffer

_without_ تحديث أي من المراجع الموجودة إلى io.ByteBuffer. إذا كانت جميع الأماكن التي تم تضمين io.ByteBuffer فيها ، فقم تلقائيًا بتغيير اسم الحقل إلى Buffer نتيجة لاستبدال تعريف النوع بالاسم المستعار ، فعندئذٍ كسرت العالم ولا يوجد إصلاح تدريجي. في المقابل ، إذا كان اسم io.ByteBuffer المضمن لا يزال ByteBuffer ، فيمكن عندئذٍ تحديث الاستخدامات واحدًا تلو الآخر في الإصلاحات التدريجية الخاصة بهم (ربما يتعين عليهم القيام بخطوات متعددة ؛ مرة أخرى ليست مثالية).

لقد ناقشنا هذا بشيء من التفصيل في # 17746. كنت في الأصل بجانب اسم io.ByteBuffer المضمن هو Buffer ، لكن الحجة أعلاه أقنعتني أنني كنت مخطئًا. قدمjimmyfrasche على وجه الخصوص بعض الحجج الجيدة حول عدم تغيير الكود اعتمادًا على تعريف الشيء المضمن. لا أعتقد أنه من الممكن عدم السماح تمامًا بالأسماء المستعارة المضمنة.

لاحظ أن هناك حلًا بديلًا في p2 في مثالك. إذا كانت p2 تريد حقًا حقلاً مضمنًا يسمى ByteBuffer دون الرجوع إلى io.ByteBuffer ، فيمكنها تحديد:

type ByteBuffer = bytes.Buffer

ثم قم بتضمين ByteBuffer (أي p2.ByteBuffer) بدلاً من io.ByteBuffer. هذا ليس مثاليًا أيضًا ، لكنه يعني أن الإصلاحات يمكن أن تستمر.

من المؤكد أن هذا ليس مثاليًا وأن إعادة تسمية الحقول بشكل عام لم يتم تناولها في هذا الاقتراح. قد لا يكون التضمين حساسًا للاسم الأساسي ، ويجب أن يكون هناك نوع من بناء الجملة لـ "تضمين X كاسم N". يمكن أيضًا أن نضيف أسماء مستعارة للحقل لاحقًا. كلاهما يبدو كأفكار معقولة بداهة ، ومن المحتمل أن يكون كلاهما منفصلين ، ويتم تقييم المقترحات اللاحقة بناءً على دليل حقيقي على الحاجة. إذا ساعدتنا الأسماء المستعارة في الوصول إلى النقطة التي يكون فيها نقص الأسماء المستعارة للمجال هو العقبة الكبيرة التالية لإعادة البناء على نطاق واسع ، فسيكون ذلك تقدمًا!

(/ ccneild و @ bcmills)

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

rsc ما كان

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

لست متأكدًا الآن مما إذا كنت أفهم حجة ByteBuffer الخاصة بك ، لكنني أيضًا في نهاية يوم عمل طويل ، لذلك لا داعي لمزيد من التوضيح ، إذا وجدت ذلك غير مقنع فسأرد عليه في النهاية :)

Merovius أعتقد أنه من المنطقي تجربة القواعد البسيطة ومعرفة المدى الذي وصلنا إليه قبل تقديم قواعد أكثر تعقيدًا. يمكننا إضافة (أ) و (ب) لاحقًا إذا لزم الأمر ؛ (ج) هو معطى مهما كان الأمر.

أوافق على أنه ربما تكون (ب) فكرة جيدة في ظروف معينة ، ولكن ربما لا تكون في ظروف أخرى. إذا كنت تستخدم الأسماء المستعارة للنوع لحالة استخدام "بنية حزمة واحدة في حزم تنفيذ متعددة" المذكورة سابقًا ، فربما لا ترغب في تضمين الاسم المستعار لفضح الاسم الآخر (والذي قد يكون في حزمة داخلية و بخلاف ذلك يتعذر على معظم المستخدمين الوصول إليه). آمل أن نتمكن من جمع المزيد من الخبرة.

rsc

ربما يمكن أن تساعد إضافة معلومات مستوى الحزمة حول قابلية الاستعارة إلى ملفات الكائن.
(مع الأخذ في الاعتبار ما إذا كان يجب أن تستمر مكونات go الإضافية في العمل بشكل صحيح أم لا.)

تضمين التغريدة

أ) يحظر تضمين كل من الاسم المستعار ويحدد النوع في نفس البنية

لاحظ أنه في كثير من الحالات يكون هذا ممنوعًا بالفعل كنتيجة للطريقة التي يتفاعل بها التضمين مع مجموعات الأساليب. (إذا كان النوع المضمن يحتوي على مجموعة طرق غير فارغة وتم استدعاء إحدى هذه الطرق ، فسيفشل البرنامج في ترجمة: https://play.golang.org/p/XkaB2a0_RK.)

لذا فإن إضافة قاعدة صريحة تمنع التضمين المزدوج يبدو أنها ستحدث فرقًا فقط في مجموعة فرعية صغيرة من الحالات ؛ لا يبدو أنه يستحق التعقيد بالنسبة لي.

لماذا لا نتعامل مع الأسماء المستعارة من النوع كأنواع جبرية بدلاً من ذلك ، وندعم الأسماء المستعارة لمجموعة من الأنواع ، لذلك نحصل أيضًا على واجهة فارغة مكافئة مع التحقق من نوع وقت الترجمة كمكافأة ، a la

type Stringeroonie = {string,fmt.Stringer}

@ j7b

لماذا لا تقترب من الأسماء المستعارة لأنواع جبرية بدلاً من ذلك وتدعم الأسماء المستعارة لمجموعة من الأنواع

الأسماء المستعارة مكافئة لغويًا وهيكلية للنوع الأصلي. لا تعتبر أنواع البيانات الجبرية: في الحالة العامة ، تتطلب مساحة تخزين إضافية لعلامات الكتابة. (تحتوي أنواع واجهة Go بالفعل على معلومات النوع هذه ، لكن الهياكل والأنواع الأخرى التي لا تحتوي على واجهة لا تفعل ذلك.)

تضمين التغريدة

قد يكون هذا تفكيرًا خاطئًا ، لكنني اعتقدت أنه يمكن التعامل مع المشكلة كاسم مستعار A من النوع T يكافئ إعلان A كواجهة {} والسماح للمترجم بتحويل متغيرات من النوع A إلى T بشفافية في النطاقات حيث يتم الإعلان عن متغيرات من النوع A ، والتي اعتقدت أنها ستكون في الغالب تكلفة خطية لوقت الترجمة ، ولا لبس فيها ، وتخلق أساسًا للأنماط الزائفة التي يديرها المترجم بما في ذلك الجبر باستخدام بناء الجملة type T = ، وربما يسمح أيضًا بتنفيذ أنواع مثل المراجع الثابتة في وقت التجميع بقدر ما يتعلق الأمر برمز المستخدم سيكون مجرد واجهات {} "تحت الغطاء".

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

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

فكرتك عن الترجمة الشفافة من الواجهة وإليها {} لا تعمل أيضًا مع الأنواع ذات الترتيب الأعلى مثل []interface{} . وفي النهاية ستنتهي بفقدان إحدى نقاط القوة في go ، والتي تتمثل في منح المستخدمين التحكم في تخطيط البيانات والقيام بدلاً من ذلك بشيء جافا المتمثل في التفاف كل شيء.

ADT ليست الحل هنا.

Merovius ، أنا متأكد تمامًا مما إذا كان بناء النوع الجبري يتضمن إعادة تسمية (والذي سيكون متسقًا مع تعريف معقول لنفسه) فهو حل ، يمكن أن تعمل هذه الواجهة {} كوكيل لشكل الإسقاط والتحديد الذي يديره المترجم الموصوفة ، ولست متأكدًا من مدى ملاءمة تخطيط البيانات أو كيفية تحديد أنواع "الترتيب الأعلى" ، فالنوع هو مجرد نوع إذا كان بالإمكان الإعلان عنه و [] الواجهة {} هي مجرد نوع.

بغض النظر عن كل ذلك ، أنا متأكد من أن type T = لديه القدرة على أن يكون مثقلًا بطرق بديهية ومفيدة تتجاوز إعادة التسمية ، ويبدو أن الأنواع الجبرية والمراجع غير القابلة للتغيير علنًا هي أكثر التطبيقات وضوحًا ، لذلك آمل أن تنتهي المواصفات بذكر تلك البنية يشير إلى نوع meta أو pseudo الذي يديره المترجم ويؤخذ في الاعتبار جميع الطرق التي يمكن أن يكون بها النوع الذي يديره المترجم مفيدًا والصياغة التي تعبر عن تلك الاستخدامات بشكل أفضل. نظرًا لأن بناء الجملة الجديد لا يحتاج إلى الاهتمام بمجموعة الكلمات المحجوزة عالميًا عند استخدامها كمعرفات ، فإن شيئًا مثل type A = alias Type سيكون واضحًا وقابل للتوسيع.

@ j7b

بغض النظر عن كل ذلك ، أنا إيجابي من النوع T = لديه القدرة على التحميل الزائد بطرق بديهية ومفيدة تتجاوز إعادة التسمية ،

أنا بالتأكيد لا أتمنى ذلك. Go (في الغالب) متعامد بشكل جيد اليوم ، والحفاظ على هذا التعامد أمر جيد.

الطريقة ، اليوم ، التي يعلن فيها المرء عن نوع جديد T في Go هو type T def ، حيث def هو تعريف النوع الجديد. إذا كان على المرء أن ينفذ أنواع البيانات الجبرية (المعروفة أيضًا باسم الاتحادات الموسومة) ، فأنا أتوقع منهم اتباع هذا النحو بدلاً من بناء الجملة للأسماء المستعارة من النوع.

أحب أن أطرح وجهة نظر مختلفة (دعمًا) للأسماء المستعارة ، والتي قد توفر نظرة ثاقبة لحالات الاستخدام البديلة إلى جانب إعادة البناء:

دعنا نتراجع للحظة ونفترض أنه لم يكن لدينا إقرارات من النوع Go القديم المعتاد بالشكل type T <a type> ، لكن اكتب فقط إقرارات الاسم المستعار type A = <a type> .

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

أنا أزعم أنه في مثل هذا العالم يمكننا كتابة نفس الكود الذي نكتبه الآن: نستخدم أسماء أنواع (الاسم المستعار) حتى لا نضطر إلى تكرار أنفسنا ، والأنواع نفسها تتأكد من أننا نستخدم البيانات في نوع -طريق امن.

بعبارة أخرى ، إذا تم تصميم Go بهذه الطريقة ، فربما كنا على ما يرام أيضًا بشكل عام.

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

لكن لن يكون لدينا آلية أمان لدينا في Go الحالية: لن نتمكن من تقديم اسم لنوع ونذكر أنه يجب أن يكون الآن نوعًا جديدًا مختلفًا. (ومع ذلك ، من المهم أن تضع في اعتبارك أنها في جوهرها آلية أمان.)

في لغات البرمجة الأخرى ، يُطلق على فكرة إنشاء نوع جديد مختلف عن النوع الحالي "العلامة التجارية": حيث يتم ربط النوع بعلامة تجارية تجعله مختلفًا عن جميع الأنواع الأخرى. على سبيل المثال ، في Modula-3 ، كانت هناك كلمة رئيسية خاصة BRANDED لتحقيق ذلك (على سبيل المثال ، TYPE T = BRANDED REF T0 سينشئ مرجعًا جديدًا مختلفًا إلى T0). في هاسكل ، كلمة new قبل أي نوع لها تأثير مماثل.

بالعودة إلى عالم Go البديل الخاص بنا ، قد نجدنا في وضع لا نواجه فيه أي مشاكل في إعادة البناء ، ولكن حيث أردنا تحسين أمان الكود الخاص بنا بحيث يشير type MyBuffer = []byte و type YourBuffer = []byte أنواع مختلفة حتى لا نستخدم النوع الخطأ عن طريق الخطأ. قد نقترح تقديم نوع من أنواع العلامات التجارية لهذا الغرض بالضبط. على سبيل المثال ، قد نرغب في كتابة type MyBuffer = new []byte ، أو حتى type MyBuffer = new YourBuffer مع تأثير أن MyBuffer أصبح الآن نوعًا مختلفًا عن YourBuffer.

هذه في جوهرها المشكلة المزدوجة لما لدينا الآن. يحدث أنه في Go ، منذ اليوم الأول ، عملنا دائمًا مع الأنواع "ذات العلامات التجارية" بمجرد حصولها على اسم. بمعنى آخر ، type T <a type> هو فعليًا type T = new <a type> .

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

النقطة المهمة هي أن كلا الآليتين مفيدتان بطبيعتهما ، وباستخدام الأسماء المستعارة للنوع ، يمكننا أخيرًا دعمهما معًا.

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

في كلا الاقتراحين ، تركت أتساءل عما إذا كان التعاون من الرابط لا يجب أن يكون مطلوبًا لأن الاسم جزء من تعريف النوع في Go كما أوضحت.

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

الفكرة هي أن وقت التشغيل يجب أن يكون قادرًا على إرجاع الأسماء المستعارة المختلفة لنوع معين ديناميكيًا بحيث تظل رسائل الخطأ واضحة (نظرًا لأن الاسم المستعار يقدم اختلافًا في التسمية بين الكود قيد التشغيل والتعليمات البرمجية المكتوبة).

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

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

atdiar لست متأكدًا من أنك تقصد ما أفعله عندما تقول "تجميع منفصل". إذا قامت الحزمة P باستيراد io و bytes ، فيمكن تجميع الثلاثة كخطوات منفصلة. ومع ذلك ، إذا تغيرت io أو بايت ، فيجب إعادة ترجمة P. ليس الأمر أنه يمكنك إجراء تغييرات على io أو بايت ثم استخدام تجميع قديم لـ P. حتى في وضع البرنامج المساعد ، هذا صحيح. نظرًا لتأثيرات مثل تضمين الحزم المتقاطعة ، فإن التغييرات غير المرئية لواجهة برمجة التطبيقات في تنفيذ io أو البايت تغير ABI الفعال ، ولهذا السبب يجب إعادة ترجمة P. الأسماء المستعارة للنوع لا تجعل هذه المشكلة أسوأ.

@ j7d ، على مستوى نظام النوع ،

يعتبر مُنشئ النوع (طريقة خيالية لقول "طريقة لاستخدام نوع") متغيرًا إذا كان يحافظ على علاقة النوع الفرعي ، ويكون متباينًا إذا عكس العلاقة.

استخدام نوع في نتيجة دالة هو متغير. Func () Buffer "هو" func () Reader ، لأن إعادة مخزن Buffer يعني أنك قمت بإرجاع قارئ. استخدام نوع في وسيطة دالة هو _not_ covariant. وظيفة (Buffer) ليست func (Reader) ، لأن func يحتاج إلى Buffer ، وبعض القراء ليسوا Buffer.

استخدام نوع في وسيطة دالة هو أمر مخالف. func (القارئ) هو func (Buffer) ، لأن func يحتاج فقط إلى قارئ ، و Buffer هو قارئ. استخدام نوع في نتيجة دالة هو _not_ متعارض. القارئ func () ليس مخزنًا وظيفيًا () مؤقتًا ، لأن الوظيفة ترجع قارئًا ، وبعض القراء ليسوا مخازن مؤقتة.

عند الجمع بين الاثنين ، فإن القارئ func (Reader) ليس مخزنًا وظيفيًا (Buffer) ، ولا العكس ، إما لأن الوسيطات لا تنجح أو أن النتائج لا تنجح. (التركيبة الوحيدة التي تعمل على طول هذه الخطوط هي أن func (Reader) Buffer هو قارئ func (Buffer).)

بشكل عام ، إذا كان func (X1) X2 (نوعًا فرعيًا من) func (X3) X4 ، فيجب أن يكون X3 (نوعًا فرعيًا من) X1 وبالمثل X2 هو (نوع فرعي من) X4. في حالة استخدام الاسم المستعار حيث نريد أن يكون T1 و T2 قابلين للتبادل ، فإن func (T1) T1 هو نوع فرعي من func (T2) T2 فقط إذا كان T1 نوعًا فرعيًا من T2 _ و T2 هو نوع فرعي من T1. هذا يعني أساسًا أن T1 هو نوع _نفس _ مثل T2 ، وليس نوعًا أكثر عمومية.

لقد استخدمت الحجج والنتائج الوظيفية لأن هذا هو المثال المتعارف عليه (وهو مثال جيد) ، لكن الشيء نفسه يحدث للطرق الأخرى لبناء نتائج معقدة. بشكل عام تحصل على التباين المشترك للمخرجات (مثل func () T ، أو <-chan T ، أو map [...] T) والتباين في المدخلات (مثل func (T) ، أو chan <- T ، أو map [T ] ...) والمساواة الإجبارية في النوع للإدخال + الإخراج (مثل func (T) T ، أو chan T ، أو * T ، أو [10] T ، أو [] T ، أو البنية {Field T} ، أو متغير من النوع T). في الواقع ، الحالة الأكثر شيوعًا في Go ، كما ترى من الأمثلة ، هي الإدخال + الإخراج.

بشكل ملموس ، فإن [] المخزن المؤقت ليس [] قارئًا (لأنه يمكنك تخزين ملف في [] قارئ ولكن ليس في [] مخزن مؤقت) ، ولا يعتبر [] قارئًا [] مخزنًا مؤقتًا (لأن الجلب من [] قد يقوم القارئ بإرجاع ملف ، بينما الجلب من المخزن المؤقت [] يجب أن يعيد المخزن المؤقت).

الاستنتاج من كل هذا هو أنه إذا كنت ترغب في حل مشكلة إصلاح الكود العامة بحيث يمكن أن يستخدم الرمز T1 أو T2 ، فلا يمكنك القيام بذلك باستخدام أي مخطط يجعل T1 نوعًا فرعيًا فقط من T2 (أو العكس). يجب أن يكون كل منهما نوعًا فرعيًا من الآخر - أي أنه يجب أن يكون من نفس النوع - وإلا فإن بعض هذه الاستخدامات المدرجة ستكون غير صالحة.

أي أن التصنيف الفرعي لا يكفي لحل مشكلة إصلاح الكود التدريجي. هذا هو السبب في أن الأسماء المستعارة للنوع تقدم اسمًا جديدًا لنفس النوع ، بحيث يكون T1 = T2 ، بدلاً من محاولة التصنيف الفرعي.

ينطبق هذا التعليق أيضًا على اقتراح iand قبل أسبوعين لنوع من "الأنواع القابلة للاستبدال" وبشكل أساسي توسيع

تم تحديث ملخص مناقشة المستوى الأعلى. التغييرات:

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

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

atdiar لا توجد قائمة من الأسماء المستعارة في أي مكان في النظام. وقت التشغيل ليس لديه حق الوصول إليه. الأسماء المستعارة غير موجودة في وقت التشغيل.

rsc هاه ، آسف. أنا عالق مع اقتراح الاسم المستعار الأولي في الرأس وكنت أفكر في التعرّف على func (أثناء مناقشة التعرّف على الأنواع). في هذه الحالة ، سيكون هناك تناقض بين الأسماء في الكود والأسماء في وقت التشغيل.
استخدام المعلومات في وقت التشغيل: قد يحتاج إطار التسجيل إلى بعض إعادة التفكير في هذه الحالة.
لا يهمني.

rsc شكرا لإعادة التلخيص. لا يزال اسم الحقل المضمن يزعجني ؛ تعتمد جميع الحلول المقترحة على kludges دائمة للحفاظ على الأسماء القديمة. على الرغم من أن النقطة الأكبر في هذا التعليق ، وهي أن هذه حالة خاصة لإعادة تسمية الحقول ، وهو أمر غير ممكن أيضًا ، تقنعني أنه يجب بالفعل النظر إلى هذه (وحلها) على أنها مشكلة منفصلة. هل من المنطقي فتح مشكلة منفصلة لطلب / اقتراح / مناقشة لدعم إعادة تسمية الحقول للإصلاح التدريجي (ربما يتم تناولها في نفس إصدار go)؟

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

العودة من أسبوعين. يبدو أن المناقشة قد تقاربت. حتى تحديث المناقشة قبل أسبوعين كان طفيفًا إلى حد ما.

أقترح أن نقوم بما يلي:

  • قبول اقتراح الاسم المستعار للنوع كحل مؤقت للمشكلة الموضحة أعلاه ،
    شريطة أن يكون التطبيق جاهزًا للأشخاص لتجربته في بداية Go 1.9 (1 فبراير).
  • إنشاء فرع dev.typealias dev بحيث يمكن مراجعة CLs الآن (يناير) ودمجها في الرئيسية في بداية Go 1.9.
  • اتخاذ قرار نهائي بشأن الاحتفاظ بالأسماء المستعارة للنوع بالقرب من بداية تجميد Go 1.9 (كما فعلنا مع الأسماء المستعارة المعممة في دورة Go 1.8).

+1

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

@ ميروفيوس : آسف لتعديل رسالتي! اعتقدت أن لا أحد كان يقرأ. في البداية في هذا التعليق ، أعربت عن بعض الشكوك في أن تغيير اللغة هذا ضروري عندما توجد بالفعل أدوات مثل الأداة gorename .

@ jcao219 لقد تمت مناقشة هذا من قبل ، ولكن من المدهش أنه لا يمكنني العثور على هذا بسرعة هنا. تمت مناقشته بإسهاب في الخيط الأصلي للأسماء المستعارة العامة # 16339 وما يرتبط بها من خيوط golang-nuts. باختصار: يتناول هذا النوع من الأدوات فقط كيفية تحضير عمليات الإصلاح ، وليس كيفية ترتيب التغييرات لمنع حدوث الأعطال. سواء تم إجراء التغييرات بواسطة أداة أو بواسطة إنسان ، فهذا أمر غير جوهري بالنسبة للمشكلة ، ولا يوجد حاليًا تسلسل من الالتزامات التي لن تؤدي إلى كسر بعض التعليمات البرمجية أو غيرها (يبرر التعليق الأصلي لهذه المشكلة والمستند المرتبط هذا البيان أكثر في -عمق).

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

في الختام ، لنفترض أن التغيير قد تم تنفيذه. بدون شك ، ستصبح تفاصيل هامشية إلى حد ما للغة ، وليس ميزة أساسية.

أود أن أعبر عن شك. :) أنا لا أعتبر هذا نتيجة مفروغ منها.

تضمين التغريدة

أود أن أعبر عن شك. :) أنا لا أعتبر هذا نتيجة مفروغ منها.

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

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

على سبيل المثال ، لنفترض أنني أردت استخدام "github.com/gonum/graph/simple".DirectedGraph ، وأردت تسميته بـ digraph لتجنب كتابة simple.DirectedGraph ، فهل سيكون ذلك جيدًا حالة الاستخدام؟ أم يجب أن يقتصر هذا النوع من إعادة التسمية على الأسماء الطويلة بشكل غير معقول التي تولدها أشياء مثل protobuf؟

@ jcao219 ، ملخص المناقشة في الجزء العلوي من هذه الصفحة يجيب على أسئلتك. على وجه الخصوص ، راجع الأقسام التالية:

  • هل يمكن أن يكون هذا تغييرًا في الأدوات أو المترجم فقط بدلاً من تغيير اللغة؟
  • ما هي الاستخدامات الأخرى التي قد تمتلكها الأسماء المستعارة؟
  • القيود (الملاحظات العامة التي تبدأ في ذلك القسم)

بالنسبة إلى وجهة نظرك العامة حول Go Experts vs New Go Programmer ، فإن الهدف الواضح لـ Go هو تسهيل البرمجة في قواعد التعليمات البرمجية الكبيرة. ما إذا كنت خبيرًا إلى حد ما لا علاقة له بحجم قاعدة الشفرة التي تعمل فيها. (ربما تكون قد بدأت للتو في مشروع جديد بدأه شخص آخر. قد لا تزال بحاجة إلى القيام بهذا النوع من العمل.)

حسنًا ، بناءً على الإجماع / الهدوء هنا ، سأقوم (كما اقترحت الأسبوع الماضي في https://github.com/golang/go/issues/18130#issuecomment-268614964) بتمييز هذا الاقتراح الذي تمت الموافقة عليه وإنشاء فرع dev.typealias .

يحتوي الملخص الممتاز على قسم "ما هي المشكلات الأخرى التي يحتاج اقتراح الأسماء المستعارة إلى معالجتها؟" ما هي الخطط لمعالجة هذه القضايا بعد إعلان قبول الاقتراح؟

يذكر CL https://golang.org/cl/34986 هذه المشكلة.

يذكر CL https://golang.org/cl/34987 هذه المشكلة.

يذكر CL https://golang.org/cl/34988 هذه المشكلة.

ulikunitz re المشكلات (كل هذه الاقتباسات من مستند التصميم تفترض "النوع T1 = T2"):

  1. التعامل مع غودوك. يحدد مستند التصميم الحد الأدنى من التغييرات على godoc. بمجرد ذلك يمكننا معرفة ما إذا كانت هناك حاجة إلى دعم إضافي. ربما ، ولكن ربما لا.
  2. هل يمكن تعريف الأساليب على الأنواع المسماة بالاسم المستعار؟ نعم فعلا. مستند التصميم: "نظرًا لأن T1 هي مجرد طريقة أخرى لكتابة T2 ، فليس لها مجموعتها الخاصة من إعلانات الأسلوب. وبدلاً من ذلك ، فإن مجموعة أساليب T1 هي نفس مجموعة T2. على الأقل بالنسبة للتجربة الأولية ، لا توجد قيود على إعلانات الطريقة سيكون استخدام T1 كنوع جهاز استقبال ، بشرط استخدام T2 في نفس الإعلان ، صالحًا ".
  3. إذا تم السماح باستخدام الأسماء المستعارة ، فكيف نتعامل مع دورات الاسم المستعار؟ لا دورات. مستند التصميم: "في تصريح النوع المستعار ، على عكس التصريح بالنوع ، يجب ألا يشير T2 أبدًا ، بشكل مباشر أو غير مباشر ، إلى T1."
  4. هل يجب أن تكون الأسماء المستعارة قادرة على تصدير المعرفات غير المُصدرة؟ نعم فعلا. مستند التصميم: "لا توجد قيود على شكل T2: قد يكون من أي نوع ، بما في ذلك على سبيل المثال لا الحصر الأنواع المستوردة من حزم أخرى."
  5. ماذا يحدث عندما تقوم بتضمين اسم مستعار (كيف يمكنك الوصول إلى الحقل المضمن)؟ الاسم مأخوذ من الاسم المستعار (الاسم المرئي في البرنامج). مستند التصميم: https://golang.org/design/18130-type-alias#effect -on-embedding.
  6. هل الأسماء المستعارة متوفرة كرموز في البرنامج المبني؟ لا ، مستند التصميم: "غالبًا ما تكون الأسماء المستعارة للأنواع غير مرئية في وقت التشغيل." (الإجابة تأتي من هذا ولكن لا يتم استدعاؤها صراحة.)
  7. حقن سلسلة Ldflags: ماذا لو أشرنا إلى اسم مستعار؟ لا توجد أسماء مستعارة فار ، لذلك هذا لا ينشأ.

يذكر CL https://golang.org/cl/35091 هذه المشكلة.

يذكر CL https://golang.org/cl/35092 هذه المشكلة.

يذكر CL https://golang.org/cl/35093 هذه المشكلة.

rsc جزيل الشكر على التوضيحات.

لنفرض:

package a

import "b"

type T1 = b.T2

بقدر ما أفهم أن T1 متطابق أساسي مع b.T2 وبالتالي فهو نوع غير محلي ولا يمكن تحديد طرق جديدة. ومع ذلك ، يتم إعادة تصدير المعرف T1 في الحزمة أ. هل هذا تفسير صحيح؟

ulikunitz هذا صحيح

تشير T1 إلى نفس النوع تمامًا مثل b.T2. إنه ببساطة اسم مختلف. سواء تم تصدير شيء ما أم لا ، يعتمد على الاسم وحده (لا علاقة له بالنوع الذي يشير إليه).

لجعل ردgriesemer صريحًا: نعم ، يتم تصدير T1 من الحزمة a (لأنها T1 وليست t1).

يذكر CL https://golang.org/cl/35099 هذه المشكلة.

يذكر CL https://golang.org/cl/35100 هذه المشكلة.

يذكر CL https://golang.org/cl/35101 هذه المشكلة.

يذكر CL https://golang.org/cl/35102 هذه المشكلة.

يذكر CL https://golang.org/cl/35104 هذه المشكلة.

يذكر CL https://golang.org/cl/35106 هذه المشكلة.

يذكر CL https://golang.org/cl/35108 هذه المشكلة.

يذكر CL https://golang.org/cl/35120 هذه المشكلة.

يذكر CL https://golang.org/cl/35121 هذه المشكلة.

يذكر CL https://golang.org/cl/35129 هذه المشكلة.

يذكر CL https://golang.org/cl/35191 هذه المشكلة.

يذكر CL https://golang.org/cl/35233 هذه المشكلة.

يذكر CL https://golang.org/cl/35268 هذه المشكلة.

يذكر CL https://golang.org/cl/35269 هذه المشكلة.

يذكر CL https://golang.org/cl/35670 هذه المشكلة.

يذكر CL https://golang.org/cl/35671 هذه المشكلة.

يذكر CL https://golang.org/cl/35575 هذه المشكلة.

يذكر CL https://golang.org/cl/35732 هذه المشكلة.

يذكر CL https://golang.org/cl/35733 هذه المشكلة.

يذكر CL https://golang.org/cl/35831 هذه المشكلة.

يذكر CL https://golang.org/cl/36014 هذه المشكلة.

هذا هو الأساس الآن ، قبل افتتاح Go 1.9. يُرجى عدم التردد في المزامنة على المستوى الرئيسي وتجربة الأشياء. شكرا.

تمت إعادة التوجيه من # 18893

package main

import (
        "fmt"
        "q"
)

func main() {
        var a q.A
        var b q.B // i'm a named unnamed type !!!

        fmt.Printf("%T\t%T\n", a, b)
}

ماذا كنت تتوقع أن ترى؟

deadwood(~/src) % go run main.go
q.A     q.B

ماذا رأيت بدلا من ذلك؟

deadwood(~/src) % go run main.go
q.A     []int

مناقشة

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

أوه ، ما لم يكن اسمًا مستعارًا ، في هذه الحالة عليك أن تتذكر أنه _يمكن أن يكون _ نوعًا غير مسمى ، حتى عند الاستيراد إلى من حزمة أخرى.

والأسوأ من ذلك ، أنه سيمكن الناس من إصدار أنماط مضادة للقراءة مثل

type Any = interface{}

الرجاء عدم السماح لأنواع غير مسماة بأن تكون مستعارة.

تضمين التغريدة

لا توجد قصة "إصلاح رمز" في الانتقال من نوع غير مسمى إلى آخر.

غير صحيح. ماذا لو كنت تريد تغيير نوع معلمة الطريقة من اسم إلى نوع غير مسمى أو العكس؟ الخطوة 1 هي إضافة الاسم المستعار ؛ الخطوة 2 هي تحديث الأنواع التي تنفذ هذه الطريقة لاستخدام النوع الجديد ؛ الخطوة 3 هي إزالة الاسم المستعار.

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

والأسوأ من ذلك ، أنه سيمكن الناس من إصدار أنماط مضادة للقراءة مثل
type Any = interface{}

يمكن للأشخاص كتابة type Any interface{} اليوم. ما الضرر الإضافي الذي تسببه الأسماء المستعارة في هذه الحالة؟

يمكن للأشخاص بالفعل كتابة أي واجهة {} اليوم. ما الضرر الإضافي الذي تسببه الأسماء المستعارة في هذه الحالة؟

لقد أطلقت عليه نمطًا مضادًا لأن هذا هو بالضبط ما هو عليه. type Any interface{} ، نظرًا لأن الشخص _ يكتب_ الرمز يكتب شيئًا أقصر قليلاً ، فهذا يجعله أكثر منطقية قليلاً.

على الجانب الآخر ، يجب على جميع القراء ، الذين لديهم خبرة في قراءة كود Go ويتعرفون على interface{} بشكل غريزي مثل وجههم في المرآة ، أن يتعلموا ويعيدوا تعلم كل متغير من Any ، Object ، T ، وقم بتعيينها لأشياء مثل type Any interface{} ، type Any map[interface{}]interface{} ، type Any struct{} على أساس كل حزمة.

هل توافق بالتأكيد على أن أسماء الحزمة المحددة لتعابير Go الشائعة هي صافي سلبي لسهولة القراءة؟

هل توافق بالتأكيد على أن أسماء الحزمة المحددة لتعابير Go الشائعة هي صافي سلبي لسهولة القراءة؟

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

حقيقة أن النمط المضاد ممكن بدون أسماء مستعارة يعني أنه يجب علينا بالفعل تثقيف مبرمجي Go لتجنبه ، بغض النظر عما إذا كانت الأسماء المستعارة لأنواع غير مسماة موجودة أم لا.

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

انصح:

package antipattern

type Any interface{}  // not an alias

type Widget interface{
  Frozzle(Any) error
}

func Bozzle(w Widget) error {
  …
}

اليوم ، سيتعطل مستخدمو antipattern.Bozzle باستخدام antipattern.Any في تطبيقاتهم Widget ، ولا توجد طريقة لإزالة antipattern.Any مع الإصلاحات التدريجية. ولكن باستخدام الأسماء المستعارة للنوع ، يمكن لمالك الحزمة antipattern إعادة تعريفها على النحو التالي:

// Any is deprecated; please use interface{} directly.
type Any = interface{}

والآن المتصلين يمكن ترحيل من Any إلى interface{} تدريجيا، والسماح لل antipattern معيل لفي نهاية المطاف إزالته.

نقطتي هي أنه لا يوجد مبرر لتسمية الأنواع غير المسماة بذلك
إن رفض هذا الخيار سيستمر في إثبات عدم ملاءمة
الممارسة.

على العكس من ذلك ، فإن السماح باستخدام الأسماء المستعارة للأنواع غير المسماة لا يُمكّن واحدًا بل اثنين
أشكال من هذا النمط المضاد.

في الخميس ، 2 فبراير 2017 ، 16:34 كتب بريان سي ميلز [email protected] :

أنت توافق بالتأكيد على أن أسماء الحزمة المحددة لتعابير Go الشائعة هي
صافي سلبي لسهولة القراءة؟

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

حقيقة أن النمط المضاد ممكن بدون أسماء مستعارة يعني ذلك
يجب أن نعلم بالفعل مبرمجي Go لتجنب ذلك ، بغض النظر عما إذا كان
يمكن أن توجد الأسماء المستعارة لأنواع غير مسماة.

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/golang/go/issues/18130#issuecomment-276872714 ، أو كتم الصوت
الخيط
https://github.com/notifications/unsubscribe-auth/AAAcA6BGrFjjTi7eW1BPp7o81XIekbGXks5rYWr-gaJpZM4LBBEL
.

davecheney لا أعتقد أن لدينا أي دليل حتى الآن على أن القدرة على إعطاء اسم حرفي من النوع التعسفي أمر ضار. هذه أيضًا ليست ميزة "مفاجأة" غير متوقعة - لقد تمت مناقشتها بالتفصيل في مستند التصميم . في هذه المرحلة ، من المنطقي استخدام هذا لبعض الوقت ومعرفة إلى أين يقودنا.

كمثال مضاد ، هناك واجهات برمجة تطبيقات عامة تستخدم النوع الحرفي فقط لأن واجهة برمجة التطبيقات لا تريد تقييد العميل بنوع معين (راجع https://golang.org/pkg/go/types/#Info على سبيل المثال ). قد يكون وجود هذا النوع الصريح الحرفي وثائق مفيدة. ولكن في الوقت نفسه ، قد يكون من المزعج للغاية تكرار نفس النوع الحرفي في كل مكان ؛ وفي الواقع يكون عائقا أمام سهولة القراءة. أن تكون قادرًا على التحدث بسهولة عن IntSet بدلاً من map[int]struct{} أن يتم قفله في هذا التعريف وفقط IntSet يعد إضافة في ذهني. هذا هو المكان المناسب تمامًا لـ type IntSet = map[int]struct{} .

أخيرًا ، أود الرجوع إلى https://github.com/golang/go/issues/18130#issuecomment -268411811 في حال فاتتك. إن إقرارات النوع غير المقيد التي تستخدم = هي بالفعل إعلان النوع "الابتدائي" ، ويسعدني أننا حصلنا عليها أخيرًا في Go.

ربما يكون type intSet = map[int]struct{} (لم يتم تصديره) طريقة أفضل لاستخدام الأسماء المستعارة من النوع غير المسماة ، ولكن هذا يبدو مثل مجال CodeReviewComments وممارسات البرمجة الموصى بها ، بدلاً من تقييد الميزة.

ومع ذلك ، فإن %T هي أداة مفيدة لمعرفة الأنواع عند تصحيح أخطاء نظام الكتابة أو استكشافها. أتساءل عما إذا كان يجب أن يكون هناك فعل تنسيق مماثل يتضمن الاسم المستعار؟ q.B = []int في مثالdavecheney .

nathany كيف تنفذ هذا الفعل؟ معلومات الاسم المستعار غير موجودة في وقت التشغيل. (فيما يتعلق بالحزمة reflect ، يكون الاسم المستعار _من نفس النوع_ مثل الشيء الذي تم تسميته باسم مستعار.)

bcmills اعتقدت أن هذا قد يكون هو الحال ... 😞

أتخيل أن أدوات التحليل الثابتة وإضافات المحرر لا تزال في الصورة للمساعدة في العمل مع الأسماء المستعارة ، لذلك لا بأس بذلك.

في 2 فبراير 2017 5:01 مساءً ، كتب "Nathan Youngman" [email protected] :

ومع ذلك ، فإن٪ T هي أداة مفيدة لمعرفة الأنواع عند تصحيح أخطاء ملف
اكتب النظام. أتساءل عما إذا كان يجب أن يكون هناك فعل مماثل للصيغة
يتضمن الاسم المستعار؟ qB = [] int indavecheney
مثال https://github.com/davecheney .

أعتقد أن الحل الأفضل هو إضافة وضع استعلام إلى المعلم للإجابة على هذا السؤال
سؤال:

وهي الأسماء المستعارة المعلنة في GOPATH بالكامل (أو حزمة معينة) لـ
هذا النوع المعطى في سطر الأوامر؟

لست قلقًا بشأن إساءة استخدام الأسماء المستعارة للأنواع غير المسماة ، ولكنها محتملة
الأسماء المستعارة المكررة لنفس النوع غير المسمى.

davecheney لقد أضفت اقتراحك إلى قسم "القيود" في ملخص المناقشة في الأعلى. مثل جميع القيود ، فإن موقفنا العام هو أن القيود تضيف تعقيدًا (انظر الملاحظات أعلاه) وسنحتاج على الأرجح إلى رؤية دليل فعلي على الضرر الواسع النطاق من أجل فرض قيود. إن الاضطرار إلى تغيير الطريقة التي تدرس بها Go ليس كافيًا: أي تغيير نجريه على اللغة سيتطلب تغيير الطريقة التي تدرس بها Go.

كما هو مذكور في مستند التصميم والقائمة البريدية ، فإننا نعمل على تحسين المصطلحات لتسهيل التفسيرات.

minux ، مثلما أشار bcmills ، لا توجد معلومات الاسم المستعار في وقت التشغيل (أساسية تمامًا للتصميم). لا توجد طريقة لتطبيق "٪ T يتضمن الاسم المستعار".

في 2 فبراير 2017 الساعة 8:33 مساءً ، كتب "Russ Cox" [email protected] :

minux https://github.com/minux ، مثلbcmills
https://github.com/bcmills أشار ، معلومات الاسم المستعار غير موجودة
في وقت التشغيل (أساسي تمامًا للتصميم). لا توجد طريقة ل
تنفيذ "٪ T يتضمن الاسم المستعار".

أقترح وضع استعلام Go guru (https://golang.org/x/tools/cmd/guru)
لتعيين الاسم المستعار العكسي ، والذي يعتمد على تحليل الكود الثابت. هو - هي
لا يهم إذا كانت معلومات الاسم المستعار متاحة في وقت التشغيل أم لا.

minux ، أري ، أنت ترد عبر البريد الإلكتروني و Github يجعل النص المقتبس يبدو وكأنه نص كتبته بنفسك. كنت أرد على النص الذي نقلته من ناثان يونغمان ، معتقدًا أنه لك. اسف لخلط الامور.

فيما يتعلق بالمصطلحات والتدريس ، وجدت أن الخلفية ذات العلامات التجارية griesemer تنشر معلومات مفيدة. شكرا على ذلك.

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

مهما كانت المصطلحات النهائية ، يمكنني تخيل إدخال الأسماء المستعارة للنوع قبل الأنواع المسماة (ذات العلامات التجارية) ، خاصة وأن الإعلان عن أنواع مسماة جديدة من المحتمل أن يأتي بعد تقديم byte و rune في أي كتاب أو منهج. ومع ذلك ، أريد أن أكون مدركًا لاهتمامdavecheney بعدم تشجيع الأنماط المضادة.

بالنسبة إلى type intSet map[int]struct{} نقول إن map[int]struct{} هو النوع _underlying_. ماذا نسمي جانبي type intSet = map[int]struct{} ؟ الاسم المستعار والنوع المستعار؟

بالنسبة إلى %T ، أحتاج بالفعل إلى توضيح أن نتيجة byte و rune هي uint8 و int32 ، لذلك هذا ليس مختلف.

إذا كان هناك أي شيء ، أعتقد أن الأسماء المستعارة من النوع ستجعل شرح byte و rune أسهل في الشرح. IMO ، سيكون التحدي هو معرفة متى يجب استخدام الأنواع المسماة مقابل الأسماء المستعارة ، ومن ثم القدرة على إيصال ذلك.

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

يقوم إعلان النوع التقليدي (غير المستعار) بمزيد من العمل: يقوم أولاً بإنشاء نوع جديد من النوع الموجود على اليمين قبل ربط المعرف الموجود على اليسار به. وبالتالي فإن المعرف والنوع الموجود على اليمين ليسا متماثلين (يشتركان فقط في نفس النوع الأساسي). من الواضح أن هذا هو المفهوم الأكثر تعقيدًا.

نحتاج إلى مصطلح جديد لهذه الأنواع التي تم إنشاؤها حديثًا لأن أي نوع يمكن أن يكون له اسم الآن. ونحتاج إلى أن نكون قادرين على الرجوع إليها نظرًا لوجود قواعد محددة تشير إليها (هوية النوع ، والتخصيص ، وأنواع قاعدة المستقبِل).

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

باستخدام هذا القياس ، يمكننا إعادة صياغة القواعد الأخرى الموجودة أيضًا:

  • دائمًا ما يختلف النوع الملون عن أي نوع آخر (لأن كل إعلان نوع يستخدم لونًا جديدًا لم يسبق استخدامه من قبل).
  • قد ترتبط الطرق فقط بأنواع قاعدة أجهزة الاستقبال الملونة.
  • النوع الأساسي من النوع هو ذلك النوع الذي يتم تجريده من كل لونه.
    إلخ.

لا نطلق على اسم ثابت اسم مستعار ، والقيمة الثابتة هي ثابت الاسم المستعار

نقطة جيدة 👍

لست متأكدًا مما إذا كان القياس الملون مقابل غير الملون أسهل في الفهم ، لكنه يوضح أن هناك أكثر من طريقة لشرح المفاهيم.

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

type intSet map[int]struct{} // a new type with an underlying type map[int]struct{}

type myIntSet intSet // a new type with an underlying type map[int]struct{}

type otherIntSet = intSet // just another name (alias) for intSet, add methods to intSet (only in the same package)

type literalIntSet = map[int]struct{} // just another name for map[int]struct{}, no adding methods

ومع ذلك ، فإنه ليس من المستحيل التغلب عليه. بافتراض وصول هذا إلى Go 1.9 ، أظن أننا سنشهد الإصدارات الثانية من العديد من كتب Go. 😉

أشير بانتظام إلى مواصفة Go للحصول على المصطلحات المقبولة ، لذلك أشعر بالفضول الشديد بشأن المصطلحات التي يتم اختيارها في النهاية.

نحتاج إلى مصطلح جديد لهذه الأنواع التي تم إنشاؤها حديثًا لأن أي نوع يمكن أن يكون له اسم الآن.

بعض الافكار:

  • "مميز" أو "مميز" (كما في ، يمكن تمييزه عن الأنواع الأخرى)
  • "فريد" (كما هو الحال في نوع مختلف عن جميع الأنواع الأخرى)
  • "ملموس" (كما هو الحال في ، كيان موجود في وقت التشغيل)
  • "قابل للتحديد" (كما هو الحال في النوع له هوية)

bcmills كنا نفكر في أنواع مميزة وفريدة ومميزة وذات علامة تجارية وملونة ومحددة وغير

أوصي بعدم:

  • "ملونة" (في السياقات غير البرمجية ، تحمل عبارة "الأنواع الملونة" دلالات تحيز عنصري قوية)
  • "non-alias" (إنه أمر محير ، نظرًا لأن هدف الاسم المستعار قد يكون أو لا يكون ما كان يُطلق عليه سابقًا "النوع المسمى")
  • "معرفة" (تم تعريف الأسماء المستعارة أيضًا ، تم تعريفها فقط على أنها أسماء مستعارة)

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

تبدو فريدة ومتميزة مثل الخيارات البارزة حتى الآن.

إنها بسيطة ومفهومة بدون الكثير من السياق أو المعرفة الإضافية. إذا لم أكن أعرف الفرق ، أعتقد أنه سيكون لدي على الأقل إحساس عام بما يعنيه ذلك. لا أستطيع أن أقول ذلك عن الخيارات الأخرى.

بمجرد أن تتعلم المصطلح لا يهم ، لكن الاسم الدلالي يتجنب العوائق غير الضرورية التي تحول دون استيعاب التمييز.

هذا هو تعريف حجة bikeshed. روبرت لديه CL معلق على https://go-review.googlesource.com/#/c/36213/ يبدو جيدًا تمامًا.

يذكر CL https://golang.org/cl/36213 هذه المشكلة.

أريد طرح قضية go fix مرة أخرى.

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

من المهم جدًا IMO أن العنوان يتعلق بنقل الكتابة. ليس لدي رغبة في إرباك القضية. هدفنا هو التعامل مع نوع من التغييرات في واجهة المشروع. عندما نصل إلى تغيير في الواجهة ، فليس صحيحًا أننا نأمل أن يستخدم جميع المستخدمين هاتين الواجهتين (القديمة والجديدة) كما هو الحال في النهاية ، ولهذا السبب نقول "إصلاح الكود التدريجي". نأمل أن يقوم المستخدمون بإزالة / تغيير استخدام القديم.

ما زلت أعتبر الأداة أفضل طريقة لإصلاح الكود ، شيء مثل الفكرة التي اقترحها @ tux21b . على سبيل المثال:

$ cat "$GOROOT"/RENAME
# This file could be used for `go fix`
[package]
x/net/context=context
[type]
io.ByteBuffer=bytes.Buffer

$ go fix -rename "$GOROOT"/RENAME [packages]
# -- or --
# use a standard libraries rename table as default
$ go fix -rename [packages]
# -- or --
# include this fix as default
$ go fix [packages]

السبب الوحيد الذي يجعل rsc يرفض هنا هو أن التغييرات ستؤثر على الأدوات الأخرى. لكنني أعتقد أن هذا ليس صحيحًا في تدفق العمل هذا : إذا كانت هناك حزمة قديمة (مثل التبعية) تستخدم الاسم / المسار المهمل للحزمة ، على سبيل المثال x/net/context ، يمكننا إصلاح الكود في البداية ، تمامًا كما يوضح المستند كيفية ترحيل التعليمات البرمجية إلى إصدار جديد ، ولكن ليس الترميز الثابت ، عبر جدول قابل للتكوين بتنسيق نصي. ثم يمكنك استخدام أي أدوات وقتما تشاء مثل Go من الإصدار الجديد. هناك أثر جانبي: سيعدل الكود.

LionNatsu ، أعتقد أنك محق ، لكن أعتقد أن هذه مسألة منفصلة: هل يجب أن نتبنى اتفاقيات للحزم لشرح للعملاء المحتملين كيفية تحديث الكود الخاص بهم استجابة لتغييرات واجهة برمجة التطبيقات بطريقة ميكانيكية؟ ربما ، ولكن علينا معرفة ما هي تلك الاتفاقيات. هل يمكنك فتح قضية منفصلة لهذا الموضوع ، والإشارة مرة أخرى إلى هذه المحادثة؟ شكرا.

يذكر CL https://golang.org/cl/36691 هذه المشكلة.

مع هذا الاقتراح عند التلميح ، يمكنني الآن إنشاء هذه الحزمة:

package safe

import "unsafe"

type Pointer = unsafe.Pointer

الذي يسمح للبرامج بإنشاء قيم unsafe.Pointer بدون استيراد unsafe مباشرة:

package main

import "safe"

func main() {
    x := []int{4, 9}
    y := *(*int)(safe.Pointer(uintptr(safe.Pointer(&x[0])) + 8))
    println(y)
}

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

في إصدار إعلان الاسم المستعار ، السبب المنطقي لهذا هو: _ "السبب في أننا نسمح بالاسم المستعار غير الآمن. Pointer هو أنه من الممكن بالفعل تحديد نوع غير آمن.المؤشر كنوع أساسي." _ https://github.com/ golang / go / issues / 16339 # issuecomment -232435361

في حين أن هذا صحيح ، أعتقد أن السماح بالاسم المستعار unsafe.Pointer يقدم شيئًا جديدًا: يمكن للبرامج الآن إنشاء قيم unsafe.Pointer بدون استيراد صريح غير آمن.

لكتابة البرنامج أعلاه قبل هذا الاقتراح ، يجب أن أقوم بتحريك الخزنة. يتم صب المؤشر في حزمة تستورد غير آمنة. قد يجعل ذلك من الصعب بعض الشيء تدقيق البرامج لاستخدامها غير الآمن.

crawshaw ، ألا يمكنك فعل هذا من قبل؟

package safe

import (
  "reflect"
  "unsafe"
)

func Pointer(p interface {}) unsafe.Pointer {
  switch v := reflect.ValueOf(p); v.Kind() {
  case reflect.Uintptr:
    return unsafe.Pointer(uintptr(v.Uint()))
  default:
    return unsafe.Pointer(v.Pointer())
  }
}

أعتقد أن هذا سيسمح بالضبط لنفس البرنامج بالتجميع ، مع نفس عدم الاستيراد في الحزمة main .

(لن يكون بالضرورة برنامجًا صالحًا: تحويل uintptr -to- Pointer يتضمن استدعاء دالة ، لذلك لا يفي بقيد الحزمة unsafe الذي " يجب أن يظهر كلا التحويلين في نفس التعبير ، مع الحساب المتداخل بينهما فقط ". ومع ذلك ، أظن أنه من الممكن إنشاء برنامج مكافئ وصالح بدون استيراد unsafe من main خلال استخدام أشياء مثل reflect.SliceHeader .)

يبدو أن تصدير نوع مخفي غير آمن هو مجرد قاعدة أخرى لإضافتها إلى التدقيق.

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

crawshaw حسب تعليقي ، كان هذا صحيحًا أيضًا قبل أن يكون لدينا كتابة مستعارة. ما يلي صالح:

package a

import "unsafe"

type P unsafe.Pointer
package main

import "./a"
import "fmt"

var x uint64 = 0xfedcba9876543210
var h = *(*uint32)(a.P(uintptr(a.P(&x)) + 4))

func main() {
    fmt.Printf("%x\n", h)
}

وهذا يعني أنه في الحزمة الرئيسية ، يمكنني إجراء عمليات حسابية غير آمنة باستخدام a.P على الرغم من عدم وجود حزمة unsafe و a.P ليس اسمًا مستعارًا. كان هذا ممكنا دائما.

هل هناك شيء آخر تشير إليه؟

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

المواصفات في الواقع ليست واضحة في هذا الشأن. بالنظر إلى تنفيذ go / types ، اتضح أن تطبيقي الأولي تطلب unsafe.Pointer بالضبط ، وليس نوعًا ما حدث أن يكون له نوع أساسي من unsafe.Pointer . لقد وجدت للتو # 6326 وهو الوقت الذي قمت فيه بتغيير go / type لتكون متوافقة مع gc.

ربما يجب علينا عدم السماح بهذا بالنسبة لتعريفات الأنواع العادية وكذلك عدم السماح بالأسماء المستعارة لـ unsafe.Pointer . لا أستطيع أن أرى أي سبب وجيه للسماح به وهو يعرض للخطر صراحة الاضطرار إلى استيراد unsafe للتعليمات البرمجية غير الآمنة.

قد حدث هذا. لا أعتقد أن أي شيء بقي هنا.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات