函数式编程/光学概念,它采用部分对象并使用镜头和遍历返回“填充”对象?

Posted

技术标签:

【中文标题】函数式编程/光学概念,它采用部分对象并使用镜头和遍历返回“填充”对象?【英文标题】:Functional Programming/Optic concept that takes a partial object and returns a "filled in" object using lenses and traversals? 【发布时间】:2020-12-24 04:36:59 【问题描述】:

编辑我正在使用monocle-ts,但如果使用 monocle-ts 是不可能的(因为作者甚至说它只是 Scala 原始 Monocle 的部分端口)但是如果有是任何语言的另一个光学包中的东西,我愿意将这些想法移植到 TypeScript。)

假设我有一个帮助器类型Partial<A>,这样它就代表了一个记录,该记录包含A 类型的部分或全部但没有非成员。 (所以如果A = foo: number, bar: string 那么Partial<A> = foo?: number, bar?: string )(编辑这是 Typescript 的内置 Partial 实用程序类型。)

我开始

interface Parent 
  xs: Child[]


interface PartialParent 
  partialxs: Partial<Child>[]


declare function fillInTheGaps(x: Partial<Child>):Child

假设我已经组合了一个镜头和遍历组合 (composedTraversal),以便它从 PartialState 聚焦到 partialxs,然后作为一个数组遍历它。这将是Traversal&lt;PartialState, Partial&lt;Child&gt;&gt;

假设我有一个declare const fn = (x:Partial&lt;Child&gt;):Partial&lt;Child&gt;,那么我可以将fn 应用于所有composedTraversal.modify(fn)(partialState) 的孩子,这将产生一个新的PartialStatefn 应用于所有partialxs

是否有一些概念可以让我“扩大”或“转换”这个遍历到不同的东西,这样我就可以组成镜头和遍历并使用fillInTheGaps,这样我就可以传入PartialState并取回@ 987654340@?

忽略我的语法是 TypeScript,我添加了 monocle-scala 标签,因为如果这个概念存在,我想它在 Monocle 库中,我可以将这些知识翻译到我正在使用的库中。

编辑 激发这个问题的问题是我在 Redux 应用程序中有一个表单输入,用户输入数据但大多数不是必需的。 INPUT 在编译时未知(它们从 RESTful API 查询中重试),因此我无法将模型表示为

interface Model 
  foo?: string[]
  bar?: string[]

相反,它表示为

interface Model 
  [index:string]: string[]

我还可以从 RESTful 服务器获取默认模型。所以我将它们建模为Parent(来自服务器)和Partial&lt;Parent&gt;(代表用户在应用程序中的输入)。

在进行一些计算之前,我需要为缺少的道具折叠默认值。这是我上面引用的fillInTheGaps 函数。

希望通过我的代码中的类型来强制执行此操作,并且由于我已经编写了很多光学元件,因此可以重用其中的一些。实际上,我编写了一个镜头和遍历来对这些数据执行其他操作。 myLens.compose(myTraversal).modify(fn) 接受 Partial&lt;State&gt; 并返回 Partial&lt;State&gt; 但我希望将这些组合成一个函数,该函数接受部分并返回整体。

我显然可以只写const filler: (Partial&lt;State&gt;):State = myLens.compose(myTraversal).modify(fillInTheGaps),然后在上面扔一个//@ts-ignore,我知道它会起作用,但这似乎,呃,很脆弱。

【问题讨论】:

我知道它不可能是 Iso,因为 Iso 应该代表一个函数及其逆函数,并且您无法从 state 中取回部分状态(您可以得到 a 部分状态返回,但不是 之前的部分状态)。 "假设我有一个辅助类型 Partial&lt;A&gt;" - 那只是 the builtin one,不是吗? 什么是LensTraversal,它们是如何定义的?你在用什么图书馆?请链接其文档。 PartialChild 是不是你的意思是PartialState 的错字?后者是如何定义的? 是的,内置的。我试图让那些不使用 TS(以防 Scala 或 Haskell 的人停下来)但不必使用伪代码的人更清楚我的问题。我将通过指向我正在使用的库的链接来充实我的问题(它是 monocle-ts,Scala 的 Monocle 库的部分端口) 【参考方案1】:

我想,你可能想要的是Polymorphic Traversal or PTraversal&lt;S, T, A, B&gt;

Traversal&lt;S, A&gt; 说,“如果我有一个函数 A =&gt; A,我可以使用 modify 获得一个函数 S =&gt; S,它使用原始函数修改所有 As 出现在S"中。

相比之下,PTraversal&lt;S, T, A, B&gt; 表示,“如果我有一个函数A =&gt; B,我可以使用modify 来获得一个函数S =&gt; T”,这将转换S 中的所有As到B,生成T

助记符PTraversal的类型参数为:

SPTraversal的来源 T PTraversal 的“修改”源 APTraversal 的目标 B PTraversal 的“修改”目标

PTraversals 很有用,因为它们让您可以编写如下内容:

PTraversal<Array<A>, Array<B>, A, B>

让您在更改元素类型时遍历Array


在您的具体情况下,您提到有两个功能:

declare function fillInTheGaps(x: Partial<Child>):Child
declare function fn(x: Partial<Child>):Partial<Child>

这些可以组合在一起产生一个单一的功能:

function transformAndFill(x: Partial<Child>): Child 
   return fillInTheGaps(fn(x)); 

然后你需要写一个PTraversal&lt;PartialState, State, Partial&lt;Child&gt;, Child&gt;

这将支持使用 Lens 组合以创建新的 PTraversal,其方式与 Traversal 所做的大致相同。

应该是可行的,我认为从您的问题来看,如果您可以将PartialState 中的每个Partial&lt;Child&gt; 转换为Child,您应该可以制作State


PTraversal 存在于 Monocle(Scala 库)中,但不幸的是,它看起来不像是在 monocle-ts 中出现:所以很遗憾,您必须编写大量光学库代码才能支持它.

【讨论】:

如果我误解了您的问题,请提前道歉。我对 Scala 库 Monocle 比较熟悉,但我几乎没有 TypeScript 的经验。 我认为这正是我所需要的。也是幸运的。我终于可以回馈我从图书馆取来的东西了:) 怎么样?

以上是关于函数式编程/光学概念,它采用部分对象并使用镜头和遍历返回“填充”对象?的主要内容,如果未能解决你的问题,请参考以下文章

函数式编程方式往往令人生畏,初学者只需要理解两个概念即可

DxO Analyzer 光学模块鱼眼镜头MTF测试

为什么开发人员喜欢函数式编程?

学会JavaScript函数式编程(第1部分)

函数式编程思想:耦合和组合,第2部分

学会使用函数式编程的程序员(第1部分)