Tentang komposisi fungsi dalam JavaScript

Mari berfantasi tentang topik komposisi fungsional, serta mengklarifikasi makna operator komposisi / pipa.


TL; DR
Menulis fungsi seperti bos:
gambar
Implementasi populer compose - ketika dipanggil, mereka menciptakan fungsi baru dan baru berdasarkan rekursi, apa kerugiannya dan bagaimana menyiasatinya.


Anda dapat mempertimbangkan fungsi tulis sebagai fungsi murni yang hanya bergantung pada argumen. Jadi, menyusun fungsi yang sama dalam urutan yang sama, kita harus mendapatkan fungsi yang identik, tetapi di dunia JavaScript tidak demikian. Setiap panggilan untuk menulis - mengembalikan fungsi baru, ini mengarah pada penciptaan lebih banyak fungsi baru di memori, serta masalah memoisasi mereka, perbandingan dan debugging.
Sesuatu harus dilakukan.


Motivasi


  • Dapatkan identitas asosiatif:

Sangat disarankan untuk tidak membuat objek baru dan menggunakan kembali hasil fungsi penulisan sebelumnya. Salah satu masalah pengembang React adalah penerapan shallowCompare, yang berfungsi dengan hasil komposisi fungsi. Misalnya, komposisi pengiriman acara dengan panggilan balik akan selalu membuat fungsi baru, yang akan mengarah pada pembaruan nilai properti.


Implementasi populer komposisi tidak memiliki identitas nilai balik.
Secara parsial, masalah identitas lagu dapat diatasi dengan memoise argumen. Namun, pertanyaan tentang identitas asosiatif tetap:


 import {memoize} from 'ramda' const memoCompose = memoize(compose) memoCompose(a, b) === memoCompose(a, b) // ,   memoCompose(memoCompose(a, b), c) === memoCompose(a, memoCompose(b, c)) // ,        

  • Sederhanakan komposisi debug:

Tentu saja, menggunakan fungsi ketuk membantu dalam fungsi debugging yang memiliki ekspresi tunggal di tubuh. Namun, diinginkan untuk memiliki setumpuk panggilan serata mungkin untuk debugging.


  • Singkirkan overhead terkait rekursi:

Implementasi rekursif dari komposisi fungsional memiliki overhead, menciptakan elemen-elemen baru dalam tumpukan panggilan. Saat Anda memanggil komposisi 5 fungsi atau lebih, ini jelas terlihat. Dan dengan menggunakan pendekatan fungsional dalam pengembangan, perlu untuk membangun komposisi dari banyak fungsi yang sangat sederhana.


Solusi


Buat monoid (atau semigroupoid dengan dukungan untuk spesifikasi kategori) dalam hal tanah fantasi:


 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 } 

Gunakan kasing


  • Berguna dalam memoisasi komposisi komposit ketika bekerja dengan editor. Misalnya untuk redux / mapStateToProps dan
    pilih kembali.
  • Komposisi lensa.

Anda dapat membuat dan menggunakan kembali lensa yang sederajat dengan fokus pada tempat yang sama.


  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) ) 

  • Memoized callbacks, dengan kemampuan untuk menyusun hingga fungsi akhir pengiriman suatu acara.

Dalam contoh ini, panggilan balik yang sama akan dikirim ke item daftar.


 ```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, }) ``` 

  • Komposisi komponen React yang malas tanpa membuat komponen tingkat tinggi. Dalam hal ini, komposisi malas akan menciutkan susunan fungsi, tanpa membuat penutupan tambahan. Pertanyaan ini mengkhawatirkan banyak pengembang yang menggunakan pustaka komposisi ulang.


     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'}), ) 

  • Monad dan aplikatif (dalam hal tanah fantasi) dengan kesetaraan yang ketat melalui caching hasil komposisi. Jika Anda mengakses kamus objek yang sebelumnya dibuat di dalam konstruktor tipe, Anda mendapatkan yang berikut:



  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) //   ,      //           

Saya telah menggunakan pendekatan ini untuk waktu yang lama, saya merancang repositori di sini .
Paket NPM: npm i lazy-compose .


Sangat menarik untuk mendapatkan umpan balik tentang penutupan cache fungsi yang dibuat dalam runtime tergantung pada penutupan.


UPD
Saya melihat pertanyaan yang jelas:
Ya, Anda dapat mengganti Peta dengan WeakMap.
Ya, Anda harus memungkinkan untuk menghubungkan cache pihak ketiga sebagai middleware.
Anda seharusnya tidak mengatur debat tentang topik cache, tidak ada strategi cache yang ideal.
Mengapa ekor dan kepala, jika semuanya ada dalam daftar - ekor dan kepala, bagian dari implementasi dengan memoisasi berdasarkan bagian komposisi, dan tidak masing-masing fungsi secara terpisah.

Source: https://habr.com/ru/post/id432196/


All Articles