首页 > 解决方案 > 有没有更好的方法来以无点的方式组合它?

问题描述

我一直在工作中遇到这个用例,我觉得必须有一种方法可以在不将 cat 定义为参数的情况下以无点的方式组合 fullName:

    const cats = [
      { name: 'Bob', lastName: 'Ross' },
      { name: 'Frank', lastName: 'Langella' },
    ];
    
    // this bugs me
    const fullName = cat => add(
      prop('name', cat),
      prop('lastName', cat)
    );
    
    const isEqual = curry((a, b) => a === b);
    const isBobRoss = compose(isEqual('BobRoss'), fullName);

编辑:上面的一些助手,以防它有助于理解挑战

/**
 *  compose :: ((a -> b), (b -> c),  ..., (y -> z)) -> a -> z
 */
const compose = (...fns) => (...args) => 
  fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];

/**
 *  curry :: ((a, b, ...) -> c) -> a -> b -> ... -> c
 */
function curry(fn) {
  const arity = fn.length;

  return function $curry(...args) {
    if (args.length < arity) {
      return $curry.bind(null, ...args);
    }

    return fn.call(null, ...args);
  };

/**
 *  add :: a -> b -> a + b
 */
const add = curry((x, y) =>  x + y)

/**
 *  prop :: String -> Object -> a
 */
const prop = curry((p, obj) => obj[p])
}

标签: functional-programming

解决方案


(最初我拒绝为 JS/TS 提供一个特定的函数式编程库,并在一般概念上工作)

请注意,您的 curredprop函数需要一个键并返回一个 reader monad。这将很有用。

假设type Obj = {name:string,lastName:string},那么你的咖喱道具 fn 是(key:'name'|'lastName') => Reader<Obj,string>

您可以使用sequencetype 函数将两个 reader monad 组合成一个,如下所示:

const getNameParts = sequence(prop('name'), prop('lastName')) // Reader<Obj, [string,string]>

然后您可以将 [string,string] 映射为单个字符串,就像在 add 函数中一样

const add = as => as.reduce((acc, item) => acc + item)

因此,如果您可以提升add阅读器 monad 的计算上下文(这里使用建议的 `map: ((a:A)=>B)=>(fa:F<A>)=>F<B>),然后编写这些操作:

const buildNameFromObject = compose(getNameParts, map(add)) // Reader<Obj, string>

我们有它。

const personsName = buildNameFromObject(someObject) // string

fp-ts是一个提供我刚才提到的所有内容的库,并使用该库(更改了一些函数名称以与 fp-ts 的词汇表保持一致),

import { reader, map } from 'fp-ts/lib/Reader'
import { sequenceT } from 'fp-ts/lib/Apply'
import { pipe } from 'fp-ts/lib/pipeable'

const getNameParts = sequenceT(reader)(prop('name'), prop('lastName'))
const buildNameFromObject = pipe(getNameParts, map(add)) // Reader<Obj, string>, which is a fancy way of writing (o:Obj)=>string
buildNameFromObject({name:'Foo', lastName: 'Bar'}) // 'FooBar'

您的“fullName”函数 ( buildNameFromObject) 现在是免费的。


推荐阅读