函数式编程/光学概念,它采用部分对象并使用镜头和遍历返回“填充”对象?
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<PartialState, Partial<Child>>
。
假设我有一个declare const fn = (x:Partial<Child>):Partial<Child>
,那么我可以将fn
应用于所有composedTraversal.modify(fn)(partialState)
的孩子,这将产生一个新的PartialState
,fn
应用于所有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<Parent>
(代表用户在应用程序中的输入)。
在进行一些计算之前,我需要为缺少的道具折叠默认值。这是我上面引用的fillInTheGaps
函数。
希望通过我的代码中的类型来强制执行此操作,并且由于我已经编写了很多光学元件,因此可以重用其中的一些。实际上,我编写了一个镜头和遍历来对这些数据执行其他操作。 myLens.compose(myTraversal).modify(fn)
接受 Partial<State>
并返回 Partial<State>
但我希望将这些组合成一个函数,该函数接受部分并返回整体。
我显然可以只写const filler: (Partial<State>):State = myLens.compose(myTraversal).modify(fillInTheGaps)
,然后在上面扔一个//@ts-ignore
,我知道它会起作用,但这似乎,呃,很脆弱。
【问题讨论】:
我知道它不可能是 Iso,因为 Iso 应该代表一个函数及其逆函数,并且您无法从 state 中取回部分状态(您可以得到 a 部分状态返回,但不是 之前的部分状态)。 "假设我有一个辅助类型Partial<A>
" - 那只是 the builtin one,不是吗?
什么是Lens
和Traversal
,它们是如何定义的?你在用什么图书馆?请链接其文档。
PartialChild
是不是你的意思是PartialState
的错字?后者是如何定义的?
是的,内置的。我试图让那些不使用 TS(以防 Scala 或 Haskell 的人停下来)但不必使用伪代码的人更清楚我的问题。我将通过指向我正在使用的库的链接来充实我的问题(它是 monocle-ts,Scala 的 Monocle 库的部分端口)
【参考方案1】:
我想,你可能想要的是Polymorphic Traversal or PTraversal<S, T, A, B>
。
Traversal<S, A>
说,“如果我有一个函数 A => A
,我可以使用 modify
获得一个函数 S => S
,它使用原始函数修改所有
A
s 出现在S
"中。
相比之下,PTraversal<S, T, A, B>
表示,“如果我有一个函数A => B
,我可以使用modify
来获得一个函数S => T
”,这将转换S
中的所有A
s到B
,生成T
。
助记符PTraversal
的类型参数为:
S
PTraversal
的来源
T
PTraversal
的“修改”源
A
PTraversal
的目标
B
PTraversal
的“修改”目标
PTraversal
s 很有用,因为它们让您可以编写如下内容:
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<PartialState, State, Partial<Child>, Child>
。
这将支持使用 Lens
组合以创建新的 PTraversal
,其方式与 Traversal
所做的大致相同。
这应该是可行的,我认为从您的问题来看,如果您可以将PartialState
中的每个Partial<Child>
转换为Child
,您应该可以制作State
。
PTraversal
存在于 Monocle(Scala 库)中,但不幸的是,它看起来不像是在 monocle-ts
中出现:所以很遗憾,您必须编写大量光学库代码才能支持它.
【讨论】:
如果我误解了您的问题,请提前道歉。我对 Scala 库 Monocle 比较熟悉,但我几乎没有 TypeScript 的经验。 我认为这正是我所需要的。也是幸运的。我终于可以回馈我从图书馆取来的东西了:) 怎么样?以上是关于函数式编程/光学概念,它采用部分对象并使用镜头和遍历返回“填充”对象?的主要内容,如果未能解决你的问题,请参考以下文章