函数类型
当你浏览像 elm/core
和 elm/html
这样的包时,你一定看到带有多个箭头的函数。例如
String.repeat : Int -> String -> String
String.join : String -> List String -> String
为什么有如此之多的箭头?这里究竟发生了什么?
隐藏的括号
当你看到所有括号时,一切将开始变得清晰。例如,编写 String.repeat
的类型如下所示
String.repeat : Int -> (String -> String)
这是一个函数,它接收一个 Int
,然后生成另一个函数。我们来看看它在中实际应用
因此,从概念上讲,每个函数都接受一个参数。它可以返回另一个接受一个参数的函数。依此类推。在某些时候,它将停止返回函数。
我们可以始终使用括号来表明这就是实际发生的情况,但是当你有多个参数时,这将变得相当笨重。这与编写 4 * 2 + 5 * 3
而不是 (4 * 2) + (5 * 3)
背后的逻辑相同。这意味着需要额外学习一些东西,但它非常常见,因此是值得的。
好的,但这个特性在第一位有什么意义?为什么不执行 (Int, String) -> String
并同时给出所有参数呢?
部分应用
在 Elm 程序中使用 List.map
函数相当常见
List.map : (a -> b) -> List a -> List b
需要两个论点:一个函数和一个列表。从那里它转变列表中的每一个元素用那个函数。下面是一些例子
List.map String.reverse ["part","are"] == ["trap","era"]
List.map String.length ["part","are"] == [4,3]
现在记住String.repeat 4
它的类型是String -> String
吗?这意味着我们可以说
List.map (String.repeat 2) ["ha","choo"] == ["haha","choochoo"]
表达式(String.repeat 2)
是一个String -> String
函数,所以我们可以直接使用它。无需说(\str -> String.repeat 2 str)
.
Elm 也使用约定,即数据结构始终是最后一个论点在整个生态系统中。这意味着通常在设计函数时考虑这种可能的用法,使这成为一种相当常见的技术。
现在重要的是要记住这可能会被过度使用!它有时候很方便和明确,但我发现最好适当地使用它。所以我总是建议在事情变得有点复杂,甚至一点点复杂时,分解顶层的辅助函数。这样它就有了一个明确的名称,论点被命名,并且很容易测试这个新的辅助函数。在我们的例子中,这意味着创建
-- List.map reduplicate ["ha","choo"]
reduplicate : String -> String
reduplicate string =
String.repeat 2 string
这种情况真的很简单,但是(1)现在更清楚的是我感兴趣于被称为倍增的语言现象,并且(2)随着我的程序的发展,很容易为reduplicate
添加新的逻辑。也许我想要shm-reduplication支持?
换句话说,如果您的部分应用程序变长,请使其成为一个辅助函数。并且如果它是多行的,那么它肯定应该被转换为一个顶级辅助函数!此建议也适用于使用匿名函数。
注意:如果你在使用此建议时最终产生了“太多”函数,我建议使用诸如
-- REDUPLICATION
之类的注释来概述接下来的 5 个或 10 个函数。老派!我在以前的示例中已经用-- UPDATE
和-- VIEW
注释来展示了这一点,但这是我在所有代码中使用的通用技术。如果您担心使用此建议使文件变得太长,我建议观看文件的生命!
管道
Elm 还有一个管道操作符,它依赖于部分应用程序。例如,假设我们有一个sanitize
函数,用于将用户输入转换为整数
-- BEFORE
sanitize : String -> Maybe Int
sanitize input =
String.toInt (String.trim input)
我们可以这样重写它
-- AFTER
sanitize : String -> Maybe Int
sanitize input =
input
|> String.trim
|> String.toInt
因此在这个“管道”中,我们将输入传递给String.trim
,然后将其传递给String.toInt
.
这很简洁,因为它允许采用许多人喜欢的“从左到右”的阅读方式,但 **管道很容易被过度使用!** 如果有三个或四个步骤,那么分离出顶级辅助函数后,代码通常会变得更清晰。现在,变换有了名称。参数已被命名。它有一个类型注释。这种方式更加具有自描述性,而且你的团队成员和未来的你会感谢它!这样,测试逻辑也会变得更容易。
注意:我个人更喜欢
BEFORE
,但也许只是因为我是在没有管道的情况下学习函数式编程语言的。