Треки
/
Roc
Roc
/
Вправи
/
Людина з лінзами
Людина з лінзами

Людина з лінзами

Середня

Вказівки

Використайте лінзи, щоб оновлювати вкладені записи (це стосується мов із незмінними даними).

Оновлювати поля вкладених незмінних записів доволі дратівливо. Чим глибша структура, тим громіздкіший код для таких випадків. Якщо в нас є, скажімо, особа, яка містить адресу, що має вулицю, а та має номер, то для оновлення номера потрібно створити нову вулицю з новим номером, потім нову адресу з новою вулицею і, нарешті, нову особу з новою адресою. Ми вже заплуталися?

Одне з рішень цієї проблеми - використати лінзи.

Реалізуйте кілька функцій доступу до записів за допомогою лінз. Набір тестів також дозволяє нам взагалі обійтися без лінз, щоб ми могли поекспериментувати з різними підходами.

Лінзи в 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, а потім спробувати лінзи.

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Roc Exercism

Час розпочати Людина з лінзами?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Roc, а також 125 вправ та справжнє наставництво від людей, і все це безкоштовно.