Go: الاقتراح: المواصفات: إضافة دعم التعداد المكتوب

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

أود أن أقترح إضافة هذا التعداد إلى Go كنوع خاص من type . تم استعارة الأمثلة أدناه من مثال protobuf.

Enums في Go اليوم

type SearchRequest int
var (
    SearchRequestUNIVERSAL SearchRequest = 0 // UNIVERSAL
    SearchRequestWEB       SearchRequest = 1 // WEB
    SearchRequestIMAGES    SearchRequest = 2 // IMAGES
    SearchRequestLOCAL     SearchRequest = 3 // LOCAL
    SearchRequestNEWS      SearchRequest = 4 // NEWS
    SearchRequestPRODUCTS  SearchRequest = 5 // PRODUCTS
    SearchRequestVIDEO     SearchRequest = 6 // VIDEO
)

type SearchRequest string
var (
    SearchRequestUNIVERSAL SearchRequest = "UNIVERSAL"
    SearchRequestWEB       SearchRequest = "WEB"
    SearchRequestIMAGES    SearchRequest = "IMAGES"
    SearchRequestLOCAL     SearchRequest = "LOCAL"
    SearchRequestNEWS      SearchRequest = "NEWS"
    SearchRequestPRODUCTS  SearchRequest = "PRODUCTS"
    SearchRequestVIDEO     SearchRequest = "VIDEO"
)

// IsValid has to be called everywhere input happens, or you risk bad data - no guarantees
func (sr SearchRequest) IsValid() bool {
    switch sr {
        case SearchRequestUNIVERSAL, SearchRequestWEB...:
            return true
    }
    return false
}

كيف يمكن أن تبدو مع دعم اللغة

enum SearchRequest int {
    0 // UNIVERSAL
    1 // WEB
    2 // IMAGES
    3 // LOCAL
    4 // NEWS
    5 // PRODUCTS
    6 // VIDEO
}

enum SearchRequest string {
    "UNIVERSAL"
    "WEB"
    "IMAGES"
    "LOCAL"
    "NEWS"
    "PRODUCTS"
    "VIDEO"
}

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

  • الأمان للأنواع المصدرة : لا شيء يمنع أي شخص من القيام بـ SearchRequest(99) أو SearchRequest("MOBILEAPP") . تتضمن الحلول الحالية إنشاء نوع غير مُصدّر بخيارات ، ولكن ذلك غالبًا ما يجعل الشفرة الناتجة أكثر صعوبة في الاستخدام / المستند.
  • أمان وقت التشغيل : تمامًا مثل أن protobuf سوف يتحقق من الصلاحية أثناء إلغاء التنظيم ، فإن هذا يوفر التحقق من صحة اللغة على نطاق واسع ، في أي وقت يتم فيه إنشاء تعداد.
  • الأدوات / التوثيق : تضع العديد من الحزم اليوم خيارات صالحة في التعليقات الميدانية ، ولكن لا يفعلها الجميع وليس هناك ما يضمن أن التعليقات ليست قديمة.

أشياء للإعتبار

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

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

Go2 LanguageChange NeedsInvestigation Proposal

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

@ md2perpe التي لا تعداد.

  1. لا يمكن تعدادها وتكرارها.
  2. ليس لديهم تمثيل سلسلة مفيد.
  3. ليس لديهم هوية:

"اذهب
الحزمة الرئيسية

يستورد (
"FMT"
)

func main () {
اكتب SearchRequest int
مقدار ثابت (
Universal SearchRequest = ذرة
الويب
)

const (
    Another SearchRequest = iota
    Foo
)

fmt.Println("Should be false: ", (Web == Foo))
    // Prints: "Should be false:  true"

}
""

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

ال 180 كومينتر

derekparker هناك مناقشة لتقديم اقتراح Go2 في # 19412

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

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

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

كلاهما يتطلب كلمة رئيسية جديدة من شأنها كسر كود Go1 الصالح باستخدام ذلك كمعرف

أعتقد أنه يمكن حل المشكلة

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

إليك الطريقة الاصطلاحية لكتابة التعدادات في Go الحالي:

type SearchRequest int

const (
    Universal SearchRequest = iota
    Web
    Images
    Local
    News
    Products
    Video
)

هذا له ميزة أنه من السهل إنشاء علامات يمكن أن تكون OR: ed (باستخدام عامل التشغيل | ):

type SearchRequest int

const (
    Universal SearchRequest = 1 << iota
    Web
    Images
    Local
    News
    Products
    Video
)

لا أستطيع أن أرى أن إدخال كلمة رئيسية enum سيجعلها أقصر بكثير.

@ md2perpe التي لا تعداد.

  1. لا يمكن تعدادها وتكرارها.
  2. ليس لديهم تمثيل سلسلة مفيد.
  3. ليس لديهم هوية:

"اذهب
الحزمة الرئيسية

يستورد (
"FMT"
)

func main () {
اكتب SearchRequest int
مقدار ثابت (
Universal SearchRequest = ذرة
الويب
)

const (
    Another SearchRequest = iota
    Foo
)

fmt.Println("Should be false: ", (Web == Foo))
    // Prints: "Should be false:  true"

}
""

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

@ md2perpe iota هي طريقة محدودة للغاية للتعامل مع الأرقام ، والتي تعمل بشكل رائع لمجموعة محدودة من الظروف.

  1. أنت بحاجة إلى int
  2. ما عليك سوى أن تكون متسقًا داخل الحزمة الخاصة بك ، ولا تمثل الحالة الخارجية

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

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

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

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

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

for i, val := range SearchRequest {
...
}

إذا كان Go سيضيف أي شيء أكثر من ذرة ، فلماذا لا تذهب لأنواع البيانات الجبرية؟

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

bep ليس مناسبًا ، ولكن يمكنك الحصول على كل هذه الخصائص:

package main

var SearchRequests []SearchRequest
type SearchRequest struct{ name string }
func (req SearchRequest) String() string { return req.name }

func Request(name string) SearchRequest {
    req := SearchRequest{name}
    SearchRequests = append(SearchRequests, req)
    return req
}

var (
    Universal = Request("Universal")
    Web       = Request("Web")

    Another = Request("Another")
    Foo     = Request("Foo")
)

func main() {
    fmt.Println("Should be false: ", (Web == Foo))
    fmt.Println("Should be true: ", (Web == Web))
    for i, req := range SearchRequests {
        fmt.Println(i, req)
    }
}

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

  • التعدادات التي تم فحصها في وقت الترجمة ليست متوافقة مع الإصدارات السابقة أو اللاحقة في حالة الإضافات أو الإزالة. # 18130 يبذل جهدًا كبيرًا للتحرك نحو تمكين الإصلاح التدريجي للكود ؛ التعداد سيدمر هذا الجهد ؛ أي حزمة تريد تغيير مجموعة من التعدادات ، ستفكك جميع مستورديها تلقائيًا وقسريًا.
  • على عكس ما يدعي التعليق الأصلي ، فإن protobuf (لهذا السبب المحدد) لا يتحقق فعليًا من صحة حقول التعداد. يحدد proto2 أنه يجب معاملة قيمة غير معروفة للتعداد كحقل غير معروف ويحدد proto3 حتى ، أن الكود الذي تم إنشاؤه يجب أن يكون لديه طريقة لتمثيلها بالقيمة المشفرة (تمامًا كما يفعل go حاليًا مع التعداد الوهمي)
  • في النهاية ، لا يضيف الكثير في الواقع. يمكنك الحصول على التوتير باستخدام أداة سترينجر. يمكنك الحصول على التكرار ، عن طريق إضافة حارس MaxValidFoo const (لكن انظر التحذير أعلاه. لا يجب أن يكون لديك هذا الشرط). لا يجب أن يكون لديك حرفان ثابتان في المقام الأول. ما عليك سوى دمج أداة في CI الخاص بك للتحقق من ذلك.
  • لا أعتقد أن الأنواع الأخرى غير ints ضرورية بالفعل. يجب أن تغطي أداة stringer بالفعل التحويل من وإلى السلاسل ؛ في النهاية ، ستكون الشفرة التي تم إنشاؤها معادلة لما سينشئه المترجم على أي حال (ما لم تقترح بجدية أن أي مقارنة على "سلسلة تعدادات" ستكرر البايت ...)

بشكل عام ، مجرد -1 ضخم بالنسبة لي. ليس فقط أنه لا يضيف أي شيء ؛ إنه يؤلم بشدة.

أعتقد أن تنفيذ التعداد الحالي في Go واضح جدًا ويوفر عمليات تحقق كافية لوقت التجميع. أتوقع في الواقع نوعًا من تعدادات Rust مع مطابقة النمط الأساسية ، لكنها ربما تكسر ضمانات Go1.

نظرًا لأن التعدادات هي حالة خاصة لأنواع الجمع والحكمة الشائعة هي أنه يجب علينا استخدام واجهات لمحاكاة أنواع المجموع ، فمن الواضح أن الإجابة هي https://play.golang.org/p/1BvOakvbj2

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

بكل جدية ، بالنسبة للميزات التي تمت مناقشتها في هذا الموضوع ، قد تكون بعض الأدوات الإضافية مفيدة.

مثل أداة stringer ، يمكن لأداة "ranger" إنشاء ما يعادل Iter func في الكود الذي ربطته أعلاه.

شيء ما يمكن أن يولد تطبيقات {Binary، Text} {Marshaler، Unmarshaler} لتسهيل إرسالها عبر السلك.

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

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

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

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

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

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

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

أخيرًا ، الهوية المميزة لست متأكدًا من أنها ميزة مفيدة على الإطلاق. Enums ليست أنواع مجموع كما في Haskell ، على سبيل المثال. يتم تسميتها بأرقام. استخدام التعدادات كقيم للعلم ، على سبيل المثال ، أمر شائع. على سبيل المثال ، يمكن أن يكون لديك ReadWriteMode = ReadMode | WriteMode وهذا شيء مفيد. من الممكن أيضًا أن يكون لديك قيم أخرى ، على سبيل المثال قد يكون لديك DefaultMode = ReadMode . ليس الأمر وكأن أي طريقة يمكن أن تمنع شخصًا ما من كتابة const DefaultMode = ReadMode في أي حال ؛ ما الغرض من طلب حدوث ذلك في إعلان منفصل؟

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

alercah ، من فضلك لا تسحب هذا idomatic Go في أي نقاش مفترض أنه "حجة رابحة" ؛ لا يحتوي Go على Enums المضمنة ، لذا فإن الحديث عن بعض التعابير غير الموجودة لا معنى له.

تم تصميم Go ليكون لغة C / C ++ أفضل أو جافا أقل تطويلًا ، لذا فإن مقارنته بالأخير سيكون أكثر منطقية. وتحتوي Java على Enum type مدمج ("أنواع تعداد لغات برمجة Java أقوى بكثير من نظيراتها في اللغات الأخرى."): https://docs.oracle.com/javase/tutorial/java /javaOO/enum.html

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

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

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

تعد أنواع تعداد لغات برمجة Java أقوى بكثير من نظيراتها في اللغات الأخرى

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

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

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

//go:generate enumerator Foo,Bar
مكتوبة مرة واحدة ، ومتاحة في كل مكان. لاحظ أن المثال مجرد.

bep أعتقد أنك أخطأت في قراءة التعليق الأصلي. كان من المفترض أن تشير عبارة "Go idiomatic enums" إلى البناء الحالي لاستخدام النوع Foo int + const-exp + iota ، على ما أعتقد ، كي لا أقول "كل ما تقترحه ليس اصطلاحيًا".

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

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

لا يمكن استخدام كلمة رئيسية جديدة قبل Go 2. سيكون انتهاكًا واضحًا لضمان توافق Go 1.

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

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

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

على الجانب الآخر من ذلك ، إذا كنت أكتب جانب الخادم لواجهة برمجة التطبيقات نفسها ، فسيكون من الجيد أن تكون قادرًا على كتابة بيان تبديل على التعداد ، مما قد يؤدي إلى حدوث خطأ في المترجم (أو على الأقل بعض go vet ) إذا لم يتم التحقق من جميع القيم الممكنة للتعداد (أو على الأقل default: موجود).

أعتقد أن هذا (التعدادات) هو مجال حصل عليه Swift حقًا .

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

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

@ Merovius أحترم رأيك ، لكنني أختلف بأدب. يعد التأكد من استخدام القيم الصالحة فقط أحد الاستخدامات الأساسية للتعدادات.

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

للتعامل مع العمليات الخارجية التي لها حالة أه-أوه أمر لا بد منه بالتأكيد.

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

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

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

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

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

أنا ألعب مع تنفيذ عميل لخادم API. بعض الوسائط وقيم الإرجاع عبارة عن تعدادات في واجهة برمجة التطبيقات. يوجد 45 نوع تعداد إجمالاً.

استخدام الثوابت المعدودة في Go غير ممكن في حالتي لأن بعض القيم لأنواع التعداد المختلفة تشترك في نفس الاسم. في المثال أدناه ، يظهر Destroy مرتين لذا سيصدر المترجم الخطأ Destroy redeclared in this block .

type TaskAllowedOperations int
const (
    _ TaskAllowedOperations = iota
    Cancel
    Destroy
)

type OnNormalExit int
const (
    _ OnNormalExit = iota
    Destroy
    Restart
)

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

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

// ENUM(_, Cancel, Destroy)
type TaskAllowedOperations int

// ENUM(_, Destroy, Restart)
type OnNormalExit int

سوف تولد

const(
  _ TaskAllowedOperations = iota
  TaskAllowedOperationsCancel
  TaskAllowedOperationsDestroy
)

const(
  _ OnNormalExit = iota
  OnNormalExitDestroy
  OnNormalExitRestart
)

الجزء الأفضل هو أنه سينشئ طرقًا String() تستبعد البادئة فيها ، مما يسمح لك بتحليل "Destroy" إما TaskAllowedOperations أو OnNormalExit .

https://github.com/abice/go-enum

الآن بعد أن خرج القابس عن الطريق ...

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

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

  • المثال التمهيدي (Enums in Go today) مضلل: تم إنشاء هذا الرمز ولن يكتب أي شخص تقريبًا رمز Go مثل هذا يدويًا. في الواقع ، فإن الاقتراح (كيف يمكن أن يبدو مع دعم اللغة) أقرب بكثير مما نفعله بالفعل في Go.

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

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

  • كنقطة ثانوية ، لا تقتصر الثوابت المعرفة من ذرة iota في Go بالطبع على ints. طالما أنها ثوابت فهي مقيدة بالأنواع الأساسية (التي يُحتمل تسميتها) (بما في ذلك العوامات ، القيم المنطقية ، السلاسل: https://play.golang.org/p/lhd3jqqg5z).

  • يعطيmerovius نقاطًا جيدة حول قيود (ثابت!) عمليات التحقق من وقت الترجمة. أشك بشدة في أن التعدادات التي لا يمكن تمديدها مناسبة في الخيوط التي يكون التمديد فيها مرغوبًا أو متوقعًا (أي سطح API طويل العمر يتطور بمرور الوقت).

وهو ما يقودني إلى بعض الأسئلة حول هذا الاقتراح والتي أعتقد أنها بحاجة إلى إجابة قبل أن يكون هناك أي تقدم ذي مغزى:

1) ما هي التوقعات الفعلية للتعدادات المقترحة؟ bep يذكر التعداد والتكرار وتمثيلات السلسلة والهوية. هل يوجد المزيد؟ هل يوجد أقل؟

2) بافتراض القائمة في 1) ، هل يمكن تمديد التعدادات؟ إذا كان الأمر كذلك ، فكيف؟ (في نفس الحزمة؟ حزمة أخرى؟) إذا كان لا يمكن تمديدها ، لماذا لا؟ لماذا هذا ليس مشكلة في الممارسة؟

3) Namespace: في Swift ، يقدم نوع التعداد مساحة اسم جديدة. هناك آلات مهمة (السكر النحوي ، واستنتاج النوع) بحيث لا يلزم تكرار اسم مساحة الاسم في كل مكان. على سبيل المثال ، بالنسبة لقيم التعداد لشهر التعداد ، في السياق الصحيح ، يمكن للمرء أن يكتب. يناير بدلاً من الشهر. يناير (أو ما هو أسوأ ، MyPackage.Month. يناير). هل هناك حاجة إلى مساحة اسم تعداد؟ إذا كان الأمر كذلك ، فكيف يتم توسيع مساحة اسم التعداد؟ ما نوع السكر النحوي المطلوب لجعل هذا العمل عمليًا؟

4) هل قيم التعداد ثوابت؟ قيم ثابتة؟

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

(لقد صححت صياغتي للفقرة التالية في https://github.com/golang/go/issues/19814#issuecomment-322771922. أعتذر عن الإهمال في اختيار الكلمات أدناه.)

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

بدون محاولة الإجابة فعلاً على هذه الأسئلة ، فإن هذا الاقتراح لا معنى له

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

  • احتاج Go إلى هذا الاقتراح لأنه بدأ مناقشة تمس الحاجة إليها => القيمة والمعنى
  • إذا كان يؤدي أيضًا إلى الاقتراح رقم 21473 => القيمة والمعنى

المثال التمهيدي (Enums in Go today) مضلل: تم إنشاء هذا الرمز ولن يكتب أي شخص تقريبًا رمز Go مثل هذا يدويًا. في الواقع ، فإن الاقتراح (كيف يمكن أن يبدو مع دعم اللغة) أقرب بكثير مما نفعله بالفعل في Go.

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

// ACLRole is the level of access to grant.
type ACLRole string

const (
    RoleOwner  ACLRole = "OWNER"
    RoleReader ACLRole = "READER"
    RoleWriter ACLRole = "WRITER"
)

يستخدمون نفس البنية في أماكن متعددة.
https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/bigquery/table.go#L78 -L116
https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/storage/acl.go#L27 -L49

دار بعض النقاش لاحقًا حول كيفية جعل الأمور أكثر إيجازًا إذا كنت موافقًا باستخدام iota ، والذي يمكن أن يكون مفيدًا بحد ذاته ، ولكن لحالة استخدام محدودة. انظر تعليقي السابق لمزيد من التفاصيل. https://github.com/golang/go/issues/19814#issuecomment -290948187

@ نقطة عادلة bep ؛ أعتذر عن إهمالي في اختيار الكلمات. اسمحوا لي أن أحاول مرة أخرى ، وآمل أن أكون أكثر احترامًا ووضوحًا في الفقرة الأخيرة أعلاه هذه المرة:

لكي تكون قادرًا على إحراز تقدم ذي مغزى ، أعتقد أن مؤيدي هذا الاقتراح يجب أن يحاولوا أن يكونوا أكثر دقة قليلاً حول ما يعتقدون أنه ميزات مهمة للتعداد (على سبيل المثال من خلال الإجابة على بعض الأسئلة في https: // github. com / golang / go / issues / 19814 # issuecomment-322752526). من المناقشة حتى الآن يتم وصف الميزات المرغوبة غامضة إلى حد ما.

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

derekperkins أود أن أسمي تلك التعاريف الثابتة (المكتوبة) ، وليس التعدادات. أظن أن الخلاف بيننا يرجع إلى فهم مختلف لما يفترض أن يكون عليه "التعداد" ، ومن هنا جاءت أسئلتي أعلاه.

(كان من المفترض أن يذهب https://github.com/golang/go/issues/19814#issuecomment-322774830 إلى derekperkins بالطبع ، وليس @ derekparker. لقد هزمني الإكمال التلقائي.)

انطلاقًا من تعليق derekperkins ، والإجابة جزئيًا على أسئلتي ، أجمع أن "تعداد" Go يجب أن يحتوي على الأقل على الصفات التالية:

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

هل هذا يبدو صحيحا؟ إذا كان الأمر كذلك ، فما الذي يجب إضافته أيضًا إلى هذه القائمة؟

كل أسئلتك جيدة.

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

بافتراض القائمة في 1) ، هل يمكن تمديد التعدادات؟ إذا كان الأمر كذلك ، فكيف؟ (في نفس الحزمة؟ حزمة أخرى؟) إذا كان لا يمكن تمديدها ، لماذا لا؟ لماذا هذا ليس مشكلة في الممارسة؟

لا أعتقد أنه يمكن تمديد التعدادات لسببين:

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

Namespace: في Swift ، يقدم نوع التعداد مساحة اسم جديدة. هناك آلات مهمة (السكر النحوي ، واستنتاج النوع) بحيث لا يلزم تكرار اسم مساحة الاسم في كل مكان. على سبيل المثال ، بالنسبة لقيم التعداد لشهر التعداد ، في السياق الصحيح ، يمكن للمرء أن يكتب. يناير بدلاً من الشهر. يناير (أو ما هو أسوأ ، MyPackage.Month. يناير). هل هناك حاجة إلى مساحة اسم تعداد؟ إذا كان الأمر كذلك ، فكيف يتم توسيع مساحة اسم التعداد؟ ما نوع السكر النحوي المطلوب لجعل هذا العمل عمليًا؟

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

هل قيم التعداد ثوابت؟ قيم ثابتة؟

أعتقد أن الثوابت.

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

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

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

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

  • كما أنها تحدد نوع التعداد المرتبط بقيم التعداد (وإلا فهي مجرد ثوابت) في نفس الإعلان
  • من المستحيل إرسال / إنشاء قيمة تعداد لنوع تعداد موجود خارج إعلانه
  • من الممكن تكرارها

هل هذا الصوت عن الحق؟ (هذا من شأنه أن يتطابق مع النهج الكلاسيكي الذي اتبعته اللغات تجاه التعدادات ، والتي ابتكرها باسكال منذ حوالي 45 عامًا).

نعم ، هذا بالضبط ما أقترحه.

ماذا عن عبارات التبديل؟ AIUI هو أحد المحركات الرئيسية للاقتراح.

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

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

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

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

derekperkins بعض المزيد من الأسئلة:

  • ما هي القيمة الصفرية لمتغير من نوع التعداد لم تتم تهيئته بشكل صريح؟ أفترض أنه لا يمكن أن يكون صفرًا بشكل عام (مما يعقد تخصيص / تهيئة الذاكرة).

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

  • فيما يتعلق بالتكرار ، لماذا لا يكون دعم go-Generator ينتج عنه التكرار (والتشديد) غير كافٍ؟

ما هي القيمة الصفرية لمتغير من نوع التعداد لم تتم تهيئته بشكل صريح؟ أفترض أنه لا يمكن أن يكون صفرًا بشكل عام (مما يعقد تخصيص / تهيئة الذاكرة).

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

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

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

فيما يتعلق بالتكرار ، لماذا لا يكون دعم go-Generator ينتج عنه التكرار (والتشديد) غير كافٍ؟

التكرار ليس حالة الاستخدام الرئيسية الخاصة بي شخصيًا ، على الرغم من أنني أعتقد أنه يضيف قيمة إلى الاقتراح. إذا كان هذا هو العامل الدافع ، فربما يكون go generate كافياً. هذا لا يساعد في ضمان سلامة القيمة. تفترض الوسيطة Stringer() أن القيمة الأولية ستكون iota أو int أو نوعًا آخر يمثل القيمة "الحقيقية". سيتعين عليك أيضًا إنشاء (Un)MarshalJSON و (Un)MarshalBinary و Scanner/Valuer وأي طرق أخرى للتسلسل قد تستخدمها لضمان استخدام قيمة Stringer للتواصل مقابل كل ما يستخدمه Go داخليًا.

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

من @ ميروفيوس https://github.com/golang/go/issues/19814#issuecomment -290969864

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

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

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

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

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

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

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

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

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

ianlancetaylor لن أتمكن بالتأكيد من التحدث إلى التنفيذ الكامل ، ولكن إذا تم تنفيذ التعدادات كمصفوفة قائمة على 0 (وهو ما يبدو أنه يفضلgriesemer ) ، فسيبدو الرقم 0 كما يبدو الفهرس يمكن أن يتضاعف كـ "جميع البتات صفر".

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

Merovius إذا تم التحقق من الشمولية من خلال go vet أو بأداة مشابهة كما اقترحها jediorange مقابل فرضها من قبل المترجم ، فهل سيخفف ذلك من مخاوفك؟

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

كل ما يقال ، أنا لست صاحب قرار.

derekperkins في https://github.com/golang/go/issues/19814#issuecomment -322818206 أنت تؤكد ذلك (من وجهة نظرك):

  • يعلن إعلان التعداد عن نوع التعداد مع قيم التعداد المسماة (الثوابت)
  • من الممكن تكرارها
  • لا يمكن إضافة أي قيم إلى التعداد خارج إعلانه
    وبعد ذلك: يجب أن يكون التبديل في التعدادات (أو ربما لا يكون) شاملاً (يبدو أقل أهمية)

في https://github.com/golang/go/issues/19814#issuecomment -322895247 أنت تقول:

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

وفي https://github.com/golang/go/issues/19814#issuecomment -322903714 أنت تقول إن "القدرة على تعديلها جزء مهم من هذا الاقتراح".

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

في https://github.com/golang/go/issues/19814#issuecomment -322903714 أنت تقول أنه يمكن تنفيذ التعدادات كمصفوفة تستند إلى 0. يشير هذا إلى أن إعلان التعداد يقدم نوعًا جديدًا مع قائمة مرتبة بأسماء التعداد والتي هي عبارة عن فهارس ثابتة تعتمد على الصفر في مصفوفة من قيم التعداد (يتم حجز المساحة تلقائيًا لها). هل هذا ما تعنيه؟ إذا كان الأمر كذلك ، فلماذا لا يكفي التصريح عن مصفوفة ذات حجم ثابت وقائمة من مؤشرات الثوابت لتتماشى معها؟ سيضمن فحص حدود المصفوفة تلقائيًا أنه لا يمكنك "توسيع" نطاق التعداد ، وسيكون التكرار ممكنًا بالفعل.

ماذا ينقصني؟

أنا في حيرة من أمري: لذا فإن التكرار ليس حافزًا أساسيًا ، حسنًا.

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

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

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

في # 19814 (تعليق) أنت تقول أنه يمكن تنفيذ التعدادات كمصفوفة على أساس 0.

هذا فقط أتخيل ما هو أبعد من راتبي فيما يتعلق بكيفية تخيل أنه يمكن تنفيذه خلف الكواليس ، بناءً على https://github.com/golang/go/issues/19814#issuecomment -322884746 و ianlancetaylor 's https: //github.com/golang/go/issues/19814#issuecomment -322899668

"هل يمكننا إجراء عمليات حسابية محدودة باستخدام قيم التعداد؟ على سبيل المثال ، في Pascal (التي برمجت فيها مرة واحدة ، والعودة إلى الوراء) ، كان من المدهش في كثير من الأحيان التكرار في الخطوات> 1. وأحيانًا أراد المرء حساب قيمة التعداد."

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

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

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

إلى أي شخص آخر أبدى اهتمامًا ، لا تتردد في المشاركة.

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

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

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

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

هل هذا منطقي؟

griesemer إنه أمر منطقي تمامًا وأنا أفهم الشريط الذي يجب تمريره والذي تحدث عنه rsc في Gophercon. من الواضح أن لديك فهمًا أعمق بكثير مما سأفعله في أي وقت مضى. في # 21473 ، ذكرت أن iota for vars لم يتم تنفيذها لأنه لم تكن هناك حالة استخدام مقنعة في ذلك الوقت. هل هذا هو نفس سبب عدم تضمين enum من البداية؟ سأكون مهتمًا جدًا بمعرفة رأيك حول ما إذا كان سيضيف قيمة إلى Go أم لا ، وإذا كان الأمر كذلك ، فأين ستبدأ العملية؟

derekperkins بخصوص سؤالك في https://github.com/golang/go/issues/19814#issuecomment -323144075: في ذلك الوقت (في تصميم Go) كنا نفكر فقط في تعدادات بسيطة نسبيًا (مثل Pascal أو C-style). لا أتذكر كل التفاصيل ولكن كان هناك بالتأكيد شعور بعدم وجود فائدة كافية للآلات الإضافية المطلوبة للتعدادات. شعرنا أنها كانت في الأساس تصريحات مجيدة ثابتة.

هناك أيضًا مشاكل في هذه التعدادات التقليدية: من الممكن إجراء العمليات الحسابية بها (إنها مجرد أعداد صحيحة) ، ولكن ماذا يعني إذا خرجت "خارج نطاق (التعداد)"؟ في Go هم مجرد ثوابت ولا يوجد "خارج النطاق". واحد آخر هو التكرار: في باسكال ، كانت هناك دوال خاصة مدمجة (أعتقد أن SUCC و PRED) لدفع قيمة متغير من نوع التعداد للأمام وللخلف (في لغة C ، هناك فقط ++ أو -). لكن نفس المشكلة تظهر هنا أيضًا: ماذا يحدث إذا تجاوز المرء النهاية (مشكلة شائعة جدًا في حلقة for تتراوح بين قيم التعداد باستخدام ++ أو ما يعادله من Pascal SUCC). أخيرًا ، يقدم إعلان التعداد نوعًا جديدًا ، عناصره هي قيم التعداد. هذه القيم لها أسماء (تلك المعرفة في إعلان التعداد) ، ولكن هذه الأسماء (في باسكال ، ج) في نفس النطاق مثل النوع. هذا غير مُرضٍ بعض الشيء: عند الإعلان عن عددين مختلفين ، قد يأمل المرء أن يستخدم نفس اسم قيمة التعداد لكل نوع تعداد بدون تعارض ، وهو أمر غير ممكن. بالطبع Go لا يحل ذلك أيضًا ، لكن الإعلان الثابت أيضًا لا يبدو أنه يقدم مساحة اسم جديدة. الحل الأفضل هو إدخال مساحة اسم مع كل تعداد ، ولكن بعد ذلك في كل مرة يتم فيها استخدام قيمة تعداد ، يجب أن يتم تصنيفها باسم نوع التعداد ، وهو أمر مزعج. يحل Swift هذا عن طريق استنتاج نوع التعداد حيثما أمكن ، وبعد ذلك يمكن للمرء استخدام اسم قيمة التعداد مسبوقًا بنقطة. لكن هذا قليل جدًا من الآلات. وأخيرًا ، في بعض الأحيان (غالبًا ، في واجهات برمجة التطبيقات العامة) ، يحتاج المرء إلى تمديد تصريح التعداد. إذا لم يكن ذلك ممكنًا (أنت لا تملك الرمز) ، فهناك مشكلة. مع الثوابت ، هذه المشاكل غير موجودة.

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

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

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

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

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

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

أعتقد أن الطريقة المتعارف عليها Go تقوم بالتعداد اليوم (كما هو موضح في https://github.com/golang/go/issues/19814#issuecomment-290909885) قريبة جدًا من التصحيح.
هناك بعض العيوب:

  1. لا يمكن تكرارها
  2. ليس لديهم تمثيل سلسلة
  3. يمكن للعملاء تقديم قيم تعداد وهمية

أنا بخير بدون # 1.
go: إنشاء + stringer يمكن استخدامه للرقم 2. إذا لم يتعامل ذلك مع حالة الاستخدام الخاصة بك ، فاجعل النوع الأساسي من "التعداد" سلسلة بدلاً من int ، واستخدم قيم ثابتة للسلسلة.

3 هو من الصعب التعامل مع Go اليوم. لدي اقتراح سخيف قد يتعامل مع هذا بشكل جيد.

أضف كلمة رئيسية explicit إلى تعريف النوع. تمنع هذه الكلمة الأساسية التحويلات إلى هذا النوع باستثناء التحويلات في كتل ثابتة في الحزمة التي تم تعريف هذا النوع فيها. (أو restricted ؟ أو ربما enum يعني explicit type ؟)

إعادة استخدام المثال الذي أشرت إليه أعلاه ،

//go:generate stringer -type=SearchRequest
explicit type SearchRequest int

const (
    Universal SearchRequest = iota
    Web
    Images
    Local
    News
    Products
    Video
)

هناك تحويلات من int إلى SearchRequest داخل كتلة const . لكن مؤلف الحزمة فقط هو من يمكنه تقديم قيمة SearchRequest جديدة ، ومن غير المرجح أن يقدم قيمة واحدة عن طريق الخطأ (من خلال تمرير int إلى دالة تتوقع SearchRequest ، على سبيل المثال).

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

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

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

سأكون على استعداد للنظر في طريقة تسمح لـ Go بمنع تحويلات النوع الصريح في ظل ظروف معينة

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

@ randall77 ، ianlancetaylor كيف يمكن أن يعمل اقتراح explicit مع القيمة الصفرية لهذا النوع؟

derekperkins عدم السماح بتحويلات النوع تمامًا سيجعل من المستحيل استخدام هذه الأنواع في أجهزة التشفير / أجهزة فك التشفير العامة ، مثل حزم encoding/* .

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

griesemer أعرف :) لكنني فهمت derekperkins ليقترح بشكل مختلف.

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

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

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

griesemerMerovius سأعيد نشر الاقتباس من ianlancetaylor مرة أخرى ، لأنه كان اقتراحه ، وليس اقتراحه.

سأكون على استعداد للنظر في طريقة تسمح لـ Go بمنع تحويلات النوع الصريح في ظل ظروف معينة

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

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

  1. تعدادات متجذرة في الأنواع الأساسية فقط (سلسلة ، uint ، int ، rune ، إلخ). إذا لم يكن هناك نوع أساسي ضروري ، فقد يكون افتراضيًا على uint؟
  2. يجب التصريح عن جميع القيم الصالحة للتعداد بإعلان النوع - الثوابت. لا يمكن تحويل القيم غير الصالحة (لم يتم التصريح عنها في إعلان النوع) إلى نوع التعداد.
  3. تمثيل سلسلة تلقائي لتصحيح الأخطاء (جميل أن يكون).
  4. عمليات التحقق من وقت التجميع للتأكد من شمولية عبارات التبديل في التعداد. اختياريا ، أوصي (عبر go vet ؟) بحالة default ، حتى عندما تكون شاملة بالفعل (من المحتمل أن تكون خطأ) للتغييرات المستقبلية.
  5. يجب أن تكون القيمة الصفرية غير صالحة أساسًا (وليس شيئًا في إعلان التعداد). أود شخصياً أن يكون nil ، مثل الشريحة.

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

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

كمثال على كيفية الإعلان عنها:

type MetadataBlockType enum[uint] {
    StreamInfo:    0
    Padding:       1
    Application:   2
    SeekTable:     3
    VorbisComment: 4
    CueSheet:      5
    Picture:       6
}

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

اكتب EnumA int
مقدار ثابت (
EnumA غير معروف = ذرة
AAA
)


اكتب EnumB int
مقدار ثابت (
EnumB غير معروف = ذرة
BBB
)

لا يمكن أن توجد قطعتان من الكود في ملف Go واحد ، ولا نفس الحزمة ، ولا حتى أحدهما يتم استيراده من حزمة أخرى.

يرجى فقط تنفيذ طريقة C # لتنفيذ Enum:
اكتب عدد الأيام {السبت ، الأحد ، الاثنين ، الثلاثاء ، الأربعاء ، الخميس ، الجمعة}
اكتب عدد الأيام [دوليًا] { السبت: 1 ، الأحد ، الثلاثاء ، الأربعاء ، الخميس ، الجمعة}
اكتب عدد الأيام [سلسلة] {السبت: "ساتورا" ، الأحد: "الأحد" ، إلخ}

KamyarM كيف يكون هذا أفضل من

type Days int
const (
  Sat Days = 1+iota
  Sun
  ...
)

و

type Days string
const (
  Sat Days = "Saturday"
  Sun      = "Sunday"
  ...
)

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

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

أود أن أرى شيئًا من هذا القبيل

type WeekDay enum string {
  Monday "mon"
  Tuesday "tue"
  // etc...
}

أو باستخدام تلقائي iota :

// this assumes that iota automatically assigned to the first declared enum key
// and values have unsigned integer type
// value is positional, so if you decide to rename your key 
// you don't have to change everything in db
// also it is easy to grow your lists
type WeekDay enum {
  Monday
  Tuesday
}

سيوفر هذا البساطة وسهولة الاستخدام:

func makeItWorkOn(day WeekDay) {
  // your implementation
}

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

if day in WeekDay {
  makeItWorkOn(day)
}

وأشياء بسيطة مثل:

if day == WeekDay.Monday {
 // whatever
}

لأكون صادقًا ، سيكون تركيب الجملة المفضل لدي مثل هذا (KISS):

// type automatically inferred from values or `iota`
enum WeekDay {
  Monday "mon"
  Tuesday "tue"
}

zoonman المثال الأخير لا يتبع مبدأ Go التالي: إعلان الوظيفة يبدأ بـ func ، إعلان النوع يبدأ بـ type ، إعلان متغير يبدأ بـ var ، ...

@ md2perpe لا أحاول اتباع مبادئ "النوع" Go ، فأنا أكتب التعليمات البرمجية يوميًا والمبدأ الوحيد الذي أتبعه - اجعل الأمور بسيطة.
أكثر من كود يجب أن تكتبه _ لاتباع المبادئ_ ثم يضيع المزيد من الوقت.
TBH I'm Go مبتدئ ولكن هناك الكثير من الأشياء التي يمكنني انتقادها.
علي سبيل المثال:

struct User {
  Id uint
  Email string
}

أسهل في الكتابة والفهم من

type User struct {
  Id uint
  Email string
}

يمكنني أن أعطيك مثالاً حيث يجب استخدام النوع:

// this is terrible because it blows your mind off
// especially if you Go newbie
// this should not be allowed by compiler/linter:
w := map[string]interface{}{"type": 0, "connected": true}

// it has to be splitted into type definition
type WeirdJSON map[string]interface{}

// and used like
w := WeirdJSON{"type": 0, "connected": true}

اعتدت كتابة تعليمات برمجية بلغة Asm ، C ، C ++ ، Pascal ، Perl ، PHP ، Ruby ، ​​Python ، JavaScript ، TypeScript ، Now Go. لقد رأيت كل ذلك. تخبرني هذه التجربة أن الشفرة يجب أن تكون مقتضبة وسهلة القراءة والفهم .

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

const (
        SMTPE0 int8 = ((-24 - (1 + (iota - 1) * 3) % 6 * (iota - 1) / ((iota - 1) | 0x01)) - 10 * ((iota - 1) % 2) - 5 * (iota / 3 - iota / 4) ) * iota / (iota | 0x01)
    SMTPE24 
    SMTPE25
    SMTPE29
    SMTPE30
)
const (
   _SMTPE0 int8 = 0 
   _SMTPE24 int8 = -24
   _SMTPE25 int8 = -25
   _SMTPE29 int8 = -29
   _SMTPE30 int8 = -30
)

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

func IsSMTPE(status int8) bool {
    j := 4
    for i:= 0; i >= -30; i -= j % 6{
        if i == int(status){ 
            return true
        }
        j+=3
    }

    return status == 0
}

PlayGroundRef

تجعل Enums حياة المبرمجين في بعض الحالات أكثر بساطة. Enums هي مجرد أداة ثم تستخدمها بشكل صحيح ، فقد توفر الوقت وتزيد الإنتاجية. أعتقد أنه لا توجد problens لتنفيذ ذلك في Go 2 كما هو الحال في c ++ أو c # أو غيرها من اللغات. هذا المثال مجرد مزحة ، لكنه يوضح المشكلة بوضوح.

@ streeter12 لا أرى كيف أن مثالك "يوضح المشكلة بوضوح". كيف ستجعل التعدادات هذا الرمز أفضل أو أكثر أمانًا؟

هناك فئة C # مع تنفيذ نفس منطق التعداد.

 public enum SMTPE : sbyte
   {
        SMTPE0 = 0,
        SMTPE24 = -24,
        SMTPE25 = -25,
        SMTPE29 = -29,
        SMTPE30 = -30
   }

   public class TestClass
   {
        public readonly SMTPE smtpe;

        public TestClass(SMTPE smtpe)
        {
            this.smtpe = smtpe;
        }
   } 

باستخدام تعدادات وقت التجميع ، يمكنني:

  1. ليس لديك عمليات تحقق وقت التشغيل.
  2. تقليل فرصة الخطأ من قبل الفريق بشكل كبير (لا يمكنك تمرير قيمة خاطئة في وقت الترجمة).
  3. لا يتعارض مع مفهوم ذرة.
  4. أسهل في فهم المنطق من أن يكون لديك اسم واحد للثوابت (من المهم إذن أن تمثل الثوابت بعض قيم بروتوكول المستوى المنخفض).
  5. يمكنك جعل ToString () أسلوبًا تناظريًا لعمل تمثيل بسيط للقيم. (CONNECTION_ERROR.NO_INTERNET أفضل من 0x12). أعرف شيئًا عن stringer ، لكن لا يوجد إنشاء كود واضح باستخدام التعدادات.
  6. في بعض اللغات ، يمكنك الحصول على مجموعة من القيم والنطاق وما إلى ذلك.
  7. من السهل فهمها أثناء قراءة الكود (لا حاجة إلى حسابات في الرأس).

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

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

الجواب مجرد حاجة بسيطة لجعل بعض الملحقات لا تستخدم التعداد.
تحتاج FE إلى جعل آلة الحالة تستخدم نمط الحالة بدلاً من التعداد.

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

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

@ streeter12 لسوء الحظ ، فإن الحقيقة هي أن الأعداد تحتاج في كثير من الأحيان إلى التمديد.

griesemer يؤدي تمديد نوع التعداد / المجموع إلى إنشاء نوع منفصل وغير متوافق أحيانًا.

لا يزال هذا صحيحًا في Go على الرغم من عدم وجود أنواع صريحة للتعداد / المجاميع. إذا كان لديك "نوع تعداد" في حزمة تتوقع قيمًا في {1 ، 2 ، 3} وقمت بتمريره 4 من "نوع التعداد الموسع" ، فإنك لا تزال تنتهك عقد "النوع" الضمني.

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

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

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

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

جيد جدا

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

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

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

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

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

على أي حال ، كنت أشير على وجه التحديد إلى:

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

و

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

هذه الحجة لا تعمل بالنسبة لي للأسباب التي ذكرتها.

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

(تم الإرسال متأخرًا - كان من المفترض أن يكون ردًا على https://github.com/golang/go/issues/19814#issuecomment-349158748)

griesemer حقًا وقد كانت بالتأكيد الدعوة الصحيحة لـ Go 1 ولكن بعضها يستحق إعادة التقييم لـ Go 2.

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

هذا النهج https://play.golang.org/p/7ud_3lrGfx يمنحك كل شيء باستثناء

  1. الأمان ضمن الحزمة المحددة
  2. القدرة على إجراء تبديل للاكتمال

يمكن أيضًا استخدام هذا النهج لأنواع الجمع الصغيرة والبسيطة † ولكنه أكثر صعوبة في الاستخدام ، ولهذا السبب أعتقد أن شيئًا مثل https://github.com/golang/go/issues/19412#issuecomment -323208336 سيضيف إلى اللغة ويمكن استخدامها بواسطة منشئ الكود لإنشاء أنواع تعداد تتجنب المشاكل 1 و 2.

† انظر https://play.golang.org/p/YFffpsvx5e للحصول على رسم تخطيطي لـ json. تحدث مع هذا البناء

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

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

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

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

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

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

Merovius عملية نظام التشغيل والحزمة كلاهما حدود ثقة ، كما وضعتها.

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

ومع ذلك ، باستخدام نوع التعداد ، يمكنك بالطبع تضمين حالات محددة لنمذجة هذه الأخطاء. علي سبيل المثال

type FromTheNetwork enum {
  // pretend all the "valid" values are listed here
  Missing // the value was not included in the message
  Unknown // the value was not in the set of the valid values
  Error // there was an error attempting to read the value
}

وباستخدام أنواع المجموع ، يمكنك الذهاب إلى أبعد من ذلك:

type FromTheNetwork pick {
  Missing struct{} // Not included in message
  Valid somepkg.TheSumBeingReceived // Include valid states with composition
  Unknown []byte // Raw bytes of unknown value received
  Error error // The error from attempting to read the value
}

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

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

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

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

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

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

ومع ذلك ، باستخدام نوع التعداد ، يمكنك بالطبع تضمين حالات محددة لنمذجة هذه الأخطاء.

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

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

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

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

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

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

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

مثال 1. أنا مطور ذو مستوى منخفض ، وأحتاج إلى ثابت لبعض عناوين السجلات ، وقيم بروتوكول منخفضة المستوى ، وما إلى ذلك. في الوقت الحالي في Go ، حصلت على حل واحد فقط: هو استخدام consts بدون ذرة ، لأنه سيكون قبيحًا في كثير من الحالات . يمكنني الحصول على عدة ثوابت لحزمة واحدة وبعد الضغط. حصلت على جميع الثوابت العشرين وإذا كان لديهم نفس النوع والأسماء المتشابهة يمكنني ارتكاب أخطاء. إذا كان المشروع كبيرًا ، فستحصل على هذا الخطأ. لمنع هذا من خلال البرمجة الدفاعية ، يجب علينا TDD أن نعرض رمز التحقق المكرر (رمز مكرر = أخطاء / اختبارات مكررة في أي حال). مع استخدام التحويلات ، لا نواجه مشكلات مماثلة ولن تتغير القيم أبدًا في هذه الحالة (حاول أن تجد موقفًا عندما تكون عناوين السجلات متوقفة في الإنتاج :)). ما زلنا في بعض الأحيان نتحقق من أن القيمة التي نحصل عليها من ملف / شبكة من الخ في النطاق ، ولكن لا توجد أي مشاكل لجعل هذا مركزيًا (انظر c # Enum.علي سبيل المثال). مع التعدادات ، أحفظ الوقت والأداء في هذه الحالة.

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

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

الفرق الرئيسي بين التعدادات ووقت التجميع حسب رأيي هو أن التعدادات لها عقدين رئيسيين.

  1. عقد التسمية.
  2. عقد القيم.
    تم النظر من قبل في جميع الحجج المؤيدة والمعارضة لهذه الفقرة.
    إذا كنت تستخدم برمجة عقد الاستخدام ، يمكنك بسهولة فهم فوائد ذلك.

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

بعد كل ما قيل ، أود أن أشير إلى أن Iota لها أيضًا عيوبها.

  1. دائمًا ما يكون له نوع int ،
  2. تحتاج إلى حساب القيم في الرأس. يمكنك أن تضيع الكثير من الوقت في محاولة حساب القيم ، وتأكد من أن الأمر على ما يرام.
    باستخدام enums / const ، يمكنني فقط الضغط على F12 الجواب لرؤية جميع القيم.
  3. تعبير Iota هو تعبير كود تحتاجه أيضًا لاختبار هذا.
    في بعض المشاريع ، رفضت تمامًا استخدام iota لهذه الأسباب.

تحاول أن تعطي مثالًا حيث يكون استخدام التعداد أمرًا سخيفًا

معذرةً على فظاظتي ، لكن بعد هذا التعليق لا أعتقد أن لديك الكثير من الأرضية لتقف عليها هنا.

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

يمكننا أن نختلف بشكل معقول ، ولكن يجب علينا جميعًا أن نجادل بحسن نية على الأقل.

مثال 1

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

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

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

إذا كنت تخطئ في كتابة الأسماء ، فلن يساعدك التعداد المغلق. فقط إذا لم تستخدم الأسماء الرمزية واستخدمت int / string-literals بدلاً من ذلك.

مثال 2

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

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

مثال 3

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

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

دائمًا ما يكون له نوع int ،

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

تحتاج إلى حساب القيم في الرأس.

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

تعبير Iota هو تعبير كود تحتاجه أيضًا لاختبار هذا.

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

iota ليست الطريقة الموصى بها حاليًا لعمل التعدادات في Go - الإعلانات const هي. يعمل iota فقط كطريقة أكثر عمومية لحفظ الكتابة عند تدوين إقرارات ثابت متتالية أو معادلة.

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

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

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

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

إذا كنت تخطئ في كتابة الأسماء ، فلن يساعدك التعداد المغلق. فقط إذا لم تستخدم الأسماء الرمزية واستخدمت int / string-literals بدلاً من ذلك.

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

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

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

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

في هذه الحالة ، لا يزال لديك فوائد تحويل الاسم والتحقق من وقت التجميع ،

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

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

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

انها ليست حجة للتعداد.

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

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

كما أنها ليست قابلة للتوسيع بأمان.

اذا كنت تمتلك

package p
type Enum int
const (
  A Enum = iota
  B
  C
)
func Make() Enum {...}
func Take(Enum) {...}

و

package q
import "p"
const D enum = p.C + 1

في حدود q ، من الآمن استخدام D (ما لم تضيف النسخة التالية من p التسمية الخاصة بها مقابل Enum(3) ) ولكن فقط طالما لم تفعل ذلك أبدًا مرره مرة أخرى إلى p : يمكنك الحصول على نتيجة p.Make وتحويل حالتها إلى D ولكن إذا اتصلت p.Take عليك التأكد من ذلك لم يتم تجاوزه q.D وعليه التأكد من أنه يحصل فقط على A أو B أو C أو أن لديك خطأ. يمكنك حل هذا عن طريق العمل

package q
import "p"
type Enum int
const (
    A = p.A
    B = p.B
    C = p.C
    D = C + 1
)
// needs to return an error if passed D or an unknown state of Enum
func To(Enum) (p.Enum, error) {...}
// needs to return an error if p.Enum has a value not known to the author
// at the time this package was written.
func From(p.Enum) (Enum, error) {...}

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

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

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

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

package p
type Enum pick {
  A, B, C struct{}
}

يمكن "تمديد" Enum مع

package q
import "p"
type Enum pick {
  P p.Enum
  D struct{}
}

وهذه المرة آمن تمامًا لإصدار جديد p لإضافة D . الجانب السلبي الوحيد هو أنه يتعين عليك التبديل المزدوج للوصول إلى حالة p.Enum من داخل q.Enum لكنه واضح وواضح ، وكما ذكرت ، يمكن للمحرر الخاص بك بصق الهيكل العظمي من التبديل تلقائيا.

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

هناك مشكلتان مع هذا.

إذا كان لديك نوع متكامل محدد مع تسميات معينة لمجموعة فرعية من مجالها عبر const / iota:

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

const (
  //Name is the ID of a record field
  Name Record = iota
  //EmpID is the ID of an employee ID field
  EmpID

  //Intermediate values are reserved for future versions

  //Custom is the base of custom fields. Any custom field must have a unique ID greater than Custom.
  Custom Record = 42
)

هذا لا يعني أن 0 و 1 و 42 هي مجال نوع التسجيلة. العقد أكثر دقة وسيتطلب أنواعًا تابعة للنمذجة. (من المؤكد أن هذا سيكون بعيدًا جدًا!)

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

هناك بديل لأنواع التعداد والجمع هنا يتغلب على هذا ، على الرغم من أن له مشكلاته الخاصة.

لنفترض أن النوع الحرفي range m n ينشئ نوعًا متكاملًا لا يقل عن m و n على الأكثر (لكل v ، m ≤ v ≤ n). مع ذلك يمكننا تحديد مجال التعداد ، مثل

package p
type Enum range 0 2
const (
  A Enum = iota
  B
  C
)

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

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

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

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

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

السبب الرئيسي في استخدامي golang فوق python و javascript واللغات الشائعة الأخرى التي لا تحتوي على أحرف هو أمان الكتابة. لقد فعلت الكثير مع Java وشيء واحد أفتقده في golang توفره Java هو التعدادات الآمنة.

لا أوافق على أن أكون قادرًا على التفريق بين الأنواع بالتعدادات. إذا كنت بحاجة إلى ints ، فما عليك سوى التمسك بـ ints بدلاً من ذلك ، كما تفعل Java. إذا كنت بحاجة إلى تعدادات آمنة ، أقترح اتباع بناء الجملة.

type enums enum { foo, bar, baz }

rudolfschmidt ، أتفق معك ، قد يبدو الأمر كذلك:

type DaysOfTheWeek enum {
  Monday
  Tuesday
}

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

لنفترض أن لدينا قائمة بالعملات المشفرة:

type CryptoCurrency enum {
  BTC
  ETH
  XMR
}

نحن نتبادل البيانات مع أنظمة طرف ثالث متعددة. دعنا نقول أن لديك الآلاف منهم.
لديك تاريخ طويل ، وعدد من البيانات في التخزين. الوقت يمر ، دعنا نقول ، تموت BitCoin في النهاية. لا أحد يستخدمه.
لذلك قررت إسقاطه من الهيكل:

type CryptoCurrency enum {
  ETH
  XMR
}

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

type CryptoCurrency enum {
  ETH = 1, // reminds const?
  XMR = 2
}
// this is real life case 
v := 3
if v is CryptoCurrency {
 // right?
} else {
 // nope, provide default value
 v = CryptoCurrency.ETH
}

علينا التفكير في إمكانية تطبيق التعدادات وحالات الاستخدام.

يمكننا تعلم كلمتين رئيسيتين جديدتين إذا كان بإمكانهما توفير آلاف الأسطر من التعليمات البرمجية المعيارية.

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

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

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

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

يبدو لي أنه من السهل جدًا توفير هذه الوظيفة كأداة. جزء منه موجود بالفعل في أداة سترينجر. إذا كانت هناك أداة يمكنك الاتصال بها مثل enumer Foo ، والتي من شأنها إنشاء طرق fmt.Stringer لـ Foo و (على سبيل المثال) طريقة Known() bool التي تتحقق مما إذا كان القيمة المخزنة في نطاق القيم المعروفة ، فهل هذا يخفف من مشاكلك؟

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

. إن استخدامه للتعدادات بشكل عام يبدو وكأنه عكاز لنظام كتابة مبسط.

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

وبالنظر إلى كيف أن Go هو كل شيء يتعلق بالسلامة من الكتابة ، وهذا يتعارض مع فلسفة اللغة نفسها.

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

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

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

لقد جمعت بعض الأفكار من المناقشة ، ما رأيك في ذلك؟

بعض المعلومات الأساسية:

  1. يمكنك تمديد تعداد مثل أي نوع آخر.
  2. يتم تخزينها بشكل ثابت ، ولكن مع اسم النوع كبادئة. السبب: عند استخدام iota-enums الحالية ، من المحتمل أن تكتب اسم التعداد كبادئة لكل ثابت. مع هذه الميزة يمكنك فقط تجنبها.
  3. فهي غير قابلة للتغيير وتعامل مثل أي ثابت آخر.
  4. يمكنك التكرار على التعدادات. عندما تفعل هذا ، فإنهم يتصرفون مثل الخريطة. المفتاح هو اسم التعداد ، القيمة هي قيمة التعداد.
  5. يمكنك إضافة عمليات إلى التعداد ، كما تفعل مع أي نوع آخر.
  6. كل قيمة تعداد يجب أن يتم إنشاؤها تلقائيًا:
  7. سيعيد Name() اسم متغير التعداد
  8. سيعيد Index() فهرس التعداد الذي يتزايد تلقائيًا. يبدأ حيث تبدأ المصفوفة.

رمز:
"اذهب
الحزمة الرئيسية

// مثال أ
اكتب تعداد البلد [هيكل] {// يمكن أن يمتد التعداد لأنواع أخرى (انظر المثال ب)
النمسا ("AT"، "Austria"، false) // سيكون متاحًا مثل كونست ، ولكن مع كتابة كـ
ألمانيا ("DE"، "Germany"، true) // بادئة (مثل Country.Austria)

//The struct will automatically begin when it doesn't match the format EnumName(...) anymore
Code, CountryName string
HasMerkel bool //Totally awesome

}

// يمكن أن يكون لدى Enums طرق مثل أي نوع آخر
اختبار func (c Country) () {}

func main () {
println (Country.Austria.CountryName) // النمسا
println (Country.Germany.Code) // DE

/* Prints:
Austria
0
Germany
1
 */
for name, country := range Country {
    println(name) //Austria
    println(name == country.Name()) //true ; also autogenerated 
    println(country.Index()) //Auto generated increasing index
}

}

// مثال ب
اكتب Coolness enum [int] {
فيريكول (10)
كوول (5)
نوت كول (0)
} ``

sinnlosername أعتقد أن التعدادات يجب أن تكون شيئًا يسهل فهمه. قد لا يؤدي الجمع بين بعض الأفكار المقدمة في المناقشة السابقة بالضرورة إلى أفضل فكرة للتعداد.

أعتقد أنه من السهل فهم ما يلي:

إعلان

type Day enum {
    Monday
    Tuesday
    ...
    Sunday
}

تحويل السلسلة (باستخدام واجهة Stringer ):

func (d Day) String() string {
    switch d {
    case Monday:
        return "mon"
    case Tuesday:
        return "tues"
    ...
    case Sunday:
        return "sun"
    }
}

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

مثال على الاستخدام

func IsWeekday(d Day) bool {
    return d != Saturday && d != Sunday
}

إذا كنت سأستخدم ثوابت string هنا لتمثيل Day ، IsWeekday ستقول أن أي سلسلة ليست "sat" أو "sun" هو يوم من أيام الأسبوع (على سبيل المثال ، ما الذي يمكن / يجب أن يعود به IsWeekday("abc") ؟). على النقيض من ذلك ، فإن مجال الوظيفة الموضحة أعلاه مقيد ، مما يسمح للوظيفة بأن تكون أكثر منطقية فيما يتعلق بمدخلاتها.

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

ربما يجب أن يكون

func IsWeekday(d Day) bool {
    return d != Day.Saturday && d != Day.Sunday
}

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

نحن في 14. مايو 2018 وما زلنا نناقش حول دعم التعداد. أعني، ماذا بحق الجحيم؟ أنا شخصياً أشعر بخيبة أمل من جولانج.

أتفهم أنه يمكن أن يكون محبطًا أثناء انتظار ميزة. لكن نشر تعليقات غير بناءة مثل هذا لا يساعد. يرجى الحفاظ على تعليقاتك محترمة. انظر https://golang.org/conduct.

شكرا.

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

لقد تخليت عن انتظار فريق جولانج لتحسين اللغة بطريقة ضرورية.

  • كلمة "ضروري" لا تعني "ما أريد".

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

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

في الواقع الميزات الأساسية لكل لغة حديثة ضرورية.

بالتأكيد. هذه الميزات الأساسية تسمى تورينج اكتمال. _كل شيء_ آخر هو اختيار تصميم. هناك مسافة كبيرة بين Turing Compleness و C ++ (على سبيل المثال) وفي هذه المساحة يمكنك العثور على الكثير من اللغات. يشير التوزيع إلى عدم وجود أفضل عالمي.

تتمتع GoLang ببعض الميزات الجيدة ، لكنها لن تستمر إذا ظل المشروع متحفظًا.

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

راجع للشغل ، إذا تخيلت ما سيحدث اليوم إذا تم اعتماد 10 ٪ من الميزات التي يطلبها الناس ، فمن المحتمل أن أستخدم الآن Go لا أكثر.

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

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

ولماذا تعتقد أن هذا التحفظ هو سبب نمو golang؟ أعتقد أن هذا مرتبط على الأرجح بكفاءة golang والمجموعة الكبيرة من المكتبات القياسية.

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

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

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

حتى الآن لا يزال ينمو. IMO ، لن يستمر في النمو إذا لم يكن متحفظًا

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

إذا نظرنا إلى C # و Typescript أو حتى Rust / Swift. إنهم يضيفون ميزات جديدة مثل الجنون. لا يزال C # في اللغات العليا يتأرجح صعودًا وهبوطًا. المطبوع ينمو بسرعة كبيرة. نفس الشيء بالنسبة لصدأ / سويفت. من ناحية أخرى ، انفجرت شعبية Go في عامي 2009 و 2016. ولكن بين ذلك لم يكن ينمو على الإطلاق ويخسر بالفعل. ليس لدى Go ما يقدمه للمطورين الجدد إذا كانوا يعرفون ذلك بالفعل ولم يختاروه مسبقًا لسبب ما. بالضبط لأن Go راكد في تصميمها. تضيف اللغات الأخرى ميزة ليس لأنه ليس لديها أي شيء آخر تفعله ولكن لأن قاعدة المستخدمين الفعلية تتطلب ذلك. يحتاج الأشخاص إلى ميزات جديدة حتى تظل قاعدة التعليمات البرمجية الخاصة بهم ذات صلة بمجالات المشكلات المتغيرة باستمرار. مثل غير متزامن / انتظار. كانت هناك حاجة لحل مشكلة حقيقية. ليس من المستغرب أن تتمكن من رؤيتها بالعديد من اللغات الآن.

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

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

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

إن إضافة ميزات اللغة لن يغير شيئًا بالنسبة لي في هذا الصدد.

هل انت متاكد من ذلك؟ أنظر فوق.


راجع للشغل ، هل رأيت نتائج مسح 2017 ؟

إذا كانت اللغة تكسر شيئًا / كل شيء كل [نصف] عام أو نحو ذلك

ثم لا تكسر أي شيء. أضاف C # عددًا كبيرًا من الميزات ولم ينتهك التوافق مع الإصدارات السابقة. هذا ليس خيارًا لهم أيضًا. نفس الشيء بالنسبة لـ C ++ على ما أعتقد. إذا لم يتمكن Go من إضافة ميزة دون كسر شيء ما ، فهذه مشكلة في Go وربما كيفية تنفيذها.

راجع للشغل ، هل رأيت نتائج مسح 2017؟

يعتمد تعليقي على استطلاعات الرأي لعام 2017/2018 ، ومؤشر TIOBE وملاحظاتي العامة حول ما يحدث بلغات مختلفة.

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

ثم لا تكسر أي شيء.

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

أضاف C # عددًا كبيرًا من الميزات ولم ينتهك التوافق مع الإصدارات السابقة.

الرجاء التحقق من الحقائق الخاصة بك: Visual C # 2010 Breaking Changes . (أول نتيجة بحث على الويب ، لا يمكنني التخمين إلا إذا كانت هي المثال الوحيد أم لا.)

يعتمد تعليقي على استطلاعات الرأي لعام 2017/2018 ، ومؤشر TIOBE وملاحظاتي العامة حول ما يحدث بلغات مختلفة.

حسنًا ، كيف يمكنك أن ترى بعد ذلك أن اللغة لا تنمو بينما تظهر نتائج الاستطلاع نموًا بنسبة 70٪ عامًا بعد عام لعدد المستجيبين؟

كيف تعرف "كسر التغييرات"؟ سيظل كل سطر من كود go يعمل بعد إضافة التعدادات أو الأدوية العامة.

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

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

كيف تعرف "كسر التغييرات"؟ سيظل كل سطر من كود go يعمل بعد إضافة التعدادات أو الأدوية العامة.

بالطبع لا. لن يتم تجميع هذا الرمز مع هذا الاقتراح:

package foo

var enum = 42

كلمة "ضروري" لا تعني "ما أريد".

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

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

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

كلمة "ضروري" لا تعني "ما أريد".

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

لا يلزم تغيير واجهة برمجة التطبيقات ، فقد تستخدم أجزاء API الجديدة فقط الأدوية الجنيسة ، ولكن ربما توجد دائمًا بدائل بدون أدوية جنيسة في الإنترنت.

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

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

سيكون الجميع سعداء إذا استطاع شخص ما أن يأتي بتصميم وتنفيذ يتمتع بمثل هذا الأداء الرائع. الرجاء المساهمة في # 15292.

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

هل لديك أي أرقام لفرق السرعة مع الأدوية الجنيسة؟

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

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

هل لديك أي أرقام لفرق السرعة مع الأدوية الجنيسة؟

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

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

هذا مختلط. كان التباطؤ حول الأدوية الجنيسة. كان "enum" متعلقًا بالتوافق مع الإصدارات السابقة وسيظل "_Every_ سطر كود الانتقال الخاطئ يعمل بعد إضافة التعدادات أو العوامل العامة". مطالبة. (يؤكد لي)

Merovius أنت راسخ ، أنا الآن أصمت.

وبإعادة هذا إلى أنواع التعداد ، وهو ما يدور حوله هذا الموضوع ، أفهم تمامًا الحجة التي تفسر سبب احتياج Go إلى أدوية عامة ، لكنني أكثر اهتزازًا بشأن الحجة حول سبب احتياج Go إلى أنواع التعداد. في الواقع ، لقد سألت هذا أعلاه في https://github.com/golang/go/issues/19814#issuecomment -290878151 وما زلت مهتزة بشأنه. إذا كان هناك إجابة جيدة لذلك ، فقد فاتني. هل يمكن لشخص أن يكررها أو يشير إليها؟ شكرا.

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

ianlancetaylor : كل شيء له علاقة بسلامة النوع. تستخدم أنواعًا لتقليل مخاطر أخطاء وقت التشغيل بسبب خطأ إملائي أو استخدام أنواع غير متوافقة. في الوقت الحالي يمكنك الكتابة في go

if enumReference == 1

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

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

يجب أن تكون قادرًا على الكتابة فقط

if enumReference == enumType

لا تحتاج إلى الكثير من الخيال لتتخيل أي سيناريوهات يمكن أن تحدث بطريقة أكثر if enumReference == 1 وتؤدي إلى مشاكل إضافية لن تراها إلا في وقت التشغيل.

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

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

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

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

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

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

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

هل يجب أن تكون قيم التعداد متسلسلة ، أم أنها تأخذ أي قيمة كما في C ++؟

في Go ، الثوابت غير مطبوعة ، لذلك إذا سمحنا بالتحويلات من الأعداد الصحيحة إلى نوع التعداد ، فسيكون من الغريب منع if enumVal == 1 . لكن أعتقد أننا نستطيع.

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

سأكرر نفسي ولكني أؤيد الاحتفاظ بالتعدادات كما هي اليوم وإضافة ميزات فوقها:

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

ما يقدمه علاوة على ذلك هو:

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

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


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

ianlancetaylor أعتقد أن حجتك بها بعض العيوب.

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

التجريد للمبرمج جيد ؛ يوفر Go العديد من التجريدات. على سبيل المثال ، لا يتم ترجمة الكود التالي:

package main

import "fmt"

const NULL = 0x0

func main() {
    str := "hello"
    if &str == NULL {
        fmt.Println("str is null")
    }
}

ولكن في C ، سيتم تجميع برنامج من هذا النمط. هذا لأن Go مكتوب بشدة و C ليس كذلك.

قد يتم تخزين فهارس التعداد داخليًا ولكن يتم إخفاؤها عن المستخدم كتجريد ، على غرار عناوين المتغيرات.

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

ianlancetaylor هذه نسخة C جدًا من التعدادات التي تتحدث عنها. أنا متأكد من أن الكثير من الناس يرغبون في ذلك ولكن ، imo:

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

سيقوم التطبيق بتجميع هذه القيم وصولاً إلى الأعداد الصحيحة ولكن هذا ليس شيئًا أساسيًا لأي سبب مشروع ليتم كشفه مباشرة للمبرمج ، إلا عن طريق غير آمن أو انعكاس. للسبب نفسه ، لا يمكنك فعل bool(0) للحصول على false .

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

إذا كنت ترسل القيمة خارج العملية ، فإن int تكون جيدة إذا كنت تتبع معيارًا محددًا جيدًا أو كنت تعلم أنك تتحدث إلى مثيل آخر من برنامجك تم تجميعه من المصدر أو كنت بحاجة إلى عمل أشياء تتلاءم مع أصغر مساحة ممكنة حتى لو تسبب ذلك في حدوث مشكلات ، ولكن لا يتم تعليق أي من هذه بشكل عام ومن الأفضل استخدام تمثيل سلسلة حتى لا تتأثر القيمة بترتيب المصدر لتعريف النوع. لا تريد أن تصبح عملية A's Green عملية B لأن شخصًا آخر قرر إضافة اللون الأزرق قبل Green للحفاظ على الأشياء أبجديًا في التعريف: أنت تريد unrecognized color "Blue" .

إنها طريقة جيدة وآمنة لتمثيل عدد من الدول بشكل تجريدي. إنه يترك البرنامج لتحديد ما تعنيه تلك الدول.

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

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

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

هناك الكثير من المقترحات هنا.

تعليق عام: بالنسبة لأي اقتراح لا يكشف عن قيمة أساسية (مثل int) يمكنك التحويل منه وإليه للتعداد ، فإليك بعض الأسئلة للإجابة عليها.

ما هي القيمة الصفرية لنوع التعداد؟

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

(أيضًا ، بخصوص "التشديد" ، فإن السلسلة الصحيحة ليوم من أيام الأسبوع تعتمد على اللغة والإعدادات المحلية.)

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

على سبيل المثال ، تحتوي بعض أنواع C # على تجاوز ToString والذي يأخذ أيضًا معلومات الثقافة. أو يمكنك استخدام كائن DateTime نفسه واستخدام طريقة ToString التي تقبل معلومات التنسيق والثقافة. لكن هذه التجاوزات ليست قياسية ، object التي يرثها الجميع لديها ToString() فقط. يشبه إلى حد كبير واجهة stringer في Go.

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

josharian بما أنه ، من ناحية التنفيذ ، سيظل عدد صحيح وقيم الصفر كلها بتات صفرية ، فإن القيمة الصفرية ستكون هي القيمة الأولى في ترتيب المصدر. هذا نوع من تسريب int-iness ولكنه في الواقع لطيف جدًا لأنه يمكنك اختيار القيمة الصفرية ، وتحديد ما إذا كان الأسبوع يبدأ يوم الاثنين أو الأحد ، على سبيل المثال. بالطبع ، من غير الجميل ألا يكون لترتيب المصطلحات المتبقية مثل هذا التأثير وأن إعادة ترتيب القيم يمكن أن يكون لها تأثيرات غير تافهة إذا قمت بتغيير العنصر الأول. هذا لا يختلف حقًا عن const / iota ، على الرغم من ذلك.

رد ما قاله creker . للتوسع ، على الرغم من ذلك ، أتوقع

var e enum {
  Sunday
  Monday
  //etc.
}
fmt.Println(reflect.ValueOf(e))

لطباعة الأحد وليس 0. الملصق هو القيمة وليس تمثيلها.

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

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

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

ليس من المنطقي بالنسبة لي أن أفترض أن جميع الأعداد لها ترتيب كلي أو أنها دورية. بالنظر إلى type failure enum { none; input; file; network } ، هل من المنطقي حقًا فرض أن الإدخال غير الصالح أقل من فشل الملف أو أن زيادة فشل الملف تؤدي إلى فشل الشبكة أو أن زيادة فشل الشبكة تؤدي إلى النجاح؟

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

type Weekday uint%7
const (
  Sunday Weekday = iota
  //etc.

حتى السبت + 1 == الأحد وأيام الأسبوع (456) == الاثنين. من المستحيل إنشاء يوم من أيام الأسبوع غير صالح. يمكن أن يكون مفيدًا خارج const / iota ، على الرغم من ذلك.

لأنه عندما لا تريد أن يكون رقم y على الإطلاق ، كما أوضحتianlancetaylor أن ما أريده حقًا هو أنواع المجموع.

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

var Weekdays = [...]string{"Sunday", ..., "Saturday"}

type Weekday = uint % len(Weekdays)

بالاقتران مع ints ذات الحجم التعسفي ، فإن هذا يمنحك أيضًا int128 و int256 وما إلى ذلك.

يمكنك أيضًا تحديد بعض العناصر المضمنة:

type uint8 = uint%(1<<8)
// etc

يمكن للمترجم أن يثبت حدودًا أكثر من ذي قبل. ويمكن لواجهات برمجة التطبيقات تقديم تأكيدات أكثر دقة عبر الأنواع ، على سبيل المثال ، يمكن للوظيفة Len64 في math/bits إرجاع uint % 64 الآن.

عند العمل على منفذ RISC-V ، أردت نوع uint12 ، نظرًا لأن مكونات ترميز التعليمات الخاصة بي هي 12 بت ؛ كان من الممكن أن يكون uint % (1<<12) . يمكن أن تستفيد من هذا الكثير من معالجة البتات ، وخاصة البروتوكولات.

الجوانب السلبية مهمة بالطبع. يميل Go إلى تفضيل الكود على الأنواع ، وهذا النوع ثقيل. يمكن أن تصبح العمليات مثل + و - فجأة مكلفة مثل٪. بدون معلمات نوع من نوع ما ، ربما يتعين عليك التحويل إلى المتعارف عليه uint8 ، uint16 ، وما إلى ذلك من أجل التعامل مع أي وظيفة مكتبة تقريبًا ، ويمكن أن يخفي التحويل الخلفي الحدود الفشل (ما لم يكن لدينا طريقة للقيام بتحويل الذعر خارج النطاق ، والذي يقدم تعقيده الخاص). ويمكنني أن أرى أنه يتم الإفراط في استخدامه ، على سبيل المثال استخدام uint % 1000 لرموز حالة HTTP.

فكرة مثيرة للاهتمام بالرغم من ذلك. :)


ردود ثانوية أخرى:

هذا نوع من تسريب int-iness

هذا يجعلني أعتقد أنهم فعلا إنتس. :)

يمكن بسهولة ملء الأنماط الشائعة بـ go إنشاء

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

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

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

رد ما قاله creker .

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

josharian Enums التي هي حقًا ints ربما تحتاج إلى آلية مماثلة على الرغم من ذلك. وإلا ، ما هو enum { A; B; C}(42) ؟

يمكنك القول أنه خطأ مترجم ولكن هذا لا يعمل في كود أكثر تعقيدًا حيث يمكنك التحويل من وإلى ints في وقت التشغيل.

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

الخيار الآخر هو ترك أي شيء من A أو B أو C ، ولكن هذا ما لدينا اليوم مع const / iota لذلك لا توجد مكاسب.

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

إن التخلص من العزيمة يزيل هذا اللغز. يتطلب إنشاء رمز للحالات التي تريد فيها إضافة بعض من تلك int-iness مرة أخرى ، ولكنه يمنحك أيضًا خيار عدم القيام بذلك ، مما يتيح لك التحكم في مقدار ما يجب تقديمه ونوعه: يمكنك إضافة "لا" " التالي "، أو الطريقة الدورية التالية ، أو الطريقة التالية التي تُرجع خطأً إذا سقطت عن الحافة. (أنت أيضًا لا ينتهي بك الأمر مع أشياء مثل Monday*Sunday - Thursday كونها قانونية). الصلابة الزائدة تجعلها مادة بناء أكثر مرونة. يقوم الاتحاد المميّز بنمذجة جيدة للتنوع غير الداخلي: pick { A, B, C struct{} } ، من بين أشياء أخرى.

الفوائد الرئيسية للحصول على معلومات مثل هذه في اللغة هي ذلك

  1. القيم غير القانونية غير قانونية.
  2. المعلومات متاحة للانعكاس والانتقال / الأنواع التي تسمح للبرامج بالتصرف وفقًا لها دون الحاجة إلى وضع افتراضات أو تعليقات توضيحية (وهي غير متاحة للتعبير عنها حاليًا).

الفوائد الرئيسية للحصول على معلومات مثل هذه باللغة هي: القيم غير القانونية غير قانونية.

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

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

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

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

القيم غير القانونية غير قانونية.
أعتقد أنه من المهم التأكيد على أنه ليس كل شخص يرى هذا على أنه فائدة.

أنا لا أفهم هذا المنطق. الأنواع هي مجموعات من القيم. لا يمكنك تعيين نوع إلى متغير قيمته ليست في ذلك النوع. هل أنا أسيء فهم شيء ما؟

ملاحظة: أوافق على أن التعدادات هي حالة خاصة لأنواع المجموع وأن هذه المسألة يجب أن تكون لها الأسبقية على هذه المسألة.

اسمحوا لي أن أعيد الصياغة / أكون أكثر دقة: لا يرى الجميع أنه من المفيد إغلاق التعدادات.

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

إذا كانت enums عبارة عن ints وكان أي int قانونيًا (من وجهة نظر نظام الكتابة) ، فإن الكسب الوحيد هو أن القيم المسماة من النوع في انعكاس.

هذا في الأساس مجرد const / iota ولكن ليس عليك تشغيل stringer لأن حزمة fmt يمكن أن تحصل على الأسماء باستخدام الانعكاس. (لا يزال يتعين عليك تشغيل stringer إذا كنت تريد أن تكون السلاسل مختلفة عن الأسماء الموجودة في المصدر).

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

على سبيل المثال ، نظرا لشيء مثل هذا

type Foo enum {
    Val1 = 1
    Val2 = 2
}

وطريقة التفكير مثل

func IsValidEnum(v {}interface) bool

يمكننا القيام بشيء مثل هذا

a := Foo.Val1
b := Foo(-1)
reflection.IsValidEnum(a) //returns true
reflection.IsValidEnum(b)  //returns false

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

crecker الفرق الوحيد بين ذلك و const / iota هو
المعلومات المخزنة في انعكاس. هذا ليس مكسبًا كبيرًا لكامل
نوع جديد.

فكرة مجنونة بعض الشيء:

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

الميزة الرئيسية بالنسبة لي ، كما يمكنك أن تقرأ في اقتراحي أعلاه

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

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

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

ما عليك سوى إلقاء نظرة على ما يجب أن يولده protobuf لجافا من أجل التغلب على تعدادات Java.

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

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

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

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

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

type Day struct {
    value string
}

// optional, if you need string representation
func (d Day) String() string { return d.value }

var (
    Monday = Day{"Monday"}
    Tuesday = Day{"Tuesday"}
)

func main() {
    getTask(Monday)
}

func getTask(d Day) string {
    if d == Monday {
        fmt.Println("today is ", d, "!”) // today is Monday !
        return "running"
    }

    return "nothing to do"
}

مزايا :

العيوب :

هل نحن حقا بحاجة الى تعدادات؟

ما الذي يمنع شخصًا ما من فعل شيء كهذا:

NotADay := Day{"NotADay"}
getTask(NotADay)

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

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

./day/day.go

package day

type Day struct {
    value string
}

func (d Day) String() string { return d.value }

var (
    Monday  = Day{"Monday"}
    Tuesday = Day{"Tuesday"}
    Days    = []Day{Monday, Tuesday}
)

./main.go

package main

import (
    "fmt"
    "github.com/somePath/day"
)

func main() {
    january := day.Day{"january"} // implicit assignment of unexported field 'value' in day.Day literal

    var march struct {
        value string
    }
    march.value = "march"
    getTask(march) // cannot use march (type struct { value string }) as type day.Day in argument to getTask

    getTask(day.Monday)
}

func getTask(d day.Day) string {
    if d == day.Monday {
        fmt.Println("today is ", d, "!") // today is Monday !
        return "running"
    }

    return "nothing to do"
}

func iterateDays() {
    for _, d := range day.Days {
        fmt.Println(d)
    }
}

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

هل Golang تجربة اجتماعية لمعرفة مدى غباء المطورين؟

@ gh67uyyghj قام شخص ما بوضع علامة على تعليقك على أنه خارج الموضوع! وأعتقد أن شخصًا ما سيفعل الشيء نفسه في ردي. لكني أعتقد أن الإجابة على سؤالك هي نعم. في GoLang ، كونك غير مميز يعني أن تكون مميزًا ، لذا فإن أي شيء لا تمتلكه GoLang هو في الواقع ميزة لا تمتلكها GoLang ولا تمتلكها لغات البرمجة الأخرى !!

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

  • اكتب
  • اقرأ
  • السبب بخصوص

في مثالك (وهو أمر رائع لأنه يعمل اليوم) وجود تعدادات (بشكل ما) يدل على الحاجة إلى:

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

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

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

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

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

العودة إلى نقاطك:

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

// Day Enum
type Day struct {
    value string
}

@ L- أوريس أرى. أنا متحمس أيضًا لمقترحات Go 2. كنت أزعم أن الأدوية الجنيسة ستزيد من تعقيد اللغة أكثر من التعداد. لكن لكي تتمسك بنقاطك:

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

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

يا رفاق ، يريدون حقًا هذه الميزة للمستقبل !. المؤشرات وطريقة تعريف " التعدادات " في _الأيام_ لا تتوافق جيدًا. على سبيل المثال: https://play.golang.org/p/A7rjgAMjfCx

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

package application

type Status struct {
Name string
isFinal bool
}

enum Status {
     Started = &Status{"Started",false}
     Stopped = &Status{"Stopped",true}
     Canceled = &Status{"Canceled",true}
}

// application.Status.Start - to use

من المفهوم كيفية تنظيم هذا الهيكل وكيفية العمل وكيفية التغيير إلى الخيط وما إلى ذلك.
وبالطبع إذا كان بإمكاني تجاوز وظيفة "التالي" ستكون رائعة.

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

type Status enum {
  Started
  Stopped
}

func isFinal(s Status) bool {
  exhaustive switch(s) {
    case Started: return false;
    case Stopped: return true;
  }
}

أعتقد أنه يجب أن يبدو أبسط

func isFinal(s Status) bool {
  return s == Status.Stopped
}

مقترح الهدف 2

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

enum Status uint8 {
  Started  // Status.Started == 0
  Stopped // Status.Stopped == 1, etc, like we have used iota
}
// or 
enum Status string  {
  Started // Status.Started == "Started", like it works with JSON
  Stopped // Status.Stopped == "Stopped", etc
}
// unless you wanna define its values explicitly
enum Status {
  Started "started"  // compiler can infer underlying type
  Stopped "finished"
}
// and enums are type extensions and should be used like this
type MyStatus Status

MyStatus validatedStatus // holds a nil until initialized

// for status value validation we can use map pattern
if validatedStatus, ok := MyStatus[s]; ok {
  // this value is a valid status
  // and we can use it later as regular read-only string
  // or like this
  if validatedStatus == MyStatus.Started {
     fmt.Printf("Hey, my status is %s", validatedStatus)
  }
}

Enums هي ملحقات النوع ، "حاويات الثوابت".

لمحبي النوع

بدائل لغوية لمن يريد أن يراها كنوع

type Status uint8 enum {
  Started  // Status.Started == 0
  Stopped // Status.Stopped == 1, etc, like we have used iota
}

لكن يمكننا أيضًا تجنب تصريحات المستوى الأعلى الصريحة

type Status enum {
  Started  // Status.Started == 0
  Stopped // Status.Stopped == 1, etc, like we have used iota
}

يبقى مثال التحقق كما هو.

لكن في حالة

type Status1 uint8 enum {
  Started  // Status1.Started == 0
  Stopped // Status1.Stopped == 1, etc, like we have used iota
}

type Status2 uint8 enum {
  Started  // Status1.Started == 0
  Stopped // Status1.Stopped == 1, etc, like we have used iota
}

ماذا عن Status1.Started == Status2.Started؟
حول التنظيم؟

إذا قمت بتغيير الموقف؟

type Status uint8 enum {
  Started  // Status.Started == 0
  InProcess
  Stopped // Status.Stopped == 1, etc, like we have used iota
}

أتفق مع Goodwine حول الأنواع غير القابلة للتغيير.

التنظيم هو سؤال مثير للاهتمام.
كل هذا يتوقف على كيفية تعاملنا مع القيمة الأساسية. إذا كنا سنستخدم القيم الفعلية ، فإن Status1.Started سيكون مساويًا لـ Status2.Started .
إذا كنا نتبع تفسيرًا رمزيًا ، فسيتم اعتبار هذه القيم مختلفة.

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

type Status uint8 enum {
  Started  0
  InProcess 2
  Stopped 1
}

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

أحب الطريقة التي يتم بها تطبيق Rust Enums.

افتراضي بدون تحديد نوع

enum IpAddr {
    V4,
    V6,
}

نوع مخصص

enum IpAddr {
    V4(string),
    V6(string),
}

home := IpAddr.V4("127.0.0.1");
loopback := IpAddr.V6("::1");

أنواع معقدة

enum Message {
    Quit,
    Move { x: int32, y: int32 },
    Write(String),
    ChangeColor(int32, int32, int32),
}

من المؤكد أن وجود تعدادات بسيطة مثل C # والتي يتم تخزينها كأنواع متكاملة سيكون أمرًا رائعًا.

ما سبق يتجاوز enum s ، فهذه نقابات مميّزة ، وهي بالفعل أكثر قوة ، خاصة مع مطابقة النمط ، والتي يمكن أن تكون امتدادًا ثانويًا لـ switch ، شيء مثل:

switch something.(type) {
case Quit:
        ...
case ChangeColor; r, g, b := something:
        ...
case Write: // Here `something` is known to be a string
        ...
// Ideally Go would warn here about the missing case for "Move"
}

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

ما احتجته عدة مرات هو تكرار جميع الثوابت من نوع معين:

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

    • أو للحصول على قائمة الثوابت المحتملة (فكر في القوائم المنسدلة).

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

سيكون من الإضافات اللطيفة تحديد الأسماء المشددة لها.

ما الذي يمنع شخصًا ما من فعل شيء كهذا:

NotADay := Day{"NotADay"}
getTask(NotADay)

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

@ L-oris فماذا عن هذا:

package main
import "yet/it/is/not/a/good/practice/in/Go/enum/example/day"

func main()
{
  // var foo day.Day
  foo := day.Day{}
  bar(foo)
}

func bar(day day.Day)
{
  // xxxxxxxxxx
}

ما نريده ليس سبب الصمت والخطأ الغريب من خلال [إرجاع "لا شيء لفعله"] ولكن تقرير خطأ وقت التجميع / وقت التشفير !
تفهم؟

  1. enum هو نوع جديد بالفعل ، وهو ما يفعله type State string ، ولا توجد حاجة اصطلاحية لتقديم كلمة رئيسية جديدة. لا يعني Go توفير مساحة في شفرة المصدر الخاصة بك ، بل يتعلق بقابلية القراءة ووضوح الغرض.

  2. يعد الافتقار إلى أمان النوع ، مما يؤدي إلى الخلط بين الأنواع الجديدة المستندة إلى string - أو int للسلاسل الفعلية / ints هي العقبة الرئيسية. يتم التصريح عن كل عبارات التعداد على أنها const ، مما يؤدي إلى إنشاء مجموعة من القيم المعروفة التي يمكن للمترجم التحقق منها.

  3. واجهة Stringer هي المصطلح الذي يستخدم لتمثيل أي نوع كنص يمكن للبشر قراءته. بدون التخصيص ، تعداد type ContextKey string هذه هي قيمة السلسلة ، وبالنسبة إلى iota التي تم إنشاؤها ، فهي العدد الصحيح ، مثل أكواد XHR ReadyState (0 - غير مرسلة ، 4 - تم) في JavaScript.

    بدلاً من ذلك ، تكمن المشكلة في قابلية الخطأ لتطبيق func (k ContextKey) String() string المخصص ، والذي يتم عادةً باستخدام رمز التبديل الذي يجب أن يحتوي على كل ثابت جملة تعداد معروف.

  4. في لغة مثل Swift ، هناك فكرة عن مفتاح شامل. هذه طريقة جيدة لكل من التحقق من النوع مقابل مجموعة من const s وبناء طريقة اصطلاحية لاستدعاء هذا الشيك. تعتبر الوظيفة String() ، ضرورة شائعة ، حالة رائعة للتنفيذ.

عرض

package main

import (
    "context"
    "strconv"
    "fmt"
    "os"
)

// State is an enum of known system states.
type DeepThoughtState int

// One of known system states.
const (
    Unknown DeepThoughtState = iota
    Init
    Working
    Paused
    ShutDown
)

// String returns a human-readable description of the State.
//
// It switches over const State values and if called on
// variable of type State it will fall through to a default
// system representation of State as a string (string of integer
// will be just digits).
func (s DeepThoughtState) String() string {
    // NEW: Switch only over const values for State
    switch s.(const) {
    case Unknown:
        return fmt.Printf("%d - the state of the system is not yet known", Unknown)
    case Init:
        return fmt.Printf("%d - the system is initializing", Init)
    } // ERR: const switch must be exhaustive; add all cases or `default` clause

    // ERR: no return at the end of the function (switch is not exhaustive)
}

// RegisterState allows changing the state
func RegisterState(ctx context.Context, state string) (interface{}, error) {
    next, err := strconv.ParseInt(state, 10, 32)
    if err != nil {
        return nil, err
    }
    nextState := DeepThoughtState(next)

    fmt.Printf("RegisterState=%s\n", nextState) // naive logging

        // NEW: Check dynamically if variable is a known constant
    if st, ok := nextState.(const); ok {
        // TODO: Persist new state
        return st, nil
    } else {
        return nil, fmt.Errorf("unknown state %d, new state must be one of known integers", nextState)
    }
}

func main() {
    _, err := RegisterState(context.Background(), "42")
    if err != nil {
        fmt.Println("error", err)
        os.Exit(1)
    }
    os.Exit(0)
    return
}

تعد القيم المرتبطة بـ PS في تعدادات Swift إحدى الحيل المفضلة لدي. في Go لا يوجد مكان لهم. إذا كنت تريد الحصول على قيمة بجوار بيانات التعداد الخاصة بك - فاستخدم struct المكتوب بقوة في الإثنين.

قبل بضعة أشهر ، كتبت إثباتًا لمفهوم لنتر يتحقق من أن الأنواع التي تم تعدادها يتم التعامل معها بشكل صحيح. https://github.com/loov/enumcheck

يستخدم حاليًا التعليقات لتمييز الأشياء كتعدادات:

type Letter byte // enumcheck

const (
    Alpha Letter = iota
    Beta
    Gamma
)

func Switch(x Letter) {
    switch x { // error: "missing cases Beta and Gamma"
    case Alpha:
        fmt.Println("alpha")
    case 4: // error: "implicit conversion of 4 to Letter"
        fmt.Println("beta")
    default: // error: "Letter shouldn't have a default case"
        fmt.Println("default")
    }
}

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

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

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

@ pofl :
بينما أوافق على أن Go enums محرجة جدًا ، فإن iota هو في الواقع مجرد كلمة إنجليزية عادية:

ذرة
_اسم_

  1. كمية صغيرة جدا مثقال ذرة؛ مثقال ذرة.
  2. الحرف التاسع من الأبجدية اليونانية (I ، ι).
  3. حرف العلة الذي يمثله هذا الحرف.

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

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

يعتمد استخدام iota in Go بشكل فضفاض على استخدامه في APL. نقلاً عن https://en.wikipedia.org/wiki/Iota :

في بعض لغات البرمجة (على سبيل المثال ، A + ، APL ، C ++ [6] ، Go [7]) ، يتم استخدام iota (إما كرمز صغير ⍳ أو معرف iota) لتمثيل وإنشاء مصفوفة من الأعداد الصحيحة المتتالية. على سبيل المثال ، في APL ⍳4 نحصل على 1 2 3 4.

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