haskell - 功能性思考。在 Haskell/Purescript 中构建新数组

标签 haskell functional-programming purescript

我是函数式编程的新手,我决定用 Purescript 构建一个应用程序。我遇到了第一个障碍,我不确定如何从概念上考虑这个问题。

I'm not looking for code as much as a way to think functionally about this problem.



我有一个数据列表。具体来说,像
[ {a :: String, b :: String, c :: String} ]

我想创建一个列表 Html (这是一个 purescript-halogen 类型)通过使用提供的记录(带有上述类型的列表)。

所以,我会有一个功能
buildElements :: forall p i. MyRecordObject -> Array (HTML p i)

现在,我想我需要给这个函数结果类型一个 Monad 计算上下文(purescript Eff 就像 Haskell IO )

所以像:
buildElements :: forall p i. MyRecordObject -> Eff (Array (HTML p i))

我的第一个想法是模糊地创建一个类似的列表
take $ length xs $ repeat ARecordObject

然后将记录映射到该列表上,但我不确定如何将其转换为代码。无论如何,这似乎是错误的,因为我的计划涉及改变 ARecordObject 的状态。 ,这是一个禁忌。

然后我找到了这个功能:
forEach :: forall e a. Array a -> (a -> Eff e Unit) -> Eff Unit

看起来几乎完美!我得到一个数组,我给它一个函数,该函数以某种方式将记录中的属性分配给这个新数组……但是不,等等……我又在考虑非功能性了。

我在这里真的有点不知所措。基本上,我想创建类似 <li></li> 的列表的内容。元素,在这里我为每个项目分配属性。

例如

我提供了一个记录:
[ { id: "id1", name: "name1", class: "class1", content: "content1" }
, { id: "id2", name: "name2", class: "class2", content: "content2" } ]

我想要一个函数 foo返回一个数组:
[ li [ id_ rec.id, name_ rec.name, class_ rec.class ] [ text rec.content ]
, li [ id_ rec.id, name_ rec.name, class_ rec.lass ] [ text rec.content ] ]

哪里rec是 recordObject 的名称(显然这两个数组不相同,但实际上映射到初始记录上)。

(点语法是一种类似于标准 getter/setter 符号的 purescript 记录语法符号)

最佳答案

My first idea was vaguely around creating a list with something like

take $ length xs $ repeat ARecordObject

and then map the record over that list, but I wasn't really sure how to translate that into code. It seemed wrong anyway, since my plan involved mutating the state of ARecordObject, which is a no-no.



函数式程序员不仅仅避免突变,因为它是禁忌(事实上,许多函数式程序谨慎地使用了可控剂量的可变性)——我们这样做是因为它产生了更安全、更简单的代码。

即:您正在考虑我所谓的“alloc-init 模式”,在这种模式下您创建某种“空”值,然后开始计算其属性。请原谅我的激烈,但这是一个从根本上被破坏的编程模型,是手动内存管理时代遗留下来的;使用它的代码永远不会安全,依赖它的抽象将永远存在漏洞。这个习语不适合任何比 C 更高级别的语言,但是,如果我每次看到这样的代码时都会有一个磅...
var foo = new Foo();
foo.Bar = new Bar();
foo.Bar.Baz = new Baz();

......我会成为一个有钱人(na na na)。默认应该是在您知道它们将是什么样子之后创建对象:
var foo = new Foo(new Bar(new Baz()));

这更简单——你只是在计算一个值,而不是访问指针引用的内存来更新它的内容——更重要的是它更安全,因为类型检查器确保你没有忘记一个属性,它允许你制作 Foo不可变的。最干净的命令式代码是函数式代码——你应该只在性能需要的地方(或者当语言强制你的手时)才是命令式的。

总之,吐槽一下。关键是你通过命令式思考让自己的生活变得比必要的更艰难。只需编写一个计算单个 <li> 的函数即可从单个对象...
toLi :: MyRecord -> HTML
toLi x = li [ id_ x.id, name_ x.name, class_ x.class ] [ text x.content ]

...(请注意,我并没有以某种方式创建“空” li 然后填充其值),然后是 map它在您的输入列表上。
toLis :: [MyRecord] -> [HTML]
toLis = map toLi

这也是我在 JS 中的做法,即使我不需要语言。无副作用,无突变,无需Eff - 只是简单、安全、纯函数式的代码。

关于haskell - 功能性思考。在 Haskell/Purescript 中构建新数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41647753/

相关文章:

java - 如何在Mono<Map<Entity, Integer>>上调用entrySet方法

c# - 递归执行列表中的函数

mocha.js - PureScript FFI 转 mocha

haskell - 记录字段的类型不一致

string - Haskell 的 `readFile` 是否将整个文件内容读入内存?

haskell - 构造函数模式匹配haskell

haskell - 类型系统的语法是如何读取的?

javascript - 为什么每次调用函数时都不会重置变量

javascript - 如何从外部模块生成 Pux 操作?

purescript - 如何将新字段添加到 Purescript 中的对象