是否可以组成具有相同签名但具有不同输入/输出的两个函数



基本上,我的问题归结为组成这个签名的几个函数的功能:

(a: A, b: B) => c: C

为了简化,我需要做这样的事情,其中a、b和c都是不同的类型:

const upper = (a, b) => {
const c = a.toString().toUpperCase();
return c;
}
const pad = (a, b) => {
const c = a.toString().padStart(2, '0');
return c;
}
const repeat = (a, b) => {
const c = a.toString().repeat(2);
return c;
}
const format = compose(repeat, pad, upper);

对于使用的记录b,为了简单起见,我在这里省略了它,但假设它必须提供给每个函数,并且每个函数都将接收相同的值作为参数。

到目前为止,我已经能够解决由于参数数量而导致(a, b) => c不可组合的问题,方法是在编写函数之前,对函数进行循环,翻转参数,并对每个函数部分应用b

我使用标准的composeflip实用程序,如下所示:https://medium.com/javascript-scene/curry-and-function-composition-2c208d774983

const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x);

const flip = fn => a => b => fn(b)(a);

const fnArr = [
repeat,
pad,
upper
];
const makeFormatter = functionArray => (a, b) => {
const flippedAndPartiallyApplied = functionArray.map(fn =>
flip(curry(fn))(b)
);
return compose(...flippedAndPartiallyApplied)(a);
};
const formatter = makeFormatter(fnArr);

因此,我已经解决了整个b问题,并且我可以编写几个一元函数。但我的问题仍然是,我的每个函数都是a => c

注意我的每个函数必须如何调用.toString()。我需要这一步,然后才能修改和返回我的值。不幸的是,我需要保持每个函数的真实性,保持相同的签名。

我有没有办法用程序进一步修改每个函数,这样我就不需要修改函数源代码,但可以用适合我的合成管道的方式对它们进行变异,或者这是不可能的?


我正在使用一个API,它通过您提供给API的一个函数运行日历日,该函数可用于将日期单元格呈现到日期选择器UI。这是API供参考,这是一个例子。

我有一个用例,我可以通过应用程序中的各种来源提供多个renderDay功能。我想以某种方式组合这些函数,因为API只接受一个函数,我想以一种优雅的方式来实现它。

如果您有一个(a, b) -> c的列表,并且需要将其带回(a, b) -> c的一个函数,我认为您可以将问题重新表述为定义[c] -> c

例如:

const formatter1 = (a, b) => a.toUpperCase();
const formatter2 = (a, b) => `${a}-${a}`;
const formatters = [formatter1, formatter2];
// Generic combiner of formatters
const applyAll = fs => (a, b) => fs.map(f => f(a, b));
// Reducer logic
const takeFirst = cs => cs[0];
const concat = cs => cs.reduce(
(acc, c) => acc + c + "n", "");
const takeLongest = cs => cs.reduce(
(acc, c) => acc.length > c.length ? acc : c, "" );

// Construct a formatter by composing applyAll and a reducer
const compose = (f, g) => (...args) => f(g(...args));
const formatterA = compose(takeFirst, applyAll(formatters));
const formatterB = compose(concat, applyAll(formatters));
const formatterC = compose(takeLongest, applyAll(formatters));
console.log("Take first:n", formatterA("a", "b"));
console.log("Concat:n", formatterB("a", "b"));
console.log("Take longest:n", formatterC("a", "b"));

您必须定义的是如何组合由不同格式化程序生成的元素

这个问题没有一个正确的答案。最直接的答案是将这些元素连接起来:

const dayFormatter = (date, opts) => (
<>
{allFormatters.map(f => f(date, opts))}
</>
)

如何为不同的renderDay-函数配备一个额外的谓词,"告诉"它是否在某一天isApplicable,然后浏览这些DayRenderer的列表,并选择第一个适用的谓词?

type DayRenderer = (d: Date, mod: DayModifiers) => React.ReactNode
type DayRendererSelector = {
isApplicable: (d: Date, modifiers: DayModifiers) => boolean,
renderDay: DayRenderer
};
function combineDayRenderers(
cases: DayRendererSelector[],
defaultRenderer: DayRenderer
): DayRenderer {
return (d: Date, mod: DayModifiers) => {
for (const { isApplicable, renderDay } of cases) {
if (isApplicable(d, mod)) {
return renderDay(d, mod);
}
}
return defaultRenderer(d, mod);
}
}

如果非要我把它命名为";"图案";,它将是";部分函数的垂直合成";(类似于这里正在讨论的内容(。基本上与switch { case ... case ... case ... default: ... },但有一个病例列表。

最新更新