Es ist nicht faul, weil die Get
Monade ein strenger Zustand Monade (in binary-0.5.0.2 to 0.5.1.1 ist, es ist ein fauler Zustand Monade vorher war, und in binary-0.6.* hat es eine Fortsetzung Monade geworden, ich habe nicht die Strenge Auswirkungen dieser Änderung analysiert):
-- | The parse state
data S = S {-# UNPACK #-} !B.ByteString -- current chunk
L.ByteString -- the rest of the input
{-# UNPACK #-} !Int64 -- bytes read
-- | The Get monad is just a State monad carrying around the input ByteString
-- We treat it as a strict state monad.
newtype Get a = Get { unGet :: S -> (# a, S #) }
-- Definition directly from Control.Monad.State.Strict
instance Monad Get where
return a = Get $ \s -> (# a, s #)
{-# INLINE return #-}
m >>= k = Get $ \s -> case unGet m s of
(# a, s' #) -> unGet (k a) s'
{-# INLINE (>>=) #-}
somit die letzte rekursive
get >>= \x ->
get >>= \(Stream xs) ->
return (Stream (x:xs))
zwingt die gesamte Stream
gelesen werden, bevor sie zurückgegeben werden können.
Ich glaube nicht, dass es möglich ist, Stream
in der Get
Monade träge zu decodieren (also erst recht nicht mit der Binary
Instanz). Aber Sie können eine faule Dekodierungsfunktion mit runGetState
schreiben:
-- | Run the Get monad applies a 'get'-based parser on the input
-- ByteString. Additional to the result of get it returns the number of
-- consumed bytes and the rest of the input.
runGetState :: Get a -> L.ByteString -> Int64 -> (a, L.ByteString, Int64)
runGetState m str off =
case unGet m (mkState str off) of
(# a, ~(S s ss newOff) #) -> (a, s `join` ss, newOff)
zuerst einen Get
Parser schreiben, die ein Maybe a
zurückzugibt,
getMaybe :: Binary a => Get (Maybe a)
getMaybe = do
t <- getWord8
case t of
0 -> return Nothing
_ -> fmap Just get
dann verwenden, eine Funktion vom Typ zu machen (ByteString,Int64) -> Maybe (a,(ByteString,Int64))
:
step :: Binary a => (ByteString,Int64) -> Maybe (a,(ByteString,Int64))
step (xs,offset) = case runGetState getMaybe xs offset of
(Just v, ys, newOffset) -> Just (v,(ys,newOffset))
_ -> Nothing
und dann können Sie Data.List.unfoldr
verwenden, um eine Liste langsam zu decodieren,
lazyDecodeList :: Binary a => ByteString -> [a]
lazyDecodeList xs = unfoldr step (xs,0)
und wickeln, dass in einem Stream
lazyDecodeStream :: Binary a => ByteString -> Stream a
lazyDecodeStream = Stream . lazyDecodeList
Gibt es einen Entwurf zurücktreten, warum die Put Monade ist faul, aber die Get Monade ist streng? Das erscheint sehr peinlich. –
'Get' war faul in' binary <0.5'. Ich bin mir nicht sicher über die Details, aber der Grund, warum ich zu einer strikten Implementierung wechseln sollte (selbst mit ungeboxten Tupeln), war die Performance. Mit einer faulen Staatsmonade ist es furchtbar einfach, riesige Thunks aufzubauen, und die Leute haben dieses Problem - enormen Speicherverbrauch und langsame Performance - nicht selten. Meistens ist eine strikte staatliche Monade besser oder zumindest nicht schlechter als eine faule, so dass der Gewinn insgesamt größer ist als der Verlust. Es ist wahrscheinlich weniger Arbeit, einen Lazy-Decoder mit der aktuellen Implementierung zu schreiben als Lecks mit dem alten zu vermeiden. –
Ich dachte, der ganze Punkt der Müsli-Paket und binär war, dass, wenn Sie Strenge wollten, Sie Müsli verwenden. Gibt es noch einen Unterschied? (Und danke für die Hilfe!) –