Расширяемые записи были одной из самых удивительных функций Elm, но начиная с v0.16 добавление и удаление полей больше не доступно . И это ставит меня в неловкое положение.
Рассмотрим пример. Я хочу дать имя случайной вещи t
, и расширяемые записи предоставляют мне идеальный инструмент для этого:
type alias Named t = { t | name: String }
«Хорошо», говорит компилятор. Теперь мне нужен конструктор, то есть функция, которая оснащает вещь с указанным именем:
equip : String -> t -> Named t
equip name thing = { thing | name = name } -- Oops! Type mismatch
Компиляция не удалась, потому что синтаксис { thing | name = ... }
предполагает, что thing
является записью с полем name
, но система типов не может этого гарантировать. Фактически, с помощью Named t
я пытался выразить нечто противоположное: t
должен быть типом записи без собственного поля name
, и функция добавляет это поле в запись. В любом случае, для реализации функции equip
необходимо добавление поля.
Так что, кажется, невозможно написать equip
полиморфно, но, вероятно, это не такая уж большая проблема. В конце концов, когда я собираюсь дать имя какой-то конкретной вещи, я могу сделать это руками. Гораздо хуже, обратная функция extract : Named t -> t
(которая стирает имя именованной вещи) требует механизма удаления поля и, следовательно, также не реализуема:
extract : Named t -> t
extract thing = thing -- Error: No implicit upcast
Это было бы чрезвычайно важной функцией, потому что у меня есть тонны процедур, которые принимают старомодные безымянные вещи, и мне нужен способ использовать их для именованных вещей. Конечно, масштабный рефакторинг этих функций не подходит.
Наконец, после этого длинного вступления, позвольте мне сформулировать мои вопросы:
Предоставляет ли современный Elm некоторую замену старому устаревшему синтаксису добавления / удаления полей?
Если нет, есть ли какая-нибудь встроенная функция, такая как equip
и extract
выше? Для каждого настраиваемого расширяемого типа записи я хотел бы иметь полиморфный анализатор (функция, извлекающая его базовую часть) и полиморфный конструктор (функция, которая объединяет базовую часть с добавкой и создает запись).
Отрицательные ответы для (1) и (2) заставили бы меня реализовать Named t
более традиционным способом:
type Named t = Named String t
В этом случае я не могу уловить цель расширяемых записей. Есть ли положительный вариант использования, сценарий, в котором расширяемые записи играют критическую роль?