有没有一种方法可以将字段名称传递给setter函数? [英] Is there a way to pass the name of a field to a setter function?

查看:59
本文介绍了有没有一种方法可以将字段名称传递给setter函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里,我有几个函数,它们都只在模型记录上设置了一个字段。
在一种更具动态性的语言中,我只有一个设置函数,然后将字段名称(作为字符串)和要在模型对象上设置的值传递给它。



有没有一种方法可以在Elm中传递字段名称?
Elm做这种事情的方式是什么?

  type alias Patient = 
{id:String
,名称:字符串
,dateOfBirth:字符串
,性别:字符串
...其他字段
}

setPatientName:患者- >字符串->患者
setPatientName患者值=
{患者|名称=值}

setPatientDateOfBirth:患者->字符串->患者
setPatientDateOfBirth患者值=
{患者| dateOfBirth = value}

setPatientSex:患者->字符串->患者
setPatientSex患者价值=
{患者|性别=值}

...许多其他

-idx是模型中患者的索引(是一组患者)
- -UpdateCell是我的Msg类型的变体,如下所示:UpdateCell Int(患者->字符串->患者)字符串
onInputHandler:Int-> (患者->字符串->患者)->字符串->消息
onInputHandler idx setter inputText =
UpdateCell idx setter inputText

-idx是模型中患者的索引(是一组患者)
createTableRow:Int->病人-> HTML消息
createTableRow idx Patient =
...
,输入[type_ text,onInput(onInputHandler idx setPatientName),值Patient.name] []
,输入[ type_ text,onInput(onInputHandler idx setPatientDateOfBirth),值Patient.dateOfBirth] []
...

我当前正在将这些功能中的每一个用作输入元素的事件处理程序。因此,我需要一个可用于处理输入事件的函数。理想情况下,我只定义一个函数,并将该单个函数用于所有输入元素,并将要在患者记录上更新的字段传递给该函数。

解决方案

简短的回答是否。但这似乎有点像 XY问题。尚不清楚您要实现什么好处,因为该功能的完整应用将比等效的记录更新表达式更长:

  setField名称患者值

-vs

{患者|名称=值}

并且作为部分应用函数仅比具有缩短功能的等效匿名函数短参数名称:

  setField name 

-vs

\rx-> {r |名称= x}

尽管后面的带有所有符号的噪声都很大



还有一个快捷功能,用于获取一个记录字段:



< pre $ = lang-hs prettyprint-override> .name

-vs

-> r.name

因此,对于setter函数也有专用语法有一些先例,但不幸的是不是。可能是因为这样做会使语言(尤其是语法)复杂化,而带来的收益却相对较少。因此,我对您实际上要完成的工作感到好奇。



问题更新后进行编辑:



Msg 中放置函数是一个非常糟糕的主意,因为它与Elm体系结构背道而驰。这会使状态转换变得不透明,并且无法与调试器很好地配合。当发生问题时,您仍然可以看到事前和事后的状态,但是您将很难理解发生了什么事,为什么发生了事,因为这些信息被编码为不透明的函数,可能不是应该的。



您也将很难考虑到逻辑因素。如果仅在某个特定字段更新时才需要执行某些操作,则可能需要将逻辑放在视图中,或者通过在 update ,而其余的则在视图中。无论哪种方式,您都在通往混乱的代码库的道路上。



通常,您应该使用消息的名称来描述发生了什么而不是要做什么,因为这很容易导致命令式的心态。例如,您可以将其称为 InputChanged 而不是 UpdateCell 。然后,应该使用该字段的标识符代替该函数。理想情况下是自定义类型,例如 InputChanged Name ,但是即使是字符串也可以使用,尽管错过错字会容易得多。



因此,您无需区分每个字段的setter函数,只需对消息进行大小写匹配,并在 update 函数中设置该字段:

  InputChanged名称值-> 
{病人| name = value}

-vs

setPatientName:Patient->字符串->患者
setPatientName患者值=
{患者|名称=值}

然后,例如,如果您需要在名称更改时清除性别,(例如原因...),您只需执行以下操作即可:

  InputChanged名称值-> 
{病人|名称=值,性别=}

榆木结构很好,因为它使更改变得容易且安全,不是因为它简洁明了且没有样板。好的Elm代码通常会有很多复制和粘贴,但这并不总是不好的。


Here I have several functions that all just set a single field on a model record. In a more dynamic language, I'd just have a single setter function and pass it the name of the field (as a string) and the value that I want to set on the model object.

Is there a way to pass the name of the field in Elm? What's the Elm way of doing something like this?

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 ] []
    ...

I'm currently using each of these functions as an event handler for input elements. So I need a function that I can use for handling the input event. Ideally, I'd define just a single function and use that single one for all the input elements and pass it the field I want to update on the patient record.

解决方案

The short answer is "no". But this seems a bit like an XY problem. It's not clear what benefit you are trying to achieve since the full application of such a function would be longer than the equivalent record update expression:

setField "name" patient value

-- vs

{ patient | name = value }

and as a partially applied function is only slightly shorter than the equivalent anonymous function with shortened argument names:

setField "name"

-- vs

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

Although the latter is significantly noisier with all the symbols.

There is also a short-hand function for getting a record field:

.name

-- vs

\r -> r.name

So there is some precedent for having a dedicated syntax for setter functions too, but unfortunately there is not. Likely because it would complicate the language, and the syntax in particular, for relatively little benefit. I'm therefore curious about what you're actually trying to accomplish.

Edit after question update:

Putting functions in the Msg is a very bad idea because it goes against the Elm Architecture. It makes the state transition opaque and won't work very well with the debugger. When something goes wrong you can still see the state before and after, but you'll have trouble understanding what happened, and why it happened, because that information is encoded in an opaque function which probably isn't the one it should be.

You'll also have trouble factoring your logic. If you need something to happen only when a certain field updates, you might have to put the logic in the view, or special-case that field by putting the logic for that in update while the rest is in view, for example. Either way, you're on the path to a messy code base.

You should generally use names for messages that describe what happened, not what to do, because that tends to lead to an imperative mindset. Instead of UpdateCell you could call it InputChanged, for example. Then instead of the function you should have an identifier for the field. Ideally a custom type, like InputChanged Name, but even a string will work, though it will be much easier to miss a typo.

So instead of setter functions for each field you'll just case match the message and set the field in the update function:

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

-- vs

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

Then if you need to clear the sex when the name changes, for example (because reasons...), you can simply do:

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

The Elm Architecture is good because it makes changes easy and safe, not because it's concise and free of boiler-plate. Good Elm code often has a lot of copy-and-paste, but that's not always bad.

这篇关于有没有一种方法可以将字段名称传递给setter函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆