函数式 Programming/Optic 概念采用部分 object 和 returns 一个 "filled in" object 使用镜头和遍历?
Functional Programming/Optic concept that takes a partial object and returns a "filled in" object using lenses and traversals?
(Edit 我正在使用 monocle-ts,但如果 monocle-ts 不可能(因为作者甚至说它只是Scala 的原始 Monocle),但如果在任何语言的另一个光学包中有一些东西,我愿意将这些想法移植到 TypeScript。)
假设我有一个助手类型 Partial<A>
,它代表一条记录,该记录有一些或全部,但没有 non-members,类型为 A
。 (所以 if A = { foo: number, bar: string }
then Partial<A> = { foo?: number, bar?: string }
)(Edit 这是 Typescript 的 built-in 部分实用程序类型。)
我从
开始
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
应用到所有 children 和 composedTraversal.modify(fn)(partialState)
这将产生一个新的 PartialState
fn
应用于所有 partialxs
.
是否有一些概念可以让我“扩展”或“转换”这种遍历到不同的东西,这样我就可以组合镜头和遍历并使用 fillInTheGaps
这样我就可以传入 PartialState
并取回 State
?
忽略我的语法是 TypeScript,我添加了 monocle-scala 标签,因为如果这个概念存在,我想它在 Monocle 库中,我可以将这些知识转化为我正在使用的库。
编辑 引发这个问题的问题是我在 Redux 应用程序中有一个表单输入,用户在其中输入数据但大多数都不是必需的。输入在 compile-time 处未知(它们是从 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>
和 returns 一个 Partial<State>
但我希望将它们组合成一个函数,该函数接受部分和 returns 整体。
我显然可以只写 const filler: (Partial<State>):State = myLens.compose(myTraversal).modify(fillInTheGaps)
,然后在上面扔一个 //@ts-ignore
,并且知道它会起作用,但这似乎,嗯,脆弱。
我想,你可能想要的是 Polymorphic Traversal or PTraversal<S, T, A, B>
.
A Traversal<S, A>
说,“如果我有一个函数A => A
,我可以使用modify
得到一个函数S => S
,它使用原始函数修改所有的
A
出现在 S
".
中
相比之下,一个PTraversal<S, T, A, B>
说,“如果我有一个函数A => B
,我可以使用modify
来获得一个函数S => T
”,这将转换所有S
到 B
中的 A
,生成 T
.
助记,PTraversal
的类型参数为:
S
PTraversal
的来源
T
PTraversal
的“修改”来源
A
目标PTraversal
B
PTraversal
的“修改”目标
PTraversal
很有用,因为它们可以让您编写如下内容:
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
:所以你很遗憾必须编写很多光学库代码以支持这一点。
(Edit 我正在使用 monocle-ts,但如果 monocle-ts 不可能(因为作者甚至说它只是Scala 的原始 Monocle),但如果在任何语言的另一个光学包中有一些东西,我愿意将这些想法移植到 TypeScript。)
假设我有一个助手类型 Partial<A>
,它代表一条记录,该记录有一些或全部,但没有 non-members,类型为 A
。 (所以 if A = { foo: number, bar: string }
then Partial<A> = { foo?: number, bar?: string }
)(Edit 这是 Typescript 的 built-in 部分实用程序类型。)
我从
开始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
应用到所有 children 和 composedTraversal.modify(fn)(partialState)
这将产生一个新的 PartialState
fn
应用于所有 partialxs
.
是否有一些概念可以让我“扩展”或“转换”这种遍历到不同的东西,这样我就可以组合镜头和遍历并使用 fillInTheGaps
这样我就可以传入 PartialState
并取回 State
?
忽略我的语法是 TypeScript,我添加了 monocle-scala 标签,因为如果这个概念存在,我想它在 Monocle 库中,我可以将这些知识转化为我正在使用的库。
编辑 引发这个问题的问题是我在 Redux 应用程序中有一个表单输入,用户在其中输入数据但大多数都不是必需的。输入在 compile-time 处未知(它们是从 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>
和 returns 一个 Partial<State>
但我希望将它们组合成一个函数,该函数接受部分和 returns 整体。
我显然可以只写 const filler: (Partial<State>):State = myLens.compose(myTraversal).modify(fillInTheGaps)
,然后在上面扔一个 //@ts-ignore
,并且知道它会起作用,但这似乎,嗯,脆弱。
我想,你可能想要的是 Polymorphic Traversal or PTraversal<S, T, A, B>
.
A Traversal<S, A>
说,“如果我有一个函数A => A
,我可以使用modify
得到一个函数S => S
,它使用原始函数修改所有的
A
出现在 S
".
相比之下,一个PTraversal<S, T, A, B>
说,“如果我有一个函数A => B
,我可以使用modify
来获得一个函数S => T
”,这将转换所有S
到 B
中的 A
,生成 T
.
助记,PTraversal
的类型参数为:
S
PTraversal
的来源
T
PTraversal
的“修改”来源
A
目标PTraversal
B
PTraversal
的“修改”目标
PTraversal
很有用,因为它们可以让您编写如下内容:
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
:所以你很遗憾必须编写很多光学库代码以支持这一点。