Explicación de la programación funcional en JavaScript: aplicación parcial y currificación

Introducción

Índice
  1. Introducción
  • Ejemplo sin aplicación parcial
  • Aplicación parcial: fijación de argumentos
    1. Usando Ramda
    2. Aplicación parcial manual
    3. Generalizando
    4. Una consideración de diseño
  • Currying y aplicación parcial conveniente
  • Conclusión
  • Con la adopción de la biblioteca Redux de JavaScript, la extensión de sintaxis y la cadena de herramientas Reason, y el marco de trabajo Cycle de JavaScript, la programación funcional con JavaScript está adquiriendo cada vez mayor relevancia. Dos ideas importantes con raíces en el pensamiento funcional son la currificación, que transforma una función de múltiples argumentos en una serie de llamadas a funciones, y la aplicación parcial, que fija el valor de algunos de los argumentos de una función sin evaluar completamente la función. En este artículo, exploraremos algunos ejemplos de estas ideas en acción, así como también identificaremos algunos lugares en los que aparecen y que podrían sorprenderte.

    Después de leer esto, podrás:

    • Defina la aplicación parcial y la curación y explique la diferencia entre ambas.
    • Utilice la aplicación parcial para fijar argumentos a una función.
    • Las funciones de Curry facilitan la aplicación parcial.
    • Funciones de diseño que faciliten la aplicación parcial.

    Ejemplo sin aplicación parcial

    Como ocurre con muchos patrones, la aplicación parcial es más fácil de entender con el contexto.

    Considere esta buildUrifunción:

    function buildUri (scheme, domain, path) {  return `${scheme}://${domain}/${path}`}

    Llamamos así:

    buildUri('https', 'twitter.com', 'favicon.ico')

    Esto produce la cadena https://twitter.com/favicon.ico.

    Esta es una función útil si estás creando muchas URL. Sin embargo, si trabajas principalmente en la web, rara vez usarás algo schemedistinto de httpo https:

    const twitterFavicon = buildUri('https', 'twitter.com', 'favicon.ico')const googleHome = buildUri('https', 'google.com', '')

    Observe el punto en común entre estas dos líneas: ambas pasan httpscomo argumento inicial. Preferiríamos eliminar la repetición y escribir algo más parecido a esto:

    const twitterFavicon = buildHttpsUri('twitter.com', 'favicon.ico')

    Hay un par de formas de hacer esto. Veamos cómo lograrlo con una aplicación parcial.

    Aplicación parcial: fijación de argumentos

    Acordamos que, en lugar de:

    const twitterFavicon = buildUri('https', 'twitter.com', 'favicon.ico')

    Preferiríamos escribir:

    const twitterFavicon =  buildHttpsUri('twitter.com', 'favicon.ico')

    Conceptualmente, buildHttpsUrihace exactamente lo mismo que buildUri, pero con un valor fijo para su schemeargumento.

    Podríamos implementarlo buildHttpsUridirectamente de la siguiente manera:

    function buildHttpsUri (domain, path) {  return `https://${domain}/${path}`}

    Esto hará lo que queremos, pero aún no ha resuelto nuestro problema por completo. Estamos duplicando buildUri, pero codificando de forma rígida httpscomo su schemeargumento.

    La aplicación parcial nos permite hacer esto, pero aprovechando el código que ya tenemos en buildUri. Primero, veremos cómo hacer esto usando una biblioteca de utilidades funcional llamada Ramda. Luego, intentaremos hacerlo a mano.

    Usando Ramda

    Usando Ramda, la aplicación parcial se ve así:

    // Assuming we're in a node environmentconst R = require('ramda')// R.partial returns a new function (!)const buildHttpsUri = R.partial(buildUri, ['https'])

    Después de esto, podemos hacer:

    const twitterFavicon = buildHttpsUri('twitter.com', 'favicon.ico')

    Analicemos lo que pasó aquí:

    • Llamamos a partialla función de Ramda y pasamos dos argumentos: primero, una función, llamada buildUri, y segundo, una matriz que contiene un "https"valor.
    • Luego, Ramda devuelve una nueva función, que se comporta como buildUri, pero con "https"como primer argumento.

    Pasar más valores en la matriz corrige más argumentos:

    // Bind `https` as first arg to `buildUri`, and `twitter.com` as secondconst twitterPath = R.partial(buildUri, ['https', 'twitter.com'])// Outputs: `https://twitter.com/favicon.ico`const twitterFavicon = twitterPath('favicon.ico')

    Esto nos permite reutilizar el código general que hemos escrito en otro lugar configurándolo para casos especiales.

    Aplicación parcial manual

    En la práctica, utilizará utilidades como partialcuando necesite utilizar una aplicación parcial. Pero, a modo de ejemplo, intentemos hacerlo nosotros mismos.

    Veamos primero el fragmento y luego lo diseccionemos.

    // Line 0function fixUriScheme (scheme) {  console.log(scheme)  return function buildUriWithProvidedScheme (domain, path) {    return buildUri(scheme, domain, path)  }}// Line 1const buildHttpsUri = fixUriScheme('https')// Outputs: `https://twitter.com/favicon.ico`const twitterFavicon = buildHttpsUri('twitter.com', 'favicon.ico')

    Vamos a desglosar lo que pasó.

    • En la línea 0, definimos una función llamada fixUriScheme. Esta función acepta un scheme, y devuelve otra función.
    • En la línea 1, guardamos el resultado de la llamada fixUriScheme('https')en una variable llamada buildHttpsUri, que se comporta exactamente igual que la versión que construimos con Ramda.

    Nuestra función fixUriSchemeacepta un valor y devuelve una función. Recuerde que esto la convierte en una función de orden superior o HOF. Esta función devuelta solo acepta dos argumentos: domainy path.

    Tenga en cuenta que, cuando llamamos a esta función devuelta, solo pasamos explícitamente domainy path, pero recuerda lo que schemepasamos en la línea 1. Esto se debe a que la función interna, buildUriWithProvidedScheme, tiene acceso a todos los valores en el alcance de su función principal, incluso después de que la función principal haya regresado. Esto es lo que llamamos cierre.

    Esto se generaliza. Cada vez que una función devuelve otra función, la función devuelta tiene acceso a cualquier variable inicializada dentro del ámbito de la función principal. Este es un buen ejemplo de cómo usar el cierre para encapsular el estado.

    Podríamos hacer algo similar usando un objeto con métodos:

    class UriBuilder {  constructor (scheme) {    this.scheme = scheme  }  buildUri (domain, path) {    return `${this.scheme}://${domain}/${path}`  }}const httpsUriBuilder = new UriBuilder('https')const twitterFavicon = httpsUriBuilder.buildUri('twitter.com', 'favicon.ico')

    En este ejemplo, configuramos cada instancia de la UriBuilderclase con un scheme. Luego, podemos llamar al método, que combina el y buildUrideseado por el usuario con nuestro .preconfigurado para producir la URL deseada.domainpathscheme

    Generalizando

    Recordemos el ejemplo con el que comenzamos:

    const twitterFavicon = buildUri('https', 'twitter.com', 'favicon.ico')const googleHome = buildUri('https', 'google.com', '')

    Hagamos un pequeño cambio:

    const twitterHome = buildUri('https', 'twitter.com', '')const googleHome = buildUri('https', 'google.com', '')

    Esta vez hay dos puntos en común: el esquema, "https"en ambos casos, y la ruta, aquí la cadena vacía.

    La partialfunción que vimos antes se aplica parcialmente desde la izquierda. Ramda también ofrece partialRight, que nos permite aplicar parcialmente de derecha a izquierda.

    const buildHomeUrl = R.partialRight(buildUri, [''])const twitterHome = buildHomeUrl('https', 'twitter.com')const googleHome = buildHomeUrl('https', 'google.com')

    Podemos llevar esto más allá:

    const buildHttpsHomeUrl = R.partial(buildHomeUrl, ['https'])const twitterHome = buildHttpsHomeUrl('twitter.com')const googleHome = buildHttpsHomeUrl('google.com')

    Una consideración de diseño

    Para fijar los argumentos schemey en , primero tuvimos que usar y luego usar en el resultado.pathbuildUrlpartialRightpartial

    Esto no es ideal. Sería mejor si pudiéramos usar partial(o partialRight), en lugar de ambos en secuencia.

    Veamos si podemos solucionar esto. Si redefinimos buildUrl:

    function buildUrl (scheme, path, domain) {  return `${scheme}://${domain}/${path}`}

    Esta nueva versión pasa primero los valores que probablemente conozcamos de antemano. El último argumento, domain, es el que probablemente queramos modificar. Organizar los argumentos en este orden es una buena regla general.

    También podemos utilizar únicamente partial:

    const buildHttpsHomeUrl = R.partial(buildUrl, ['https', ''])

    Esto demuestra que el orden de los argumentos es importante. Algunos órdenes son más convenientes para la aplicación parcial que otros. Tómese un tiempo para pensar en el orden de los argumentos si planea usar sus funciones con una aplicación parcial.

    Currying y aplicación parcial conveniente

    Ahora lo hemos redefinido buildUrlcon un orden de argumentos diferente:

    function buildUrl (scheme, path, domain) {  return `${scheme}://${domain}/${path}`}

    Tenga en cuenta que:

    • Los argumentos que probablemente queramos corregir aparecen a la izquierda. El que queremos modificar está a la derecha.
    • buildUries una función de tres argumentos. En otras palabras, necesitamos pasar tres cosas para que se ejecute.

    Hay una estrategia que podemos utilizar para aprovechar esto:

    const curriedBuildUrl = R.curry(buildUrl)// We can fix the first argument...const buildHttpsUrl = curriedBuildUrl('https')const twitterFavicon = buildHttpsUrl('twitter.com', 'favicon.ico')// ...Or fix both the first and second arguments...const buildHomeHttpsUrl = curriedBuildUrl('https', '')const twitterHome = buildHomeHttpsUrl('twitter.com')// ...Or, pass everything all at once, if we have itconst httpTwitterFavicon = curriedBuildUrl('http', 'favicon.ico', 'twitter.com')

    La función curry toma una función, la curra y devuelve una nueva función, de forma similar a partial.

    Currying es el proceso de transformar una función que llamamos de una sola vez con múltiples variables, como buildUrl, en una serie de llamadas de función, donde pasamos cada variable de una en una.

    • curryNo corrige los argumentos inmediatamente. La función devuelta toma tantos argumentos como la función original.
    • Si pasa todos los argumentos necesarios a la función currada, se comportará como buildUri.
    • Si pasa menos argumentos de los que tomó la función original, la función currada devolverá automáticamente lo mismo que obtendría al llamar a partial.

    Currying nos ofrece lo mejor de ambos mundos: aplicación parcial automática y la capacidad de utilizar nuestras funciones originales.

    Tenga en cuenta que la currificación facilita la creación de versiones parcialmente aplicadas de nuestras funciones. Esto se debe a que las funciones currificadas son convenientes para aplicarlas parcialmente, siempre y cuando hayamos sido cuidadosos con el orden de nuestros argumentos.

    Podemos llamar curriedbuildUrlde la misma manera que llamaríamos buildUri:

    const curriedBuildUrl = R.curry(buildUrl)// Outputs: `https://twitter.com/favicon.ico`curriedBuildUrl('https', 'favicon.ico', 'twitter.com')

    También podemos llamarlo así:

    curriedBuildUrl('https')('favicon.ico')('twitter.com')

    Tenga en cuenta que esto curriedBuildUrl('https')devuelve una función que se comporta como buildUrl, pero con su esquema fijado a "https".

    Luego, llamamos inmediatamente a esta función con "favicon.ico". Esto devuelve otra función, que se comporta como buildUrl, pero con su esquema fijado a "https" y su ruta fijada a la cadena vacía.

    Finalmente, invocamos esta función con "twitter.com". Como este es el último argumento, la función se resuelve en el valor final de: http://twitter.com/favicon.ico.

    La conclusión importante es que curriedBuldUrlse puede llamar como una secuencia de llamadas de función, donde pasamos solo un argumento con cada llamada. Es el proceso de convertir una función de muchas variables pasadas "todas a la vez" en una secuencia de "llamadas de un solo argumento" a lo que llamamos currificación.

    Conclusión

    Resumamos las conclusiones principales:

    • La aplicación parcial nos permite fijar los argumentos de una función, lo que nos permite derivar nuevas funciones, con un comportamiento específico, a partir de otras funciones más generales.
    • La currificación transforma una función que acepta múltiples argumentos "todos a la vez" en una serie de llamadas de función, cada una de las cuales involucra solo un argumento a la vez. Las funciones currificadas con un orden de argumentos bien diseñado son convenientes para aplicarlas parcialmente.
    • Ramda ofrece utilidades partial, partialRight, y curry. Otras bibliotecas populares similares son Underscore y Lodash.
    SUSCRÍBETE A NUESTRO BOLETÍN 
    No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

    Subir

    Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion