Récemment, j'essaie d'utiliser Haskell dans certains de mes systèmes de production de cas réels. Le système de type Haskell m'a vraiment aidé. Par exemple, quand j'ai réalisé que j'avais besoin d'une fonction de type
f :: (Foldable t, Monad m) => ( a-> b -> m b) -> b -> t a -> m b
Il existe en fait des fonctions comme foldM
, foldlM
et foldrM
.
Cependant, ce qui m'a vraiment choqué, c'est la définition de ces fonctions, telles que:
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldr f' return xs z0
where f' x k z = f z x >>= k
donc la fonction f'
doit être de type:
f' :: a -> b -> b
tel que requis par foldr
, alors b
doit être de nature *-> m *
, de sorte que toute la définition de foldlM
pourrait avoir un sens.
Un autre exemple comprend les définitions de liftA2
et<*>
(<*>) :: f (a -> b) -> f a -> f b
(<*>) = liftA2 id
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x = (<*>) (fmap f x)
J'ai essayé certaines de mes propres solutions avant de jeter un œil au code source. Mais l'écart est si énorme que je ne pense pas que je pourrais jamais trouver cette solution, quel que soit le nombre de lignes de code que j'écrirai à l'avenir.
Ma question est donc de savoir quel type de connaissances ou quelle branche mathématique spécifique est nécessaire pour que quelqu'un raisonne à un niveau aussi abstrait.
Je sais que la théorie des catégories pourrait offrir de l'aide et j'ai suivi cette excellente conférence pendant longtemps et j'y travaille toujours.
la source
Réponses:
En général, la logique etc., j'imagine. Mais vous pouvez aussi l'apprendre en le faisant. :) Avec le temps, vous remarquez certains modèles, prenez quelques astuces.
Comme ça
foldr
avec une chose d'argument supplémentaire. Certains le voient comme se repliant en fonctions afin de pouvoir les combiner à travers.
etid
(qui sont parfois vraiment<=<
etreturn
)Certains trouvent plus facile à comprendre en termes syntaxiques plus simples, comme
donc quand
g
est non strict dans son deuxième argument,s
peut servir d'état passant par la gauche, même si nous nous replions à droite, par exemple.la source
La meilleure façon de le comprendre est donc de le faire. Ci-dessous, il y a une implémentation de l'
foldlM
utilisationfoldl
au lieu defoldr
. C'est un bon exercice, essayez-le et revenez plus tard à la solution que je suggérerais. L'exemple explique tout le raisonnement que j'ai fait pour y parvenir, qui peut être différent du vôtre et peut être biaisé parce que je savais déjà comment utiliser un accumulateur de fonctions.Étape 1 : Essayons d'écrire
foldlM
en termes defoldl
Ici, vous réalisez que
f'
c'est pur et vous devez extraire le résultat def
pour taper match. La seule façon d'extraire une valeur monadique est avec l'>>=
opérateur, mais un tel opérateur doit être bouclé juste après son utilisation.Donc, en conclusion: chaque fois que vous vous retrouvez avec, j'aimerais déballer complètement cette monade , abandonnez. N'est-ce pas la bonne façon
Étape 2 : Essayons d'écrire
foldlM
en termes defoldl
mais en utilisant d'abord[]
comme pliable, car il est facile de faire correspondre les motifs (c'est-à-dire que nous n'avons pas vraiment besoin d'utiliserfold
)Ok, c'était facile. Comparons la définition avec la
foldl
définition habituelle des listesCool!! ils sont à peu près les mêmes. Le cas trivial concerne exactement la même chose. Le cas récursif est un autre petit peu, vous voulez écrire quelque chose comme:
foldlM' f (f z0 x) xs
. Mais ce n'est pas compiler comme à l'étape 1, donc vous pourriez penser OK, je ne veux pas appliquerf
, juste pour tenir un tel calcul et le composer avec>>=
. J'aimerais écrire quelque chose de plus commefoldlM' f (f z0 x >>=) xs
si ça avait du sens ...Étape 3 Réalisez que ce que vous voulez accumuler est une composition de fonction et non un résultat. ( ici, je suis probablement biaisé par le fait que je le savais déjà parce que vous l'avez posté ).
Par le type
initFunc
et l'utilisation de nos connaissances de l'étape 2 (la définition récursive), nous pouvons en déduireinitFunc = return
. La définition def'
peut être complétée en sachant quef'
devrait utiliserf
et>>=
.Comme vous pouvez le voir, ce n'est pas si difficile à faire. Il a besoin de pratique, mais je ne suis pas un développeur de haskell professionnel et je pourrais le faire moi-même, c'est une question de pratique
la source
Monad
instances typiques .Vous n'avez pas besoin de connaissances spécifiques en mathématiques pour écrire une fonction comme
foldM
. J'utilise Haskell en production depuis 4 ans déjà, et j'ai aussi du mal à comprendre cette définition defoldM
. Mais c'est surtout parce que c'est mal écrit. S'il vous plaît, ne le prenez pas comme une faute personnelle si vous ne comprenez pas un code obscur. Voici une version plus lisible defoldlM
Cette fonction n'est pas encore la plus simple. Principalement parce qu'il a une utilisation non typique de l'
foldr
endroit où l'accumulateur intermédiaire est une fonction. Mais vous pouvez voir quelques méthodes qui rendent cette définition plus lisible:where
(pour que vous connaissiez la forme des arguments).Après avoir vu une telle fonction, vous pouvez maintenant effectuer une technique de raisonnement équationnel pour développer la définition étape par étape et voir comment cela fonctionne. La capacité de proposer de telles fonctions vient avec l'expérience. Je n'ai pas de solides compétences en mathématicien, et cette fonction n'est pas une fonction typique de Haskell. Mais plus vous vous entraînez, mieux c'est :)
la source