Je ne sais pas un tutoriel, mais je pense que c'est plus facile à comprendre flèches si vous regardez certains des exemples concrets. Le plus gros problème que j'ai eu à apprendre comment utiliser les flèches était qu'aucun des tutoriels ou des exemples montrent comment utiliser les flèches, juste la façon de composer. Donc, avec cela à l'esprit, voici mon mini-tutoriel. Je vais examiner les deux flèches: les fonctions et définies par l'utilisateur type de flèche MyArr
.
-- type representing a computation
data MyArr b c = MyArr (b -> (c,MyArr b c))
1) Une Flèche est un calcul à partir d'une entrée d'un type spécifié à la sortie d'un type spécifié. La flèche typeclass prend trois arguments de type: le type de flèche, le type d'entrée, et le type de sortie. En regardant l'exemple de la tête de flèche instances, nous trouvons:
instance Arrow (->) b c where
instance Arrow MyArr b c where
La Flèche (soit (->)
ou MyArr
) est une abstraction d'un calcul.
Pour une fonction b -> c
, b
à l'entrée et c
à la sortie.
Pour un MyArr b c
, b
à l'entrée et c
à la sortie.
2) Pour exécuter une flèche de calcul, vous utilisez une fonction spécifique à votre type de flèche. Pour les fonctions en vous il suffit d'appliquer la fonction à un argument. Pour d'autres flèches, il doit y avoir une fonction distincte (comme runIdentity
, runState
, etc. pour monades).
-- run a function arrow
runF :: (b -> c) -> b -> c
runF = id
-- run a MyArr arrow, discarding the remaining computation
runMyArr :: MyArr b c -> b -> c
runMyArr (MyArr step) = fst . step
3) Flèches sont fréquemment utilisés pour traiter une liste d'entrées. Pour les fonctions, ils peuvent être réalisés en parallèle, mais pour certains flèches de sortie à une étape donnée dépend précédente entrées (par exemple, en gardant un total des entrées).
-- run a function arrow over multiple inputs
runFList :: (b -> c) -> [b] -> [c]
runFList f = map f
-- run a MyArr over multiple inputs.
-- Each step of the computation gives the next step to use
runMyArrList :: MyArr b c -> [b] -> [c]
runMyArrList _ [] = []
runMyArrList (MyArr step) (b:bs) = let (this, step') = step b
in this : runMyArrList step' bs
C'est une des raisons les Flèches sont utiles. Ils fournissent un modèle de calcul qui peut implicitement faire usage d'état sans jamais exposer cet état pour le programmeur. Le programmeur peut utiliser arrowized de calculs et de les combiner pour créer des systèmes complexes.
Voici un MyArr qui tient compte du nombre d'entrées qu'il a reçu:
-- count the number of inputs received:
count :: MyArr b Int
count = count' 0
where
count' n = MyArr (\_ -> (n+1, count' (n+1)))
Maintenant la fonction runMyArrList count
prendra une liste de longueur n en entrée et retourne une liste d'Entiers de 1 à n.
Notez que nous n'avons pas encore utilisé tout "flèche", qui est l'une des Flèches de la classe de méthodes ou de fonctions écrites en termes d'entre eux.
4) la Plupart du code ci-dessus est spécifique à chaque Flèche exemple[1]. Tout en Control.Arrow
(et Control.Category
) est sur la composition des flèches pour faire de nouvelles flèches. Si nous prétendons que la Catégorie est la partie de la Flèche à la place d'une autre classe:
-- combine two arrows in sequence
>>> :: Arrow a => a b c -> a c d -> a b d
-- the function arrow instance
-- >>> :: (b -> c) -> (c -> d) -> (b -> d)
-- this is just flip (.)
-- MyArr instance
-- >>> :: MyArr b c -> MyArr c d -> MyArr b d
L' >>>
fonction prend deux flèches et utilise la sortie de la première comme entrée pour la seconde.
Voici un autre opérateur, communément appelée "distribution":
-- &&& applies two arrows to a single input in parallel
&&& :: Arrow a => a b c -> a b c' -> a b (c,c')
-- function instance type
-- &&& :: (b -> c) -> (b -> c') -> (b -> (c,c'))
-- MyArr instance type
-- &&& :: MyArr b c -> MyArr b c' -> MyArr b (c,c')
-- first and second omitted for brevity, see the accepted answer from KennyTM's link
-- for further details.
Depuis Control.Arrow
fournit un moyen de combiner les calculs, voici un exemple:
-- function that, given an input n, returns "n+1" and "n*2"
calc1 :: Int -> (Int,Int)
calc1 = (+1) &&& (*2)
J'ai souvent trouvé des fonctions comme calc1
utile dans les plis, ou de fonctions qui opèrent sur des pointeurs par exemple.
L' Monad
type de classe nous donne un moyen de combiner monadique les calculs en un seul nouveau monadique de calcul à l'aide de l' >>=
fonction. De même, l' Arrow
classe nous donne les moyens de combiner arrowized les calculs en un seul nouveau arrowized calcul à l'aide de quelques fonctions primitives (first
, arr
, et ***
, avec >>>
et id
de Contrôle.La catégorie). Également semblable à Monades, la question de "Ce qui fait une flèche de faire?" ne peut pas être généralement répondu. Il dépend de la flèche.
Malheureusement, je ne connais de nombreux exemples de flèche instances dans la nature. Fonctions et PRF semblent être les applications les plus courantes. HXT est la seule autre significatif de l'utilisation qui vient à l'esprit.
[1] à l'Exception de count
. Il est possible d'écrire une fonction de comptage qui fait la même chose pour n'importe quelle instance d' ArrowLoop
.