【问题标题】:Ramda pick for fp-ts options / maybeRamda 选择 fp-ts 选项/也许
【发布时间】:2022-10-24 09:43:43
【问题描述】:

使用fp-ts。我有一个数组选项

const arrayofKeys: Option<Array<K>>, 

和一个记录选项

const record: Option<Record<K,V>>

我想选择 Ks 与数组相交的记录的 Vs 并将结果粘贴到选项中。

在 ramda 中:R.pick(arrayOfKeys, record)

我如何使用 fp-ts 或 fp-ts 生态系统中的其他软件包解决这个问题?

【问题讨论】:

    标签: ramda.js fp-ts monocle-ts


    【解决方案1】:

    我个人会避免使用 Ramda 等人,因为根据我的经验,它们的类型不是很好。这是一个纯粹的 fp-ts 方法(Str.fromNumber 来自 fp-ts-std,被简单替换):

    declare const arrayOfKeyNums: Option<Array<number>>
    const arrayOfKeys = pipe(arrayOfKeyNums, O.map(A.map(Str.fromNumber)))
    declare const record: Option<Record<string, number>>
    
    const keyIntersectedVals: O.Option<Array<number>> = pipe(
      sequenceT(O.Apply)(arrayOfKeys, record),
      O.map(([ks, rec]) =>
        pipe(
          rec,
          R.foldMapWithIndex(Str.Ord)(A.getMonoid<number>())((k, v) =>
            A.elem(Str.Eq)(k)(ks) ? [v] : [],
          ),
        ),
      ),
    )
    

    由于需要传递类型类实例,因此有点冗长。从好的方面来说,使用 typeclass 实例意味着可以轻松更新它以支持任何值类型,包括具有任何给定 Eq 的非原始类型。

    下面是在 Haskell 中的 body 可能看起来的比较,其中不需要传递 typeclass 实例:

    keyIntersectedVals :: Maybe [Int]
    keyIntersectedVals = uncurry (M.foldMapWithKey . intersectedToList) <$> sequenceT (mkeys, mmap)
      where intersectedToList ks k v
              | k `elem` ks = [v]
              | otherwise   = []
    

    例如,给定键O.some(["a", "c"]) 和记录O.some({ a: 123, b: 456, c: 789 }),我们得到O.some([123, 789])

    【讨论】:

      【解决方案2】:

      拉姆达的lift升降机对某些值起作用的函数容器那些价值观。所以 lift (pick) 可能会做你想做的事,只要 fp-ts 的 Option 支持 FantasyLand Apply 规范。

      const {of} = folktale.maybe
      const {lift, pick} = R
      
      const keys = of (['k', 'e', 'y', 's'])  // Maybe (['k', 'e', 'y', 's'])
      const record = of ({s: 1, k: 2, y: 3, b: 4, l: 5, u: 6, e: 7}) // Maybe ({s: 1, k: 2, ...})
      
      console .log (lift (pick) (keys, record) .toString())
      <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>
      <script src="//cdnjs.cloudflare.com/ajax/libs/folktale/2.0.0/folktale.min.js"></script>

      【讨论】:

        【解决方案3】:

        这是traverseArray 的一个很好的用例,traverse 的优化版本。您还可以使用“Do notation”和apS 来获得一个非常干净的单子管道。如果这些操作中的任何一个返回None,整个流程将提前终止(这很好!)。

        此外,lookup 是一个非常方便的函数,类似于 Ramda/Lodash 中的get,但它返回一个选项。 Record 和 Array 模块都导出了这个函数的一个版本。

        declare const arrayofKeys: O.Option<Array<string>>
        declare const record: O.Option<Record<string, number>>
        
        export const result: O.Option<ReadonlyArray<number>> = pipe(
          O.Do,
          O.apS('keys', arrayofKeys),
          O.apS('rec', record),
          O.chain(({ keys, rec }) =>
            pipe(
              keys,
              O.traverseArray(key => pipe(rec, R.lookup(key)))
            )
          )
        )
        

        使用的功能:

        1. https://gcanti.github.io/fp-ts/modules/Option.ts.html#do
        2. https://gcanti.github.io/fp-ts/modules/Option.ts.html#aps
        3. https://gcanti.github.io/fp-ts/modules/Option.ts.html#chain
        4. https://gcanti.github.io/fp-ts/modules/Option.ts.html#traversearray
        5. https://gcanti.github.io/fp-ts/modules/Record.ts.html#lookup

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-03-11
          • 1970-01-01
          • 2011-07-26
          • 1970-01-01
          • 2021-09-24
          • 1970-01-01
          • 1970-01-01
          • 2022-01-22
          相关资源
          最近更新 更多