Есть ли способ передать имя поля в функцию установки?

Здесь у меня есть несколько функций, которые просто устанавливают одно поле в записи модели. На более динамичном языке я бы просто использовал одну функцию установки и передал бы ей имя поля (в виде строки) и значение, которое я хочу установить для объекта модели.

Есть ли способ передать имя поля в Elm? Как Elm делает что-то подобное?

type alias Patient =
  { id : String
  , name : String
  , dateOfBirth : String
  , sex : String
  ... other fields
  }

setPatientName : Patient -> String -> Patient
setPatientName patient value =
    { patient | name = value }

setPatientDateOfBirth : Patient -> String -> Patient
setPatientDateOfBirth patient value =
  { patient | dateOfBirth = value }

setPatientSex : Patient -> String -> Patient
setPatientSex patient value =
  { patient | sex = value }

... many others

-- idx is the index of the patient in the model (which is an array of patients)
-- UpdateCell is a variant of my Msg type, like this: UpdateCell Int (Patient -> String -> Patient) String
onInputHandler : Int -> (Patient -> String -> Patient) -> String -> Msg
onInputHandler idx setter inputText =
    UpdateCell idx setter inputText

-- idx is the index of the patient in the model (which is an array of patients)
createTableRow : Int -> Patient -> Html Msg
createTableRow idx patient =
    ...
    , input [ type_ "text", onInput (onInputHandler idx setPatientName), value patient.name ] []
    , input [ type_ "text", onInput (onInputHandler idx setPatientDateOfBirth), value patient.dateOfBirth ] []
    ...

В настоящее время я использую каждую из этих функций в качестве обработчика событий для элементов ввода. Поэтому мне нужна функция, которую я могу использовать для обработки события ввода. В идеале я бы определил только одну функцию и использовал бы ее для всех элементов ввода и передал бы ей поле, которое я хочу обновить в записи пациента.


person A. Sallai    schedule 23.11.2019    source источник


Ответы (1)


Короткий ответ - нет". Но это немного похоже на проблему XY. Непонятно, какую выгоду вы пытаетесь получить, поскольку полное применение такой функции будет длиннее, чем эквивалентное выражение обновления записи:

setField "name" patient value

-- vs

{ patient | name = value }

и как частично применяемая функция лишь немного короче эквивалентной анонимной функции с укороченными именами аргументов:

setField "name"

-- vs

\r x -> { r | name = x }

Хотя последний значительно шумит со всеми символами.

Существует также сокращенная функция для получения поля записи:

.name

-- vs

\r -> r.name

Таким образом, есть некоторый прецедент для использования специального синтаксиса для функций установки, но, к сожалению, его нет. Скорее всего потому, что это усложнило бы язык и, в частности, синтаксис, при относительно небольшой пользе. Поэтому мне любопытно, чего вы на самом деле пытаетесь достичь.

Изменить после обновления вопроса:

Помещать функции в Msg — очень плохая идея, потому что это противоречит архитектуре Elm. Это делает переход состояния непрозрачным и не очень хорошо работает с отладчиком. Когда что-то идет не так, вы по-прежнему можете видеть состояние до и после, но вам будет трудно понять, что произошло и почему это произошло, потому что эта информация закодирована в непрозрачная функция, которая, вероятно, не та, которой должна быть.

У вас также будут проблемы с факторингом вашей логики. Если вам нужно, чтобы что-то происходило только при обновлении определенного поля, вам, возможно, придется поместить логику в представление или выделить это поле в особом случае, поместив логику для этого в update, а остальное, например, в view. В любом случае, вы на пути к беспорядочной кодовой базе.

Как правило, вы должны использовать имена для сообщений, которые описывают, что произошло, а не что делать, потому что это ведет к императивному мышлению. Например, вместо UpdateCell вы можете назвать его InputChanged. Тогда вместо функции у вас должен быть идентификатор поля. В идеале пользовательский тип, например InputChanged Name, но подойдет и строка, хотя пропустить опечатку будет намного проще.

Таким образом, вместо функций установки для каждого поля вы просто сопоставляете сообщение и устанавливаете поле в функции update:

InputChanged Name value ->
    { patient | name = value }

-- vs

setPatientName : Patient -> String -> Patient
setPatientName patient value =
    { patient | name = value }

Затем, если вам нужно очистить пол при изменении имени, например (по причинам...), вы можете просто сделать:

InputChanged Name value ->
    { patient | name = value, sex = "" }

Архитектура Elm хороша тем, что делает изменения легкими и безопасными, а не тем, что она лаконична и свободна от шаблонов. В хорошем коде Elm часто много операций копирования и вставки, но это не всегда плохо.

person glennsl    schedule 23.11.2019
comment
Пожалуйста, смотрите нижнюю часть вопроса, я обновил его. - person A. Sallai; 24.11.2019
comment
Я думаю, что нам нужно больше подробностей и обзоров, чтобы сказать больше конкретики, но то, что вы пытаетесь сделать, звучит как очень плохая идея. Как вы будете обновлять модель? Вы собираетесь поместить функции в Msg? Это запутает переходы и может даже привести к сбою, поскольку функции нельзя сравнивать. Или вы собираетесь поместить всю модель в Msg? Это вызовет проблемы, когда вам в конечном итоге понадобится что-то сделать, когда вы обновите одно поле, но не другие. Поместите логику в update, а не в view. view должен отправлять глупые сообщения только update. - person glennsl; 24.11.2019
comment
Вы также можете поискать библиотеки и примеры, которые имеют дело с проверкой формы, так как они решают многие проблемы, с которыми вы, вероятно, столкнетесь, пытаясь сделать это. - person glennsl; 24.11.2019
comment
Я начинаю соглашаться с проблемой XY ... тем не менее я снова обновил вопрос, чтобы он отражал то, что я пытаюсь сделать. Это неправильно? - person A. Sallai; 24.11.2019
comment
@A.Sallai Обновлен ответ, чтобы более подробно рассмотреть общую картину. - person glennsl; 24.11.2019