haskell - programacion - ¿Por qué debería usar functors aplicativos en la programación funcional?
programacion funcional ventajas y desventajas (7)
En mi experiencia, los funtores aplicativos son excelentes por las siguientes razones:
Ciertos tipos de estructuras de datos admiten tipos potentes de composiciones, pero en realidad no pueden convertirse en mónadas. De hecho, la mayoría de las abstracciones en la programación reactiva funcional entran en esta categoría. Si bien técnicamente podríamos hacer, por ejemplo, Behavior
(aka Signal
) una mónada, por lo general no se puede hacer de manera eficiente. Los funtores aplicativos nos permiten tener aún composiciones poderosas sin sacrificar la eficiencia (es cierto que es un poco más complicado usar un aplicativo que una mónada a veces, simplemente porque no tienes la misma estructura con la que trabajar).
La falta de dependencia de datos en un funtor aplicativo le permite, por ejemplo, recorrer una acción buscando todos los efectos que podría producir sin tener los datos disponibles. Entonces, podrías imaginar un aplicativo de "formulario web", usado así:
userData = User <$> field "Name" <*> field "Address"
y podría escribir un motor que atravesaría para encontrar todos los campos usados y mostrarlos en un formulario, luego, cuando recupere los datos, ejecútelo nuevamente para obtener el User
construido. Esto no se puede hacer con un functor simple (porque combina dos formas en una), ni una mónada, porque con una mónada puedes expresar:
userData = do
name <- field "Name"
address <- field $ name ++ "''s address"
return (User name address)
que no se puede representar, porque el nombre del segundo campo no se puede conocer sin tener ya la respuesta del primero. Estoy bastante seguro de que hay una biblioteca que implementa esta idea de formularios. He ganado algunas veces para este y ese proyecto.
La otra cosa agradable acerca de los funtores aplicativos es que componen . Más precisamente, el functor de composición:
newtype Compose f g x = Compose (f (g x))
es aplicativo cuando f
y g
son. No se puede decir lo mismo de las mónadas, lo que ha creado toda la historia del transformador de mónadas, que es complicada de algunas maneras desagradables. Los aplicadores son súper limpios de esta manera, y significa que puede construir la estructura del tipo que necesita al enfocarse en componentes compostables pequeños.
Recientemente, apareció la extensión ApplicativeDo
en GHC, que le permite usar la notación do
con los aplicativos, aliviando parte de la complejidad de la notación, siempre que no haga ninguna cosa monady.
Soy nuevo en Haskell, y estoy leyendo sobre funtores y funtores aplicativos. Ok, entiendo los funtores y cómo puedo usarlos, pero no entiendo por qué los funtores aplicativos son útiles y cómo puedo usarlos en Haskell. ¿Puede explicarme con un simple ejemplo por qué necesito funtores aplicativos?
Es difícil encontrar ejemplos en los que necesite funcionadores aplicativos. Puedo entender por qué un programador Haskell intermedio se haría esa pregunta, ya que la mayoría de los textos introductorios presentan instancias derivadas de las Mónadas que usan los Funcionadores Aplicativos solo como una interfaz conveniente.
La idea clave, como se menciona aquí y en la mayoría de las introducciones al tema, es que los Funcionadores Aplicativos se encuentran entre Funcionadores y Mónadas (incluso entre Funcionadores y Flechas). Todas las Mónadas son Funcionadores Aplicativos pero no todos los Functores son Aplicativos.
Por lo tanto, necesariamente, a veces podemos usar los combinadores aplicativos para algo para lo que no podemos usar los combinadores monádicos. Uno de ellos es ZipList
(consulte también esta pregunta SO para obtener más detalles ), que es solo un resumen de las listas para tener una instancia Applicative diferente de la derivada de la instancia de Monad de la lista. La documentación ZipList
utiliza la siguiente línea para dar una idea intuitiva de para qué ZipList
:
f <$> ZipList xs1 <*> ... <*> ZipList xsn = ZipList (zipWithn f xs1 ... xsn)
Como se señaló here , es posible crear extrañas instancias de Monad que casi funcionan para ZipList.
Hay otros Funcionadores Aplicativos que no son Mónadas (vea this pregunta SO) y son fáciles de encontrar. Tener una interfaz alternativa para Mónadas es agradable y todo, pero a veces hacer una Mónada es ineficiente, complicado o incluso imposible, y es entonces cuando necesitas Funcionadores Aplicativos.
Descargo de responsabilidad: Hacer Funcionadores Aplicativos también puede ser ineficiente, complicado e imposible, en caso de duda, consulte a su teórico de la categoría local para el uso correcto de los Funcionadores Aplicativos.
Los funtores aplicativos son útiles cuando se necesita secuenciar acciones, pero no es necesario nombrar ningún resultado intermedio. Por lo tanto, son más débiles que las mónadas, pero más fuertes que los funtores (no tienen un operador de enlace explícito, pero sí permiten ejecutar funciones arbitrarias dentro del functor).
¿Cuándo son útiles? Un ejemplo común es el análisis sintáctico, donde debe ejecutar una serie de acciones que leen partes de una estructura de datos en orden, y luego unir todos los resultados. Esto es como una forma general de composición de funciones:
f a b c d
donde se puede pensar en a
, b
, etc. como acciones arbitrarias para ejecutar, y f
como el funtor para aplicar al resultado.
f <$> a <*> b <*> c <*> d
Me gusta pensar en ellos como "espacio en blanco" sobrecargado. O bien, las funciones regulares de Haskell están en el functor aplicativo de identidad.
También sugeriría echar un vistazo a this
Al final del artículo hay un ejemplo
import Control.Applicative
hasCommentA blogComments =
BlogComment <$> lookup "title" blogComments
<*> lookup "user" blogComments
<*> lookup "comment" blogComments
Lo cual ilustra varias características del estilo de programación aplicativo.
Un buen ejemplo: análisis aplicativo.
Ver [haskell del mundo real] ch16 http://book.realworldhaskell.org/read/using-parsec.html#id652517
Este es el código del analizador con notación do:
-- file: ch16/FormApp.hs
p_hex :: CharParser () Char
p_hex = do
char ''%''
a <- hexDigit
b <- hexDigit
let ((d, _):_) = readHex [a,b]
return . toEnum $ d
Usar el funtor lo hace mucho más corto :
-- file: ch16/FormApp.hs
a_hex = hexify <$> (char ''%'' *> hexDigit) <*> hexDigit
where hexify a b = toEnum . fst . head . readHex $ [a,b]
''levantar'' puede ocultar los detalles subyacentes de algún código repetitivo. entonces puedes usar menos palabras para contar la historia exacta y precisa.
Perla funcional de Conor McBride y Ross Paterson en el estilo tiene varios buenos ejemplos. También es responsable de popularizar el estilo en primer lugar. Usan el término "idioma" para "funcionador aplicativo", pero aparte de eso es bastante comprensible.
Los funtores aplicativos son una construcción que proporciona el punto medio entre los funtores y las monads , y por lo tanto están más extendidos que las mónadas, mientras que son más útiles que los funtores. Normalmente, puedes asignar una función a un functor. Los funtores aplicativos le permiten tomar una función "normal" (tomando argumentos no funcionarios) usarla para operar en varios valores que están en contextos de functor. Como corolario, esto te da una programación efectiva sin mónadas.
here puede encontrar una explicación agradable y autónoma llena de ejemplos. También puede leer un ejemplo práctico de análisis desarrollado por Bryan O''Sullivan, que no requiere conocimiento previo.