功能上的思考。在Haskell / Purescript中构建一个新数组 [英] Thinking Functionally. Building a New Array in Haskell / Purescript

查看:124
本文介绍了功能上的思考。在Haskell / Purescript中构建一个新数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是功能编程的新手,我决定在Purescript中构建一个应用程序。我已经打到了我的第一个障碍,我不知道该如何思考这个概念。

就像在功能上思考这个问题一样。


我有一个数据列表。具体而言,就像

  [{a :: String,b :: String,c :: String}] 

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

,我会有一个函数

  buildElements :: forall p i。 MyRecordObject  - >数组(HTML pi)

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

所以,像这样:

  buildElements :: forall p i。 MyRecordObject  - > Eff(Array(HTML pi))

我的第一个想法是模糊地创建一个类似于

  take $ length xs $ repeat ARecordObject 

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

然后我发现这个函数:

  forEach :: forall e a。数组a  - > (a  - > Eff e Unit) - > Eff单位

看起来几乎完美!我得到一个数组,我给它一个函数,以某种方式将记录中的属性赋值给这个新的数组... ...但是,等等...我再次想到了非功能性的问题。



我在这里真的有点不知所措。基本上,我想创建类似于< 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记录语法记法)

解方案


我的第一个想法是模糊地创建一个类似于
$ b

  take $ length xs $ repeat ARecordObject 

然后将记录映射到该列表上,但是我没有'不知道如何将
翻译成代码。这似乎是错误的,因为我的计划
涉及改变 ARecordObject 的状态,这是一个不允许的。




函数式程序员不仅避免了变异,因为它是一个不可否认的(实际上,许多函数式程序仔细使用受控剂量的可变性) - 我们这样做是因为它产生更安全,简单的代码。



换句话说:你正在考虑我所谓的alloc-init模式,其中你创建某种空值,然后去关于计算它的属性。原谅我的强大,但这是一个从根本上破坏编程模型,从手动内存管理的时代遗留下来;使用它的代码将永远不会安全,依赖于它的抽象将永远是泄漏的。这个成语不适合任何比C更高级别的语言,然而,如果我每次看到类似这样的代码时都有一英镑......

  var foo = new Foo(); 
foo.Bar = new Bar();
foo.Bar.Baz = new Baz();

...我会成为一个有钱人(不适用)。默认情况下,应该在创建对象之后创建对象:

  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 - 只是简单,安全,纯粹的功能性代码。


I'm new to functional programming, and I've decided to build an app in Purescript. I've hit my first hurdle, and I'm not sure how to think about this conceptually.

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

I have a list of data. Specifically, something like

[ {a :: String, b :: String, c :: String} ]

I would like to create a list of Html (which is a purescript-halogen type) by using the record provided (with a list of the above types).

So, I would have a function

buildElements :: forall p i. MyRecordObject -> Array (HTML p i)

Now, I think I'm going to need to give this function result type a Monad computational context (purescript Eff is like Haskell IO)

So something like:

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

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.

So then I found this function:

forEach :: forall e a. Array a -> (a -> Eff e Unit) -> Eff Unit

which looks almost perfect! I get an array, I give it a function that somehow assigns the properties in the record to this new array...but no, wait...I'm thinking non-functionally again.

I'm really at a bit of a loss here. Basically, I want to create something like a list of <li></li> elements, where I assign properties to each item.

E.g

I'm provided a record with:

[ { id: "id1", name: "name1", class: "class1", content: "content1" }
, { id: "id2", name: "name2", class: "class2", content: "content2" } ]

And I would like a function foo that returns an array:

[ 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 ] ]

where rec is the name of the recordObject (and obviously the two arrays are not identical, but actually mapped over the initial record).

(the dot syntax is a purescript record syntax notation similar to standard getter/setter notation)

解决方案

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.

Functional programmers don't just avoid mutation because it's a no-no (indeed, many functional programs make careful use of a controlled dose of mutability) - we do it because it produces safer, simpler code.

To wit: You're thinking in what I call "alloc-init mode", wherein you create some sort of "empty" value and then go about calculating its properties. Forgive my vehemency, but that's a fundamentally broken programming model, left over from the days of manual memory management; code which uses it will never be safe and abstractions relying on it will forever be leaky. The idiom doesn't fit into any language that's higher-level than C, and yet, if I had a pound for every time I see code like this...

var foo = new Foo();
foo.Bar = new Bar();
foo.Bar.Baz = new Baz();

...I would be a rich man (na na na). The default should be to create objects after you know what they're going to look like:

var foo = new Foo(new Bar(new Baz()));

This is simpler - you're just calculating a value, rather than reaching into the memory referenced by a pointer to update its contents - and more importantly it's safer because the type-checker ensures that you haven't forgotten a property and it allows you to make Foo immutable. The cleanest imperative code is functional code - you should only be imperative where necessary for performance (or when the language forces your hand).


Anyway, rant over. The point is that you're making life harder for yourself than necessary by thinking imperatively. Just write a function that calculates a single <li> from a single object...

toLi :: MyRecord -> HTML
toLi x = li [ id_ x.id, name_ x.name, class_ x.class ] [ text x.content ]

... (note that I'm not somehow creating an "empty" li and then populating its values), and then map it over your input list.

toLis :: [MyRecord] -> [HTML]
toLis = map toLi

This is how I'd do it in JS, too, even though I'm not required to by the language. No side-effects, no mutation, no need for Eff - just simple, safe, purely functional code.

这篇关于功能上的思考。在Haskell / Purescript中构建一个新数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆