Vamos fantasiar sobre o tema da composição funcional, bem como esclarecer o significado do operador de composição / pipeline.
TL; DR
Componha funções como um chefe:

Implementações populares de compose
- quando chamadas, elas criam novas e novas funções baseadas na recursão, quais são as desvantagens e como contornar isso.
Você pode considerar a função de composição como uma função pura que depende apenas dos argumentos. Assim, compondo as mesmas funções na mesma ordem, devemos obter uma função idêntica, mas no mundo JavaScript não é assim. Qualquer chamada para compor - retorna uma nova função, isso leva à criação de mais e mais novas funções na memória, bem como a problemas de memorização, comparação e depuração.
Algo deve ser feito.
Motivação
- Obtenha identidade associativa:
É altamente recomendável não criar novos objetos e reutilizar os resultados anteriores da função de composição. Um dos problemas do desenvolvedor do React é a implementação do shallowCompare, que funciona com o resultado da composição da função. Por exemplo, a composição do envio de um evento com um retorno de chamada sempre criará uma nova função, o que levará a uma atualização do valor da propriedade.
Implementações populares de uma composição não têm identidade de valor retornado.
Parcialmente, a questão da identidade da música pode ser resolvida memorizando os argumentos. No entanto, a questão da identidade associativa permanece:
import {memoize} from 'ramda' const memoCompose = memoize(compose) memoCompose(a, b) === memoCompose(a, b)
- Simplifique a depuração da composição:
Obviamente, o uso das funções de toque ajuda na depuração de funções que têm uma única expressão no corpo. No entanto, é desejável ter uma pilha de chamadas o mais plana possível para depuração.
- Livre-se das despesas gerais relacionadas à recursão:
A implementação recursiva da composição funcional possui uma sobrecarga, criando novos elementos na pilha de chamadas. Quando você chama uma composição de 5 ou mais funções, isso é claramente perceptível. E usando abordagens funcionais no desenvolvimento, é necessário construir composições a partir de dezenas de funções muito simples.
Solução
Faça um monóide (ou um semigroupoide com suporte para a especificação da categoria) em termos de terra da fantasia:
import compose, {identity} from 'lazy-compose' import {add} from 'ramda' const a = add(1) const b = add(2) const c = add(3) test('Laws', () => { compose(a, compose(b, c)) === compose(compose(a, b), c) // compose(a, identity) === a //right identity compose(identity, a) === a //left identity }
Casos de uso
- Útil para memorizar composições compostas ao trabalhar com editores. Por exemplo, para redux / mapStateToProps e
selecione novamente. - A composição das lentes.
Você pode criar e reutilizar lentes estritamente equivalentes, focadas no mesmo local.
import {lensProp, memoize} from 'ramda' import compose from 'lazy-compose' const constantLens = memoize(lensProp) const lensA = constantLens('a') const lensB = constantLens('b') const lensC = constantLens('c') const lensAB = compose(lensB, lensA) console.log( compose(lensC, lensAB) === compose(lensC, lensB, lensA) )
- Retornos de chamada memorizados, com a capacidade de compor até a função final de envio de um evento.
Neste exemplo, o mesmo retorno de chamada será enviado para os itens da lista.
```jsx import {compose, constant} from './src/lazyCompose' // constant - returns the same memoized function for each argrum // just like React.useCallback import {compose, constant} from 'lazy-compose' const List = ({dispatch, data}) => data.map( id => <Button key={id} onClick={compose(dispatch, makeAction, contsant(id))} /> ) const Button = React.memo( props => <button {...props} /> ) const makeAction = payload => ({ type: 'onClick', payload, }) ```
Composição lenta dos componentes React sem criar componentes de ordem superior. Nesse caso, a composição lenta reduzirá a matriz de funções, sem criar fechamentos adicionais. Esta pergunta preocupa muitos desenvolvedores que usam a biblioteca de recomposição.
import {memoize, mergeRight} from 'ramda' import {constant, compose} from './src/lazyCompose' const defaultProps = memoize(mergeRight) const withState = memoize( defaultState => props => { const [state, setState] = React.useState(defaultState) return {...props, state, setState} } ) const Component = ({value, label, ...props)) => <label {...props}>{label} : {value}</label> const withCounter = compose( ({setState, state, ...props}) => ({ ...props value: state, onClick: compose(setState, constant(state + 1)) }), withState(0), ) const Counter = compose( Component, withCounter, defaultProps({label: 'Clicks'}), )
Mônadas e aplicativos (em termos de terra da fantasia) com equivalência estrita através do armazenamento em cache do resultado da composição. Se você acessar o dicionário de objetos criados anteriormente dentro do construtor de tipos, obterá o seguinte:
type Info = { age?: number } type User = { info?: Info } const mayBeAge = LazyMaybe<Info>.of(identity) .map(getAge) .contramap(getInfo) const age = mayBeAge.ap(data) const maybeAge2 = LazyMaybe<User>.of(compose(getAge, getInfo)) console.log(maybeAge === maybeAge2) // , //
Eu uso essa abordagem há muito tempo, criei o repositório aqui .
Pacote NPM: npm i lazy-compose
.
É interessante receber feedback sobre o fechamento do cache de funções criadas em tempo de execução, dependendo dos fechamentos.
UPD
Prevejo perguntas óbvias:
Sim, você pode substituir o mapa pelo WeakMap.
Sim, você precisa possibilitar a conexão de um cache de terceiros como middleware.
Você não deve organizar um debate sobre o tópico caches, pois não existe uma estratégia ideal de cache.
Por que cauda e cabeça, se tudo está na lista - cauda e cabeça, parte da implementação com memorização baseada em partes da composição, e não cada função separadamente.