Welche Kenntnisse oder Schulungen sind erforderlich, damit jemand die Definition von foldlM wie folgt aufschreibt? [geschlossen]


9

Vor kurzem versuche ich, Haskell in einigen meiner realen Fallproduktionssysteme zu verwenden. Das Haskell-System bietet mir wirklich große Hilfe. Zum Beispiel, als ich merkte, dass ich eine Funktion vom Typ brauche

f :: (Foldable t, Monad m) => ( a-> b -> m b) -> b -> t a -> m b

Es gibt tatsächlich Funktionen wie foldM, foldlMund foldrM.

Was mich jedoch wirklich schockiert hat, ist die Definition dieser Funktionen, wie zum Beispiel:

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

Die Funktion f'muss also vom Typ sein:

f' :: a -> b -> b

wie von verlangt foldr, bmuss dann von Art sein *-> m *, damit die gesamte Definition von foldlMsinnvoll sein könnte.

Ein weiteres Beispiel enthält Definitionen von liftA2und<*>

(<*>) :: 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)

Ich habe einige meiner eigenen Lösungen ausprobiert, bevor ich in den Quellcode geschaut habe. Aber die Lücke ist so groß, dass ich nicht glaube, dass ich jemals eine Lösung finden könnte, egal wie viele Codezeilen ich in Zukunft schreiben werde.

Meine Frage ist also, welche Art von Wissen oder welcher spezifische Mathematikzweig notwendig ist, damit jemand auf einer so stark abstrahierten Ebene argumentieren kann.

Ich weiß, dass die Kategorietheorie möglicherweise hilfreich ist, und ich verfolge diesen großartigen Vortrag schon lange und arbeite immer noch daran.


13
Haskell ist eine Sprache. Es enthält viele Wörter, und die meisten dieser Wörter können auf verschiedene Arten verwendet werden. Wenn Sie eine neue Sprache lernen, machen viele Sätze und Redewendungen jahrelang keinen Sinn. Aber je öfter Sie es verwenden, desto mehr sehen Sie vertraute Muster, und Dinge, die Sie einst für einschüchternd und fortgeschritten hielten, kommen ganz natürlich vor. Entspannen.
Luqui

Antworten:


3

Im Allgemeinen würde ich mir Logik usw. vorstellen. Sie können es aber auch lernen, indem Sie es tun. :) Mit der Zeit bemerken Sie einige Muster, lernen Sie einige Tricks.

So foldrmit einem zusätzlichen Argument. Einige sehen es als Faltung in Funktionen, so dass sie durch .und id(die manchmal wirklich <=<und sind return) kombiniert werden können ,

foldr g z xs  =  foldr ($) z . map g $ xs
              =  foldr (.) id (map g xs) z
         ~/=  foldr (<=<) return (map g xs) z
{-
  (\f -> f . f) :: (a -> a) -> (a -> a)

  (\f -> f <=< f) :: Monad m => (a -> m a) -> (a -> m a)
                            (still just a type, not a kind)
-}

Einige finden es einfacher, es in einfacheren, syntaktischen Begriffen zu verstehen, wie

foldr g z [a,b,c,...,n] s =
     g a (foldr g z [b,c,...,n]) s

Wenn galso das zweite Argument nicht streng ist, skann es als Zustand dienen, der von links weitergegeben wird, obwohl wir als ein Beispiel rechts falten.


1
Vielen Dank, ich habe versucht herauszufinden, ob diese Definition einzigartig ist und habe hier nicht mit der Verwendung der Kleisli-Komposition gerechnet. Diese Antwort löst wirklich meinen Zweifel.
Theodora

Bitte. :)
Will Ness

4

Der beste Weg, es zu verstehen, ist es, es zu tun. Unten finden Sie eine Implementierung von foldlMusing foldlanstelle von foldr. Es ist eine gute Übung, probieren Sie es aus und kommen Sie später zu der Lösung, die ich vorschlagen würde. Das Beispiel erklärt alle Überlegungen, die ich angestellt habe, um dies zu erreichen. Diese können sich von Ihren unterscheiden und voreingenommen sein, da ich bereits über die Verwendung eines Funktionsakkumulators Bescheid wusste.

Schritt 1 : Versuchen wir, foldlMin Bezug auf zu schreibenfoldl

-- this doesn't compile because f returning type is (m b) and not just (b) 
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f z0 xs 

-- So let substitute f by some undefined f'
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
  where f' = undefined

-- cool, but f' should use f somehow in order to get the monadic behaviour
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
  where f' b a = f somethingIDontkNow 

Hier erkennen Sie, dass dies f'rein ist und Sie das Ergebnis extrahieren müssen, um fÜbereinstimmung einzugeben. Die einzige Möglichkeit, einen monadischen Wert zu "extrahieren", ist der >>=Operator. Ein solcher Operator muss jedoch direkt nach seiner Verwendung umbrochen werden.

Fazit: Jedes Mal, wenn Sie am Ende diese Monade vollständig auspacken möchten , geben Sie einfach auf. Ist nicht der richtige Weg

Schritt 2 : Lassen Sie uns versuchen, foldlMin Form von zu schreiben, foldlaber zuerst []als faltbar zu verwenden, da es einfach ist, Muster zu finden (dh wir müssen es eigentlich nicht verwenden fold).

-- This is not very hard. It is pretty standard recursion schema. :)
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 []     = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs

Ok, das war einfach. Vergleichen Sie die Definition mit der üblichen foldlDefinition für Listen

foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 []     = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs

myfoldl :: (b -> a -> b) -> b -> [a] -> b
myfoldl f z0 []     = z0
myfoldl f z0 (x:xs) = foldl f (f z0 x) xs

Cool!! Sie sind ziemlich gleich. Der triviale Fall handelt von genau der gleichen Sache. Der rekursive Fall ist etwas anders, Sie möchten etwas mehr schreiben wie : foldlM' f (f z0 x) xs. Wird aber nicht wie in Schritt 1 kompiliert, so dass Sie vielleicht denken, OK, ich möchte mich nicht bewerben f, nur um eine solche Berechnung zu halten und sie zu komponieren >>=. Ich würde gerne etwas mehr schreiben, foldlM' f (f z0 x >>=) xs wenn es Sinn hätte ...

Schritt 3 Stellen Sie fest, dass das, was Sie akkumulieren möchten, eine Funktionszusammensetzung und kein Ergebnis ist. ( hier bin ich wahrscheinlich voreingenommen von der Tatsache, dass ich es bereits wusste, weil Sie es gepostet haben ).

foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' initFunc xs
  where initFunc = undefined :: b -> m b
        f'       = undefined :: (b -> m b) -> a -> (b -> m b) -- This type signature can be deduce because f' should be applied to initFunc and a's from t a. 

Durch die Art initFuncund Verwendung unseres Wissens aus Schritt 2 (der rekursiven Definition) können wir daraus schließen initFunc = return. Die Definition f'kann zu wissen , abgeschlossen werden , dass f'verwenden soll , fund >>=.

foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' return xs z0
--                        ^^^^^^
--                        |- Initial value
  where f' b a = \bvalue -> b bvalue >>= \bresult -> f bresult a -- this is equivalent to (b >=> \result -> f result a) which captures the sequence behaviour of the implementation
--         ^      ^^^^^^                  ^^^^^^^
--         |      |                       |- This is the result of previous computation
--         |      |- f' should return a function b -> m b. Any time you have to return a function, start writing a lambda  
--         |- This b is the accumulated value and has type b -> m b
-- Following the types you can write this with enough practise

Wie Sie sehen können, ist es nicht soooo schwierig, dies zu tun. Es braucht Übung, aber ich bin kein professioneller Haskell-Entwickler und ich könnte es selbst tun. Es ist eine Frage der Übung


1
Ich sehe nicht wirklich, was eine linke Falte leichter verständlich macht als eine rechte Falte. Es ist viel wahrscheinlicher, dass die rechte Falte ein nützliches Ergebnis für unendliche Strukturen liefert und für typische MonadFälle effizient ist .
Feuer

@dfeuer Es geht nicht darum, ein einfacheres Beispiel zu zeigen, sondern eine geeignete Übung für das OP vorzuschlagen und eine begründete Argumentation der Lösung aufzudecken, um zu beweisen, dass es nicht notwendig ist, ein Super-Master-Haskeller zu sein, um zu erhalten eine solche Lösung. Abweichungen von der Effizienz werden nicht berücksichtigt
lsmor

3

Sie benötigen keine spezifischen Kenntnisse in Mathematik, um eine Funktion wie zu schreiben foldM. Ich verwende Haskell bereits seit 4 Jahren in der Produktion und habe auch Probleme, diese Definition von zu verstehen foldM. Aber das liegt hauptsächlich daran, dass es schlecht geschrieben ist. Bitte nehmen Sie es nicht als persönlichen Fehler, wenn Sie einen obskuren Code nicht verstehen können. Hier ist eine besser lesbare Version vonfoldlM

foldlM
    :: forall t m a b .
       (Foldable t, Monad m)
    => (b -> a -> m b)  -- ^ Monadic action
    -> b                -- ^ Starting accumulator
    -> t a              -- ^ List of values
    -> m b              -- ^ Computation result inside a monad
foldlM f z xs = (foldr step pure xs) z
  where
    step :: a -> (b -> m b) -> b -> m b
    step cur next acc = do
        result <- f acc cur
        next result

Diese Funktion ist immer noch nicht die einfachste. Meistens, weil es eine nicht typische Verwendung hat, foldrbei der der Zwischenspeicher eine Funktion ist. Sie können jedoch einige Möglichkeiten erkennen, die eine solche Definition lesbarer machen:

  1. Kommentare zu Funktionsargumenten.
  2. Bessere Argumentnamen (immer noch kurz und idiomatisch, aber zumindest besser lesbar).
  3. Die explizite Typensignatur der darin enthaltenen Funktion where(damit Sie die Form der Argumente kennen).

Nachdem Sie eine solche Funktion gesehen haben, können Sie jetzt die Technik des Gleichungsdenkens ausführen , um die Definition Schritt für Schritt zu erweitern und zu sehen, wie sie funktioniert. Die Fähigkeit, solche Funktionen zu entwickeln, hängt von der Erfahrung ab. Ich habe keine starken mathematischen Fähigkeiten und diese Funktion ist keine typische Haskell-Funktion. Aber je mehr Übung du hast, desto besser wird es :)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.