榆木搜官网
我们的Haskell网络系列涵盖了许多很酷的库,您可以在制作Web应用程序时使用它们。 但是我们尚未在此博客上介绍的一件事是使用Haskell进行前端Web开发。 那里有许多库和框架。 想到Yesod和Snap 。 另一个选择是Reflex FRP ,它在引擎盖下使用GHCJS。
但是对于这个新系列,我决定采用不同的方法。 在接下来的几周中,我们将关注Elm !
我爱榆树有几个原因。 Elm坚信我可以采用函数式编程的原理并将其付诸实践。 语言是废话,文档也很好。 榆树有一些语法怪癖。 它还缺少一些Haskell关键功能。 但是,我们仍然可以做很多事情。
本周,我们将介绍基本的安装,区别和用法。 下周,我们将在Elm中编写一个简单的Todo应用程序。 这将使我们对如何构建Elm应用程序有一种感觉。 我们将通过探索如何在我们的应用程序中添加更多效果以及如何将Elm类型与Haskell集成来结束本文。
当然,前端只是故事的一部分。 要了解有关将Haskell用于后端Web的更多信息,请查看我们的Haskell Web系列 ! 您也可以下载我们的生产清单以获取更多想法!
基本设定
与任何语言一样,首次将Elm安装到我们的计算机上将涉及一些设置。 对于Windows和Mac,您可以运行此处提供的安装程序。 对于Linux,有单独的说明,但是它们很简单。 您获取二进制文件,将其tar
,然后移至bin
。
一旦安装了elm
可执行文件,就可以开始。 使用完足够的软件包管理程序后,该过程将变得更容易理解。 elm
命令与stack
和npm
有一些基本的共同点。
首先,我们可以运行elm init
来创建一个新项目。 这将为我们创建一个src
文件夹以及一个elm.json
文件。 此JSON文件与Node.js的.cabal
文件或package.json
相当。 在这里,我们将指定所有不同的程序包依赖项。 默认版本将提供大多数基本的Web软件包。 然后,我们将在/src
创建.elm
源文件。
运行基本页面
Elm开发看起来与我使用过的大多数普通Javascript系统不同。 在编写代码时,我们不需要为应用程序指定特定的入口点。 我们制作的每个文件都是我们可以查看的潜在网页。 因此,这是我们从最简单的应用程序开始的方法:
import Browser
import HTML exposing (Html, div, text)
type Message = Message
main : Program () Int Message
main =
Browser.sandbox { init = 0, update = update, view = view }
update : Message -> Int -> Int
update _ x = x
view : Int -> Html Message
view _ = div [] [text "Hello World!"]
榆树使用模型/视图/控制器系统。 我们在main
函数中定义程序。 我们的Program
类型具有三个参数。 第一个与我们可以传递给程序的标志有关。 我们暂时将其忽略。 第二个是我们程序的模型类型。 我们将从一个简单的整数开始。 然后最后一个类型是一条消息。 通过发送此类消息,我们的视图将导致更新。 sandbox
功能意味着我们的程序很简单,并且没有副作用。 除了传递初始状态外,我们还传递update
功能和view
功能。
update
功能使我们可以接收新消息并在必要时更改模型。 然后, view
是一个采用我们的模型并确定HTML组件的函数。 您可以将view
类型读为“发送消息类型为Message
HTML组件”。
我们可以运行elm-reactor
命令并将浏览器指向localhost:8000
。 这会将我们带到仪表板,在这里我们可以检查所需的任何文件。 我们只希望看一下具有main
功能的产品。 然后,我们将在屏幕上看到带有div的简单页面。 (如果我们选择一个纯库文件,它会奇怪地旋转)。
根据Elm教程,我们可以通过在模型中使用Int
使其变得更加有趣。 我们将更改Message
类型,使其可以表示Increment
或Decrement
。 然后,我们的更新功能将根据消息更改模型。
type Message = Increment | Decrement
update : Message -> Int -> Int
update msg model = case msg of
Increment -> model + 1
Decrement -> model - 1
view : Int -> Html Message
view model = div [] [String.fromInt model]
作为最后的更改,我们将在界面中添加+
和-
按钮。 这些将使我们能够将Increment
和Decrement
消息发送到我们的类型。
view model = div []
[ button [onClick Decrement] [text "-"]
, div [] [ text (String.fromInt model) ]
, button [onClick Increment] [text "+"]
]
现在我们有了一个界面,可以按每个按钮,屏幕上的数字将会改变! 那是我们的基本应用!
制作命令
elm reactor
命令建立了一个虚拟界面供我们使用和检查我们的页面。 但是我们的最终目标是做到这一点,以便我们可以从Elm代码生成HTML和Javascript。 然后,我们将导出这些资产,以便我们的后端可以将它们用作资源。 我们可以使用elm make
命令来做到这一点。 这是一个示例:
elm make Main.elm --output=main.html
我们将要使用脚本将所有这些元素组合在一起并将它们转储到资产文件夹中。 当我们完成一个完整的Elm + Haskell项目时,我们将在几周内获得一些经验。
与Haskell的区别
将Elm与Haskell进行比较时,存在一些语法陷阱。 我们不会涵盖所有内容,但是这里是基础知识。
我们已经可以看到导入和模块语法有所不同。 我们在导入定义中使用exposing
关键字来从该模块中选择所需的特定表达式。
import HTML exposing (Html, div, text)
import Types exposing (Message(..))
当我们定义自己的模块时,我们还将使用exposing
关键字代替模块定义中的where
:
module Types exposing
(Message(..))
type Message = Increment | Decrement
我们还可以看到Elm使用了在Haskell中使用data
type
。 如果我们需要类型同义词,Elm提供了type alias
组合:
type alias Count = Int
从上面的类型运算符可以看到,Elm反转了:
运算符和::
。 单个冒号是指类型签名。 双冒号是指附加列表:
myNumber : Int
myNumber = 5
myList : [Int]
myList = 5 :: [2, 3]
Elm还缺少Haskell的一些更好的语法元素。 例如,榆木缺少功能和后卫的模式匹配。 榆树也没有where
子句。 仅存在case
和let
语句。 而不是.
用于函数组合的运算符,可以使用<<
。 data-preserve-html-node =“ true”以下是这些要点的一些示例:
isBigNumber : Int -> Bool
isBigNumber x = let forComparison = 5 in x > forComparison
findSmallNumbers : List Int -> List Int
findSmallNumbers numbers = List.filter (not << isBigNumber) numbers
作为本节的最后一点,对Elm进行了严格评估。 Elm编译为Javascript,因此可以在浏览器中运行。 而且,使用严格的语言生成明智的Java脚本要容易得多。
榆树唱片
Elm的另一个主要区别是记录语法的工作方式。 对于Elm,“记录”是一种特定类型。 这些模拟Javascript对象。 在此示例中,我们为记录定义类型同义词。 虽然通常没有模式匹配,但是我们可以在记录上使用模式匹配:
type alias Point2D =
{ x: Float
, y: Float
}
sumOfPoint : Point2D -> Float
sumOfPoint {x, y} = x + y
为了使我们的代码更像Javascript,可以使用.
操作员以不同方式访问记录。 我们可以使用Javascript之类的语法,也可以使用句点和字段名作为常规函数。
point1 : Point2D
point1 = {x = 5.0, y = 6.0}
p1x : Float
p1x = point1.x
p1y : Float
p1y = .y point1
我们还可以轻松地更新记录的特定字段。 这种方法可以很好地扩展到许多领域:
newPoint : Point2D
newPoint = { point1 | y = 3.0 }
类型类和单子
Haskell和Elm之间更具争议的区别在于这两个概念。 Elm没有类型类。 对于像我这样的Haskell资深人士,这是一个很大的限制。 正因为如此,榆树也缺乏do
语法。 请记住, do
语法依赖于Monad
类型类存在的想法。
这些遗漏是有原因的。 榆树的创作者写了一篇有趣的文章 。
他的主要观点是(与我不同),大多数Elm用户来自Javascript,而不是Haskell。 他们往往没有太多的函数式编程和相关概念的知识。 因此,Elm捕获这些结构并不是一个优先事项。 那么有哪些替代方法可用呢?
好吧,当涉及到类型类时,每个类型都必须为函数定义自己的定义。 让我们以map
的简单示例为例。 在Haskell中,我们有fmap
函数。 它允许我们在容器上应用函数,而无需知道容器是什么:
fmap :: (Functor f) => (a -> b) -> fa -> fb
无论有列表还是字典,我们都可以应用相同的功能。 但是在Elm中,每个库都有其自己的map
功能。 因此,我们必须限定其用法:
import List
import Dict
double : List Int -> List Int
double l = List.map (* 2) l
doubleDict : Dict String Int -> Dict String Int
doubleDict d = Dict.map (* 2) d
Elm使用一个名为andThen
的函数代替monad。 这很像Haskell的>>=
运算符。 我们在Java等面向对象的语言中更经常看到这种模式。 作为文档中的示例,我们可以看到Maybe
如何使用它。
toInt : String -> Maybe Int
toValidMonth : Int -> Maybe Int
toValidMonth month =
if month >= 1 && month <= 12
then Just month
else Nothing
toMonth : String -> Maybe Int
toMonth rawString =
toInt rawString `andThen` toValidMonth
因此Elm不能提供与Haskell一样多的功能。 也就是说,Elm首先是一种前端语言。 它表达了如何显示我们的数据以及如何将组件组合在一起。 如果我们需要复杂的功能元素,则可以使用Haskell并将其放在后端。
结论
我们现在就停在那里。 下周,我们将通过编写一个更复杂的程序来扩展对Elm的理解。 我们将编写一个简单的Todo list应用程序,并观察Elm的体系结构。
要了解周一早上Haskell的更多信息,请确保订阅我们的新闻通讯! 这也将使您能够访问我们很棒的参考资料页面!
翻译自: https://hackernoon.com/elm-more-functional-frontend-c7d8c21c8bce
榆木搜官网