我是函数式编程的新手,我决定用 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/