2010-03-07 17 views
110

No entiendo lo que es "levantar". ¿Debería primero entender las mónadas antes de entender qué es un "ascensor"? (Igualmente, soy completamente ignorante sobre las mónadas :) ¿O alguien puede explicarme con palabras simples?¿Qué es "levantar" en Haskell?

+6

Quizás útil, tal vez no: http://www.haskell.org/haskellwiki/Lifting – kennytm

Respuesta

147

La elevación es más un patrón de diseño que un concepto matemático (aunque espero que alguien por aquí ahora me refute al mostrar cómo los ascensores son una categoría o algo así).

Normalmente tiene algún tipo de datos con un parámetro. Algo así como

data Foo a = Foo { ...stuff here ...} 

Supongamos que usted encuentra que una gran cantidad de usos de Foo tomar tipos numéricos (Int, Double etc) y te guarde tener que escribir código que se desenvuelve estos números, añade o los multiplica, y luego las envuelve la espalda arriba. Puede cortocircuitar esto escribiendo el código desenvolver y envolver una vez. Esta función se llama tradicionalmente "levantar" porque se parece a esto:

liftFoo2 :: (a -> b -> c) -> Foo a -> Foo b -> Foo c 

En otras palabras, usted tiene una función que toma una función de dos argumentos (como el operador (+)) y lo convierte en la función equivalente para Foos.

lo que ahora puede escribir

addFoo = liftFoo2 (+) 

Editar: más información

Por supuesto, puede tener liftFoo3, liftFoo4 y así sucesivamente. Sin embargo, esto a menudo no es necesario.

de inicio con la observación

liftFoo1 :: (a -> b) -> Foo a -> Foo b 

Pero eso es exactamente lo mismo que fmap.Así que en lugar de liftFoo1 que iba a escribir

instance Functor Foo where 
    fmap foo = ... 

Si realmente desea la regularidad completa a continuación, puede decir

liftFoo1 = fmap 

Si se puede hacer Foo en un funtor, tal vez puede que sea un funtor aplicativo. De hecho, si se puede escribir liftFoo2 entonces la instancia aplicativo se parece a esto:

import Control.Applicative 

instance Applicative Foo where 
    pure x = Foo $ ... -- Wrap 'x' inside a Foo. 
    (<*>) = liftFoo2 ($) 

El operador (<*>) de Foo tiene el tipo

(<*>) :: Foo (a -> b) -> Foo a -> Foo b 

Se aplica la función ajustada al valor ajustado. Entonces, si puede implementar liftFoo2, puede escribir esto en términos de ello. O se puede aplicar directamente y no molestarse en liftFoo2, ya que el módulo incluye Control.Applicative

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c 

e igualmente hay liftA y liftA3. Pero en realidad no se utiliza muy a menudo porque no hay otro operador

(<$>) = fmap 

Esto le permite escribir:

result = myFunction <$> arg1 <*> arg2 <*> arg3 <*> arg4 

El término myFunction <$> arg1 devuelve una nueva función envuelto en Foo. Esto a su vez se puede aplicar al siguiente argumento usando (<*>), y así sucesivamente. Así que ahora, en lugar de tener una función de elevación para cada aria, solo tienes una cadena de aplicativos.

+20

Probablemente valga la pena recordar que los ascensores deben respetar las leyes estándar 'lift id == id' y' lift (f. G) == (lift f). (levantar g) '. –

+11

Los elevadores son de hecho "una categoría o algo". Carlos acaba de enumerar las leyes de Functor, donde 'id' y' .' son la flecha de identidad y la composición de flecha de alguna categoría, respectivamente. Por lo general, cuando se habla de Haskell, la categoría en cuestión es "Hask", cuyas flechas son funciones Haskell (en otras palabras, 'id' y' .' se refieren a las funciones Haskell que conoces y amas). –

+0

me gusta la forma en que empiezas mencionando un tipo con un parámetro. Esta es la idea más importante sobre Monad. Todavía no puedo creer que en el libro: Haskell mundo real del autor ni de explicar que Mónada es simplemente una clase de tipo (ver el capítulo sobre las mónadas y la programación mónada en ese libro) – osager

10

de elevación es un concepto que permite transformar una función en una función correspondiente dentro de otro (por lo general más general) establecer

echar un vistazo a http://haskell.org/haskellwiki/Lifting

+34

Sí, pero esa página comienza "Normalmente comenzamos con un functor (covariante) ...". No es exactamente amiga de los novatos. –

+3

Pero el "functor" está vinculado, por lo que el novato solo puede hacer clic para ver qué es un Functor. Es cierto que la página vinculada no es tan buena. Necesito obtener una cuenta y arreglar eso. – jrockway

+4

Es un problema que he visto en otros sitios de programación funcional; cada concepto se explica en términos de otros conceptos (desconocidos) hasta que el novato da un rodeo completo (y alrededor de la curva). Debe tener algo que ver con que te guste la recursión. – DNA

19

Vamos a empezar con un ejemplo:

> replicate 3 'a' 
"aaa" 
> :t replicate 
replicate :: Int -> a -> [a] 
> :t liftA2 replicate 
liftA2 replicate :: (Applicative f) => f Int -> f a -> f [a] 
> (liftA2 replicate) [1,2,3] ['a','b','c'] 
["a","b","c","aa","bb","cc","aaa","bbb","ccc"] 
> :t liftA2 
liftA2 :: (Applicative f) => (a -> b -> c) -> (f a -> f b -> f c) 

liftA2 transforma en función de los tipos de fricción a una función de estos tipos envueltos en una Applicative, tales como listas, IO, etc.

Otro ascensor común es lift de Control.Monad.Trans. Transforma una acción monádica de una mónada a una acción de una mónada transformada.

En general, levanta "levantar" una función/acción en un tipo "envuelto".

La mejor manera de entender esto, y las mónadas, etc. y comprender por qué son útiles, es probablemente para codificarlo y usarlo. Si hay algo que codificaste previamente que sospechas puede beneficiarse de esto (es decir, esto acortará el código, etc.), simplemente pruébalo y entenderás fácilmente el concepto.

34

Paul's y yairchu son buenas explicaciones.

Me gustaría agregar que la función que se está levantando puede tener un número arbitrario de argumentos y que no tienen que ser del mismo tipo. Por ejemplo, también se puede definir un liftFoo1:

liftFoo1 :: (a -> b) -> Foo a -> Foo b 

En general, el levantamiento de las funciones que toman 1 argumento es capturado en la clase de tipo Functor, y la operación de elevación se llama fmap:

fmap :: Functor f => (a -> b) -> f a -> f b 

Tenga en cuenta la similitud con el tipo liftFoo1. De hecho, si usted tiene liftFoo1, puede hacer Foo una instancia de Functor:

instance Functor Foo where 
    fmap = liftFoo1 

Por otra parte, la generalización de levantar a un número arbitrario de argumentos se llama estilo aplicativo. No se moleste en sumergirse en esto hasta que capte el levantamiento de las funciones con un número fijo de argumentos. Pero cuando lo haces, Learn you a Haskell tiene un buen capítulo sobre esto. El Typeclassopedia es otro buen documento que describe Functor y Aplicable (así como otras clases de tipos, vaya al capítulo de la derecha en ese documento).

Espero que esto ayude!

0

Según this shiny tutorial, un funtor es un poco de contenedor (como Maybe<a>, List<a> o Tree<a> que puede almacenar elementos de algunos otro tipo, a). He utilizado Java genéricos notación, <a>, para el elemento de tipo a y pensar en los elementos como las bayas en el árbol Tree<a>. Hay una función fmap, que tiene una función de conversión de elemento, a->b y el recipiente functor<a>. Aplica a->b a cada elemento del contenedor convirtiéndolo efectivamente en functor<b>. Cuando se suministra sólo primer argumento, a->b, fmap espera a que el functor<a>. Es decir, el suministro de a->b solo convierte esta función a nivel de elemento en la función de functor<a> -> functor<b> que opera sobre los contenedores. Esto se llama levantando de la función. Debido a que el recipiente también se llama un funtor , los Functors en lugar de las mónadas son un requisito previo para el levantamiento. Las mónadas son una especie de "paralelo" al levantamiento. Ambos se basan en la noción de Functor y hacen f<a> -> f<b>. La diferencia es que la elevación usa a->b para la conversión, mientras que Monad requiere que el usuario defina a -> f<b>.

+4

Les di una marca, porque "un functor es un contenedor" es cebo de llama con sabor a troll. Ejemplo: funciones de algún 'r' a un tipo (usemos' c' para variedad), son Functors. No "contienen" ninguna 'c'. En este caso, fmap es la composición de la función, tomando una función 'a -> b' y una' r -> a', para darle una nueva función 'r -> b'. Todavía no hay contenedores. También, si pudiera, lo volvería a marcar para la oración final. – BMeph

+1

Además, 'fmap' es una función y no" espera "nada; El "contenedor" es un Functor es el punto de levantamiento. Además, las mónadas son, en todo caso, una idea doble para levantar: una mónada le permite usar algo que se ha levantado algunas veces positivas, como si solo se hubiera levantado una vez; esto es mejor conocido como _flattening_. – BMeph

+1

@BMeph 'Esperar',' esperar', 'anticipar' son los sinónimos. Al decir "función espera" quise decir "función anticipada". – Val

Cuestiones relacionadas