react 函数式编程
这篇文章经过了MoritzKröger , Mark Brown和Dan Prince的同行评审。 感谢所有SitePoint的同行评审员使SitePoint内容达到最佳状态!
Elm是一种功能编程语言,最近引起了很多兴趣。 本文探讨了它的含义以及您为什么要关心它。
Elm当前的主要重点是使前端开发更简单,更强大。 Elm编译为JavaScript,因此可用于为任何现代浏览器构建应用程序。
Elm是具有类型推断功能的静态类型语言 。 类型推断意味着我们不需要自己声明所有类型,我们可以让编译器为我们推断许多类型。 例如,通过写one = 1
,编译器知道one
是整数。
Elm是几乎纯的函数式编程语言。 Elm建立在许多功能模式之上,例如纯视图 , 参照透明性 , 不变的数据和受控的副作用 。 这是密切相关的其他ML语言,如哈斯克尔和Ocaml程序编写 。
榆木是React性的。 榆树的一切都通过信号流动。 Elm中的信号随时间传送消息。 例如,单击按钮将通过信号发送消息。
您可以认为信号类似于JavaScript中的事件,但是与事件不同,信号是Elm中的头等公民,可以传递,转换,过滤和组合。
榆木语法
Elm语法类似于Haskell
,因为它们都是ML家族语言。
greeting : String -> String
greeting name =
"Hello" ++ name
这个函数接受一个String
并返回另一个String
。
为什么要使用榆木?
要了解为什么要关心Elm,让我们谈谈最近几年的一些前端编程趋势:
描述状态而不是转换DOM
不久前,我们通过手动更改DOM(例如使用jQuery)来构建应用程序。 随着应用程序的增长,我们引入了更多的状态。 必须对它们之间的转换进行编码,这成倍地增加了我们应用程序的复杂性,使其难以维护。
而不是这样做,像React这样的库已经普及了关注于描述特定DOM状态的概念,然后让该库为我们处理DOM转换。 我们仅专注于描述谨慎的DOM状态,而不关注我们如何到达那里。
这导致编写和维护的代码大大减少。
事件和数据转换
当涉及到应用程序状态时,通常要做的是自己更改状态,例如向数组添加注释。
除了这样做,我们只能描述如何根据事件更改应用程序状态,并让其他事物为我们应用这些转换。 在JavaScript中, Redux使这种构建应用程序的方式流行起来。
这样做的好处是我们可以编写“纯”函数来描述这些转换。 这些功能更易于理解和测试。 另一个好处是,我们可以控制更改应用程序状态的位置,从而使我们的应用程序更易于维护。
另一个好处是我们的视图不需要知道如何改变状态,它们只需要知道要调度哪些事件。
单向数据流
另一个有趣的趋势是使我们所有的应用程序事件都以单向方式流动。 我们不通过任何组件与任何其他组件的对话,而是通过中央消息管道发送消息。 这个集中的管道应用了我们想要的转换,并将更改广播到应用程序的所有部分。 助焊剂就是一个例子。
通过这样做,我们可以更好地了解应用程序中发生的所有交互。
不变数据
![](https://i-blog.csdnimg.cn/blog_migrate/e9cc082b475ab7d469fbc189fbdee21c.png)
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
可变数据很难限制更改位置,因为任何有权访问它的组件都可以添加或删除某些内容。 这导致不可预测性,因为状态可能会在任何地方更改。
通过使用不可变数据,我们可以通过严格控制更改应用程序状态的位置来避免这种情况。 将不可变数据与描述转换的函数结合在一起,可以为我们提供非常强大的工作流程,而不可变数据则可以帮助我们通过不让我们在意想不到的位置更改状态来强制执行单向流。
中央集权国家
前端开发的另一个趋势是使用集中式“原子”来保持所有状态。 这意味着我们将所有状态放在一棵大树中,而不是将其分散在各个组件中。
在典型的应用程序中,我们通常具有全局应用程序状态(例如,一组用户)和特定于组件的状态(例如,特定组件的可见性状态)。 将两种状态都存储在一个地方是否有益还存在争议。 但是至少将所有应用程序状态保持在一个地方会有很大的好处,那就是在应用程序的所有组件之间提供一致的状态。
纯成分
另一个趋势是使用纯组分。 这意味着给定相同的输入,组件将始终呈现相同的输出。 这些组件内部没有发生副作用。
这使我们比以前更容易理解和测试我们的组件,因为它们更容易预测。
回到榆木
这些都是使应用程序更加健壮,可预测和可维护的出色模式。 但是,为了在JavaScript中正确使用它们,我们需要尽力避免在错误的位置进行某些操作(例如,更改组件内部的状态)。
Elm是一种从一开始就创建的编程语言,牢记了许多模式。 拥抱和使用它们非常自然,而不必担心做错事。
在Elm中,我们使用以下方法构建应用程序:
- 不变的数据
- 描述DOM的纯视图
- 单向数据流
- 集中状态
- 描述数据突变的集中位置
- 所包含的副作用
安全
Elm的另一个重要好处是它提供的安全性。 通过完全避免数值为空的可能性,它迫使我们处理应用程序中的所有替代途径。
例如,在JavaScript(和许多其他语言)中,您可以通过执行以下操作来获取运行时错误:
var list = []
list[1] * 2
这将返回JavaScript中的NaN
,您需要对其进行处理以避免运行时错误。
如果您在榆树中尝试类似的方法:
list = []
(List.head list) * 2
编译器将拒绝此操作,告诉您List.head list
返回Maybe类型。 Maybe类型可能包含也可能不包含值,我们必须处理该值为Nothing
。
(Maybe.withDefault 1 (List.head list)) * 2
这使我们对应用程序充满信心。 很少看到Elm应用程序中的运行时错误。
样品申请
为了更清楚地了解Elm语言以及如何使用它构建应用程序,让我们开发一个微型应用程序,该应用程序显示HTML元素在页面上移动。 您可以通过转到http://elm-lang.org/try并在其中粘贴代码来尝试该应用程序。
import Html
import Html.Attributes exposing (style)
import Time
name : Html.Html
name =
Html.text "Hello"
nameAtPosition : Int -> Html.Html
nameAtPosition position =
Html.div [
style [("margin-left", toString position ++ "px")]
] [
name
]
clockSignal : Signal Float
clockSignal =
Time.fps 20
modelSignal : Signal Int
modelSignal =
Signal.foldp update 0 clockSignal
update : Float -> Int -> Int
update _ model =
if model > 100 then
0
else
model + 1
main : Signal Html.Html
main =
Signal.map nameAtPosition modelSignal
让我们一步一步地研究它:
import Html
import Html.Attributes exposing (style)
import Time
首先,我们导入应用程序中所需的模块。
name : Html.Html
name =
Html.text "Hello"
name
是一个函数,该函数返回包含文本Hello
的Html
元素。
nameAtPosition : Int -> Html.Html
nameAtPosition position =
Html.div [
style [("margin-left", toString position ++ "px")]
] [
name
]
nameAtPosition
将name
包装在div
标签中。 Html.div
是一个返回div
元素的函数。 该函数将整数position
作为唯一参数。
Html.div
的第一个参数是HTML 属性的列表 。 第二个参数是子HTML元素的列表 。 空的div标签应为Html.div [] []
。
style [("margin-left", toString position ++ "px")]
创建样式HTML属性,该属性包含给定位置的margin-left
。 这将以style="margin-left: 11px;"
在位置11
被召唤时。
因此,在摘要中, nameAtPosition
呈现Hello
并在左侧留有空白。
clockSignal : Signal Float
clockSignal =
Time.fps 20
在这里,我们创建一个信号 ,每秒以20次流式传输消息。 这是浮动信号。 我们将以此为刷新动画的心跳。
modelSignal : Signal Int
modelSignal =
Signal.foldp update 0 clockSignal
clockSignal
给了我们一个心跳,但是它通过信号发送的消息没有用, clockSignal
的有效负载只是每个消息之间的增量。
我们真正想要的是一个计数器(即1、2、3等)。 为此,我们需要在应用程序中保持状态。 那是我们拥有的最后一个计数,并且在每次clockSignal
触发器触发时增加它。
Signal.foldp
是如何在Elm应用程序中保持状态的方法。 您可以以类似于JavaScript中的Array.prototype.reduce
方式来考虑foldp
, foldp
具有一个累加函数 ,一个初始值和一个源信号 。
每次源信号传输事件时, foldp
使用先前的值调用累积函数,并保留返回的值。
因此,在这种情况下,每次clockSignal
发送一条消息时,我们的应用程序都会使用最新计数调用update
。 0
是初始值。
update : Float -> Int -> Int
update _ model =
if model > 100 then
0
else
model + 1
update
是累积功能 。 它以Float
作为第一个参数,它是clockSignal
的增量。 一个整数,它是计数器的先前值作为第二个参数。 并返回另一个整数,该整数是计数器的新值。
如果model
(计数器的先前值)大于100,则将其重置为0,否则将其增加1。
main : Signal Html.Html
main =
Signal.map nameAtPosition modelSignal
最后,Elm中的每个应用程序都从main
功能开始。 在这种情况下,我们map
了modelSignal
上面我们通过创建nameAtPosition
功能。 也就是说,每次modelSignal
流传输一个值时,我们都会重新渲染视图。 nameAtPosition
将接收来自modelSignal
的有效负载作为第一个参数,有效地modelSignal
更改div margin-left
样式二十次,因此我们可以看到文本在页面上移动。
我们上面构建的应用程序演示:
- Elm中HTML
- 使用信号
- 保持功能状态
- 纯意见
如果您使用过Redux,您会注意到Elm和Redux之间有一些相似之处。 例如Elm中的update
与Redux中的reducer非常相似。 这是因为Redux受到Elm架构的极大启发。
结论
Elm是一种令人兴奋的编程语言,它包含用于构建可靠应用程序的出色模式。 它具有简洁的语法,并内置许多安全性,可避免运行时错误。 它也有一个很棒的静态类型系统,在重构过程中有很大帮助,并且不会干扰,因为它使用类型推断。
关于如何构建Elm应用程序的学习曲线并非微不足道,因为使用功能性React式编程的应用程序与我们习惯的应用程序不同,但值得这样做。
其他资源
- 在Elm中构建大型应用程序时,最好使用Elm体系结构。 有关更多信息,请参见本教程 。
- Elm Slack社区是寻求帮助和建议的绝佳场所。
- Elm上的Pragmatic Studio视频是一个很好的入门资源。
- Elm-tutorial是我正在研究的指南,教您如何使用Elm构建Web应用程序。
翻译自: https://www.sitepoint.com/functional-reactive-programming-elm-introduction/
react 函数式编程