如何减少榆树中的参数?

How to decrease arguments in elm?

我是 Elm 的新手(版本 0.19)。

困扰我的是我传递的大量论点。我认为问题出在我的 OOP 思维方式上。在我的代码中,我有一堆辅助函数需要访问我的 model (TEA)。我一直在视图函数中使用 let / in 语法来定义这些助手,因为这使它们可以访问模型参数。但是我有 10 多个辅助函数并且我不断地传递它们,这让我的代码看起来很难看且难以理解。在 OOP 中,这些辅助函数都是我将传递的某些对象上的方法。

下面的代码片段是一个使用 elm-ui. Full example can be run on Ellie

的人为示例
Element.layout []  
    <| column 
        [ w |> px |> width
        , h |> px |> height
        , blueBg
        , centerX
        , centerY
        ]
        [ el [centerX, centerY, whiteTxt, fontSize 40] <| text "Hello world"
        , header w h scale whiteTxt space blueBg pad radius whiteBg fontSize blackTxt greyBg blueTxt
        ]


header w h scale whiteTxt space blueBg pad radius whiteBg fontSize blackTxt greyBg blueTxt =
    -- code here
    el [] Element.none

最好的方法是什么?

对此有几种策略:

  • 使用类型来抽象掉数据的复杂性。使用类型为您的问题建模,然后创建简单的函数来对这些类型进行操作。
  • 在更高级别定义常量和辅助函数,以便它们在函数的范围内,而不是将它们作为参数传递。
  • 将函数参数缩小到仅该函数严格需要的参数。

在 header 示例中,您可以为样式、标题和副标题提供自己的类型:

type alias Title = String

type alias SubTitle = String

header : HeaderStyle -> Title -> SubTitle -> Element Msg

当您确实有大量信息要传递时,记录通常是保存数据的第一个 go-to。例如,如果您使用不同的样式多次重复使用 header 函数,那么 Header 样式记录类型可能会有用:

type alias HeaderStyle = { borderColor : Color
                         , textColor: Color
                         , backgroundColor : Color
                         ...
                         }

现在,由于标题和副标题是 header 的一部分,您还可以将其全部拉入一条记录:

type alias HeaderData = { borderColor : Color
                         , textColor: Color
                         , backgroundColor : Color
                         , bannerText : String
                         , bannerImage : Url
                         , headerTitle : Title
                         , headerSubTitle : SubTitle
                         ...
                         }

如果我们注意库提供的类型,我们会发现 elm-ui 将样式保留在列表中以匹配它们的类型可能更有意义。修改我们的记录,我们得到:

type alias HeaderData = { styleList : List (Attribute Msg) 
                        , headerTitle : Title
                        , headerSubTitle : SubTitle
                        ...
                        }

这里的好处是我们可以通过调用自动函数styleList: HeaderData -> List (Attribute Msg)提取样式列表并直接在Element模块的函数中使用它。

缺点是,如果有人 HeaderData 缺少一些关键样式,我们会丢失我们漂亮的编译器错误消息。

无论哪种方式,现在我们的 header 功能都只有一个输入。

header : HeaderData -> Element Msg

很好,但这仍然意味着我们每次 运行 header 都需要填充一个 Header 数据。我们如何简化它?

一种策略是在顶层使用常量,并使用辅助函数/部分应用程序来应用这些常量。

例如,我们可以定义一个顶级常量和辅助函数,让我们创建各种预定义的 headers:

pageStyle : List (Attribute Msg)
pageStyle = [ Border.color <| rgb255 35 97 146
            , Font.color <| rgb255 35 97 146 ]


redHeaderStyle : Title -> HeaderData
redHeaderStyle title =
   { styleList : pageStyle 
                 ++ [ Background.color red
                    , Font.color black
                    ...
                    ]
   , headerTitle : title
   }

这里 pageStyle 可以用在别处,而 readHeaderStyle 是为这个特殊情况添加的。请注意,我们为以后保留了一个参数,因为每个应用程序的标题可能会发生变化。

当然,在 Elm 中,您不仅限于列表和记录 - 您还可以使用 Header 的乘积和求和类型。何时使用每种类型将取决于诸如您希望类型安全的位置以及您希望如何组合函数等因素。

总结一下:

  • 首先使用类型对您的问题域进行建模,而不是根据 objects 或组件进行思考。

  • 不要试图让所有东西通用和可重用。而是创建保存信息的类型,并使用辅助函数,这样您只需担心相关的内容。

  • 不要害怕在 top-level 处定义常量和辅助函数。限制对 let..in 结构的使用以提高可读性,而不是将定义作为方法进行分组。

  • 在 as-needed 的基础上为您的类型编写辅助函数,而不是尝试创建包含所有可能功能的库。

  • 如果你想划分相似的功能和类型,你可以使用模块,然后将它们导入一个方便的名称。这样你就可以说例如Background.colorElement.row.

最后,Scott Wlashchin 就函数式编程中的设计策略做了一个很好的演讲:Functional programming design patterns by Scott Wlaschin