استخدم العدسات لتحديث السجلات المتداخلة (خاص باللغات التي تتضمن بيانات غير قابلة للتغيير).
تحديث حقول السجلات المتداخلة غير القابلة للتغيير أمر مزعج نوعًا ما. الكود في مثل هذه الحالات يكون مرهقًا بقدر ما يكون الهيكل عميقًا. إذا كان لديك، على سبيل المثال، شخص يحتوي على عنوان، والعنوان له شارع، والشارع له رقم، فإن تحديث الرقم يتطلب إنشاء شارع جديد بالرقم الجديد، ثم عنوان جديد بالشارع الجديد، وأخيرًا شخص جديد بالعنوان الجديد. هل شعرت بالارتباك بالفعل؟
أحد الحلول لهذه المشكلة هو استخدام العدسات.
نفّذ عدة دوال للوصول إلى السجلات باستخدام العدسات. كما تتيح لك مجموعة الاختبارات تجنّب العدسات تمامًا، حتى تجرّب أساليب مختلفة.
لا تُستخدم العدسات كثيرًا في Roc لأن صيغة تحديث السجلات تغطي معظم حالات الاستخدام بشكل جيد. على سبيل المثال، انظر إلى هذا السجل المتداخل:
bob = { hair: { color: Brown, length: Long }, mouth: Smiling, height: 6.0 }
والآن لنمنح بوب قصة شعر جديدة:
new_bob = { ..bob, hair: { ..bob.hair, length: Short } }
لا حاجة للعدسات! ومع ذلك، ورغم أن العدسات نادرة في Roc، فهي شائعة في لغات أخرى مثل Haskell وScala، لذلك قد تكون فضوليًا لمعرفة كيف يمكن تنفيذها في Roc. كما أنها وسيلة جيدة للتدرّب على بعض ميزات Roc المتقدمة مثل الأنواع ذات المعاملات، والسجلات التي تحتوي على دوال. في الواقع، إليك كيفية تمثيل عدسة في Roc:
Lens(whole, part) := {
get : whole -> part,
set : whole, part -> whole,
}
هذا نوع ذو معاملات: يمكن أن يكون whole وpart أي نوعين. عادةً ما يكون whole سجلًا، بينما يمكن أن يكون part أي نوع حقل. يحتوي حقل get على دالة تُرجع part محددًا من whole المعطى، بينما يحتوي حقل set على دالة تُرجع نسخة من whole المعطى، مع استبدال part محدد بـ part المعطى.
على سبيل المثال، إليك عدسة للحصول على شعر شخص أو ضبطه:
hair_lens = {
get: |person| person.hair,
set: |person, hair| { ..person, hair },
}
يمكننا استخدام هذه العدسة للتركيز على شعر بوب هكذا:
bob_hair = bob |> hair_lens.get
ويمكننا تغيير شعره هكذا:
new_hair = { ..bob.hair, length: Short }
new_bob = bob |> (hair_lens.set)(new_hair)
ملاحظة: الأقواس الهلالية حول hair_lens.set مطلوبة: فبدونها، سيبحث Roc عن دالة باسم set في نوع hair_lens.
تصبح العدسات مفيدة بشكل خاص عندما تبدأ في تركيبها، مما يتيح لك الحصول على أجزاء متداخلة بعمق وضبطها. يمكن القيام بذلك بتنفيذ دالة مثل هذه:
compose : Lens(outer, inner), Lens(inner, part) -> Lens(outer, part)
بمجرد تنفيذ هذه الدالة، يمكنك استخدامها لإنشاء عدسة طول الشعر هكذا:
length_lens = {
get: |hair| hair.length,
set: |hair, length| { ..hair, length },
}
hair_length_lens = compose(hair_lens, length_lens)
الآن يمكنك منح بوب قصة شعر أخرى:
set_hair_length = hair_length_lens.set
new_bob = bob |> set_hair_length(Short)
قد ترغب أيضًا في تنفيذ دالة تتيح لك تطبيق أي تحويل على جزء متداخل:
over : Lens(whole, part), whole, (part -> part) -> whole
بمجرد تنفيذها، يمكنك استخدام over هكذا:
cut_hair = |current_length| match current_length {
Long => Short
Short => Bald
Bald => Bald
}
new_bob = hair_length_lens |> over(bob, cut_hair)
في هذا التمرين، ستتحقق الاختبارات من أن الكود الخاص بك قادر على تعديل أجزاء متداخلة متنوعة من السجل المتداخل التالي:
test_person : Person
test_person = {
name: { forenames: "Jane Joanna", surname: "Doe" },
birth: {
born_at: { street: "Longway", house_number: 1024, place: "Springfield", country: "United States" },
born_on: { year: 1984, month: 4, day: 12 },
},
address: { street: "Shortlane", house_number: 2, place: "Fallmeadow", country: "Canada" },
}
نوصي بأن تجتاز الاختبارات أولًا باستخدام صيغة تحديث السجلات في Roc، ثم تجرب استخدام العدسات بدلًا من ذلك.
سجّل في Exercism لتتعلّم وتتقن Roc عبر 125 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.