如何在整个 Web 应用程序堆栈中利用 Haskell 类型安全性?

Posted

技术标签:

【中文标题】如何在整个 Web 应用程序堆栈中利用 Haskell 类型安全性?【英文标题】:How can you leverage Haskell type safety through the whole web application stack? 【发布时间】:2013-12-03 08:42:53 【问题描述】:

我想知道以 CRUD 为中心的 Web 应用程序可以从 Haskell 的类型系统中获得多少好处,尤其是当前端是使用 AngularJS 等传递无类型数据对象的 javascript MVC 框架构建时。

在我看来,只要将 Haskell 数据类型转换为 JSON 对象,然后传递给繁重的 JavaScript MVC 框架层,将 Haskell 的类型系统作为 Web 堆栈的一部分的好处就会开始急剧下降,因为没有让类型检查器确保数据流过整个 Web 应用程序的类型完整性的方法。

例如,您可以更改数据库架构和关联的 Haskell 类型,但类型检查器将无法告诉您 JavaScript MVC 前端的哪些部分也需要更新。我认为这是一个问题。

我是否正确地说明了问题,如果是,Haskell Web 应用程序开发人员可以在这一点上给出什么建议?

【问题讨论】:

我不是 Haskell 开发人员,但是对应用程序的后端部分(以及通过暴露接口从前端接收的任何数据)进行类型检查还不够吗? @Tibos 这不是没有,但我的问题是,太多的应用程序逻辑最终都在 JavaScript 部分,你无法从 Haskell 的类型系统中受益。 Yesod, AngularJS and Fay 的文章给出了一些指点(yesodweb.com/blog/2012/10/yesod-fay-js) 您可以使用 Haskell 对整个 Web 应用程序进行类型检查。您可以在 Haskell 中定义一次数据类型,然后以 Haskell 本机格式将其保存到磁盘(不需要 SQL)(参见 acid-state)。您的前端代码可以使用类型安全的 html 组合器 (jaspervdj.be/blaze) 生成,您的 JavaScript 代码可以使用 jMacro 或 Fay(现在更多..)生成,以确保编译时安全 (happstack.com/page/view-page-slug/15/…)。 还有GHCjs github.com/ghcjs/ghcjs 【参考方案1】:

自从我们最近开始了一个具有大量 javascript 前端的项目以来,我们一直在努力解决这个完全相同的问题。我的轶事观察是,与之前仅使用 Snap 并使用 Heist 生成 HTML 的应用程序相比,我们在 javascript 应用程序中存在的错误要多得多。我们尚未做出任何决定,但以下是我们一直在考虑的一些可能的解决方案:

精简 Javascript 包装器

CoffeeScript TypeScript Dart

这些对我来说是非常不满意的解决方案。 Javascript 略有改进,但与我使用 Haskell 获得的东西相比还差得远。

更多功能和类型安全的前端语言

Fay 是 Haskell 的一个真子集,可以编译为 Javascript。它肯定有一些吸引力,但不能让您访问所有 Haskell。最后我听说它不支持类型类,我想这很快就会成为一个障碍。 Elm Roy

这些解决方案(以及前一组)的问题是,如果您的后端是用 Haskell 编写的,您仍然会遇到阻抗不匹配,因为您的前端语言不是 Haskell。这使您的代码不那么 DRY,因为您最终必须在 Haskell 和前端语言中定义相同的数据结构。当您在 Haskell 中更改它们时,您不会收到指示前端代码需要更改的错误。应用程序崩溃了。

将 Haskell 编译为 Javascript

镇上的新游戏是ghcjs。这是一个非常有前途的项目,但我认为至少在 GHC 7.8 发布之前它不适合生产。这有望在下周内发生。一旦 7.8 发布,您仍然必须考虑到 ghcjs 仍然很新。即使在假设功能 100% 完成并且第一个版本运行良好的假设场景中,您仍然必须记住,在 Haskell+ghcjs 与 Angular 等高级 JavaScript 框架一样有效之前,必须构建相当多的基础设施,余烬等。

2016 年 9 月更新:现在,在我最初写这个答案将近三年后,GHCJS 有了很大的改进。还有更多改进的空间,但我已经将它用于生产应用程序并且效果很好。与 Reflex FRP library 结合使用时,它的功能特别强大,可以更轻松地构建反应式 UI。

使用 EDSL 从 Haskell 生成 Javascript

如果您有一个相对受限的问题,则可以在生成 javascript 的 EDSL 之上完成您的所有应用程序工作。我们已经有了出色的 jmacro 包来处理生成 Javascript 的低级问题。您可以利用它并生成使用适用于您的应用程序的任何其他 javascript 库的代码。这可能是 javascript + jquery、D3.js,甚至是使用更高级别的 javascript 框架(如 Angular 或 Ember)的代码。我倾向于认为 Angular 比 Ember 更容易生成代码,因为它简单且封装性更强。

为浏览器中的函数式语言设计的字节码虚拟机

这只是我的一个天上掉馅饼的想法。我不认为它真的很实用,因为它需要大量的工作并且很难获得采用。但我想至少提到完整性的想法。其他人已经指出 asm.js 几乎已经是这样了。情况可能是这样,但最好从一开始就将尾调用优化等东西设计到 VM 级别。

【讨论】:

谢谢@mightybyte。顺便说一句,编译的 Heist 是否提供与 BlazeHtml 相同程度的类型检查? Heist 在运行时从磁盘读取模板,所以没有(根据定义)。但是 Heist 确实有足够的能力,你仍然可以获得类似程度的 DRY 和抽象。在我看来,由于不需要使用 GHC 编译模板,设计人员友好性的提高以及灵活性和更快的开发时间足以弥补类型安全性的小损失。【参考方案2】:

在我看来,简单的解决方案 - 从 haskell 数据声明(或其他 api 方案描述)生成 typescript interfaces 并将 TypeScript 用于前端部分。 它为不懂 Haskell 但懂 Javascript 的人提供了开展项目的机会。

例如

data RpcResponse = RpcResponse  number :: Int, string :: Maybe String 

编译成

interface RpcResponse 
  number : number,
  string?: string

并且函数 parseRpcResponseJson 具有 Typescript 类型

parseRpcResponseJson(response: string): Option<RpcResponse>;

【讨论】:

以上是关于如何在整个 Web 应用程序堆栈中利用 Haskell 类型安全性?的主要内容,如果未能解决你的问题,请参考以下文章

如何在颤动中推动替换删除整个导航堆栈?

如何使堆栈面板内的按钮展开以占用整个空间?

如何在 2022 年为 Web 应用程序选择技术堆栈

用于构建 Web 应用程序的技术堆栈 [关闭]

如何在单页移动 Web 应用程序中实现我自己的历史堆栈?

如何在导航控制器中关闭视图控制器,而不关闭整个堆栈