teclas - Haskell: ¿Estoy malinterpretando cómo se pueden usar las flechas?
tecla bloqueo de desplazamiento o scroll lock (1)
¿Alguien puede explicar cómo entiendo mal cómo funcionan las flechas y cómo podrían usarse? ¿Hay una filosofía fundamental para las flechas que me falta?
Me da la impresión de que estás tratando a esta Arrow
como si fueras una Monad
. No sé si esto cuenta como una "filosofía fundamental", pero hay una diferencia significativa entre los dos, a pesar de la frecuencia con la que se superponen. En cierto sentido, la cosa clave que define una Monad
es la función de join
; cómo colapsar una estructura anidada en una sola capa. Son útiles debido a lo que permite la join
: puede crear nuevas capas monádicas en una función recursiva, alterar la estructura de Functor
función de su contenido, etc. Pero esto no se trata de Monad
s, así que lo dejaremos así.
La esencia de una Arrow
, por otro lado, es una versión generalizada de una función . La clase de tipo Category
define versiones generalizadas de la composición de la función y la función de identidad, mientras que la clase de tipo Arrow
define cómo elevar una función regular a una Arrow
y cómo trabajar con Arrow
que toman múltiples argumentos (en forma de tuplas: Arrows
no necesariamente puede ser curry!).
Cuando combina Arrow
s de una manera básica, como en su primera función countExample
, todo lo que realmente está haciendo es algo así como una composición de funciones elaborada. Mire hacia atrás a su definición de (.)
está tomando dos funciones con estado y conectándolas en una sola función con estado, con el comportamiento de cambio de estado manejado automáticamente.
Por lo tanto, el principal problema con su cuenta, por countExample
es que incluso las menciones count''
y tal. Todo esto se hace entre bambalinas, al igual que no es necesario pasar explícitamente el parámetro de estado cuando se usa la notación do
en la mónada State
.
Ahora, debido a que la notación de proceso solo le permite construir grandes Arrow
compuestas, para usar realmente su función de estado, tendrá que trabajar fuera de la sintaxis de Arrow
, al igual que necesita runState
o runState
para ejecutar un cálculo en la mónada State
. Su segundo countExample
es en este sentido, pero demasiado especializado. En el caso general, su función con estado asigna un flujo de entradas a un flujo de salidas, lo que lo convierte en un transductor de estado finito , por lo que runStatefulFunction
probablemente tomaría una lista perezosa de valores de entrada y los convertiría en una lista perezosa de valores de salida usando un derecho pliegue con unSF
para alimentar cada uno al transductor por turno.
Si desea ver un ejemplo, el paquete de arrows
incluye un Automaton
transformador de Arrow
que define algo casi idéntico a su función de StatefulFunction
, excepto con una Arrow
arbitraria en lugar de la función simple que ha utilizado.
Ah, y para revisar brevemente la relación entre Arrow
s y Monad
s:
Las Arrows
son solo cosas de función de "primer orden". Como dije antes, no siempre se pueden aplicar al curry, y de la misma manera no siempre se pueden "aplicar" en el mismo sentido en que la función ($)
aplica funciones. Si realmente desea Arrows
orden superior, la clase de tipo ArrowApply
define una Arrow
aplicación. Esto agrega una gran cantidad de poder a una Arrow
y, entre otras cosas, permite la misma función de "estructura anidada de colapso" que proporciona Monad
, lo que hace posible definir una instancia de ArrowApply
para cualquier instancia de ArrowApply
.
En la otra dirección, dado que las Monad
permiten combinar funciones que crean una nueva estructura monádica, para cualquier Monad
m
puede hablar de una "flecha Kleisli", que es una función de tipo a -> mb
. Las flechas de Kleisli para una Monad
pueden recibir una instancia de Arrow
de una manera bastante obvia.
Aparte de las flechas ArrowApply
y Kleisli, no existe una relación particularmente interesante entre las clases de tipos.
Escribí un código de juguete para jugar con el concepto de flechas. Quería ver si podía escribir una flecha que codificara el concepto de una función con estado, dando un valor diferente después de diferentes llamadas.
{-# LANGUAGE Arrows#-}
module StatefulFunc where
import Control.Category
import Control.Arrow
newtype StatefulFunc a b = SF { unSF :: a -> (StatefulFunc a b, b) }
idSF :: StatefulFunc a a
idSF = SF $ /a -> (idSF, a)
dotSF :: StatefulFunc b c -> StatefulFunc a b -> StatefulFunc a c
dotSF f g = SF $ /a ->
let (g'', b) = unSF g a
(f'', c) = unSF f b
in (dotSF f'' g'', c)
instance Category StatefulFunc where
id = idSF
(.) = dotSF
arrSF :: (a -> b) -> StatefulFunc a b
arrSF f = ret
where ret = SF fun
fun a = (ret, f a)
bothSF :: StatefulFunc a b -> StatefulFunc a'' b'' -> StatefulFunc (a, a'') (b, b'')
bothSF f g = SF $ /(a,a'') ->
let (f'', b) = unSF f a
(g'', b'') = unSF g a''
in (bothSF f'' g'', (b, b''))
splitSF :: StatefulFunc a b -> StatefulFunc a b'' -> StatefulFunc a (b, b'')
splitSF f g = SF $ /a ->
let (f'', b) = unSF f a
(g'', b'') = unSF g a
in (splitSF f'' g'', (b, b''))
instance Arrow StatefulFunc where
arr = arrSF
first = flip bothSF idSF
second = bothSF idSF
(***) = bothSF
(&&&) = splitSF
eitherSF :: StatefulFunc a b -> StatefulFunc a'' b'' -> StatefulFunc (Either a a'') (Either b b'')
eitherSF f g = SF $ /e -> case e of
Left a -> let (f'', b) = unSF f a in (eitherSF f'' g, Left b)
Right a'' -> let (g'', b'') = unSF g a'' in (eitherSF f g'', Right b'')
mergeSF :: StatefulFunc a b -> StatefulFunc a'' b -> StatefulFunc (Either a a'') b
mergeSF f g = SF $ /e -> case e of
Left a -> let (f'', b) = unSF f a in (mergeSF f'' g, b)
Right a'' -> let (g'', b) = unSF g a'' in (mergeSF f g'', b)
instance ArrowChoice StatefulFunc where
left = flip eitherSF idSF
right = eitherSF idSF
(+++) = eitherSF
(|||) = mergeSF
Entonces, después de revisar varias definiciones de clase de tipo (no estoy seguro de si ArrowZero funcionaría para esto o no, así que lo omití), definí algunas funciones de ayuda.
evalSF :: (StatefulFunc a b) -> a -> b
evalSF f a = snd (unSF f a)
givenState :: s -> (s -> a -> (s, b)) -> StatefulFunc a b
givenState s f = SF $ /a -> let (s'', b) = f s a in (givenState s'' f, b)
Y resuelto un ejemplo de uso.
count :: StatefulFunc a Integer
count = givenState 1 $ /c _ -> (c+1, c)
countExample :: StatefulFunc a Integer
countExample = proc _ -> do
(count'', one) <- count -< ()
(count'''', two) <- count'' -< ()
(count'''''', three) <- count'''' -< ()
returnA -< three
Sin embargo, cuando trato de compilar countExample
, recibo los errores "No está en el alcance" para count''
y count''''
, lo que supongo que significa que debo volver al tutorial y leer qué se puede usar cuando. Creo que lo que realmente me gustaría de todos modos es algo más como
countExample :: Integer
countExample =
let (count'', one) = unSF count ()
(count'''', two) = unSF count'' ()
(count'''''', three) = unSF count'''' ()
in three
Pero eso es un poco incómodo, y esperaba algo más natural.
¿Alguien puede explicar cómo entiendo mal cómo funcionan las flechas y cómo podrían usarse? ¿Hay una filosofía fundamental para las flechas que me falta?