Possiamo ridurre questo per
GHCi> toTwosComp (1 :: Word8)
*** Exception: divide by zero
Nota che questo funziona se si utilizza Word16, Int, Integer, o un qualsiasi numero di tipi, ma non riesce quando si utilizza Word8, come B.unpack
ci dà! Allora perché fallisce? La risposta si trova nello source code a Codec.Utils.toTwosComp. Puoi vedere che chiama lo toBase 256 (abs x)
, dove l'argomento è x.
Il tipo di toBase
- non esportato dal modulo Codec.Utils, e senza una firma di tipo esplicito nella sorgente, ma si può vedere questo mettendo la definizione in un file e chiedendo GHCi ciò che il tipo è (:t toBase
) , è
toBase :: (Integral a, Num b) => a -> a -> [b]
Quindi, annotare i tipi in modo esplicito, toTwosComp
sta chiamando toBase (256 :: Word8) (abs x :: Word8)
. Cos'è 256 :: Word8
?
GHCi> 256 :: Word8
0
Oops! 256> 255, quindi non possiamo tenerlo in un Word8, e trabocca silenziosamente. toBase
, nel processo della sua conversione di base, divide per base utilizzata, quindi finisce dividendo per zero, producendo il comportamento che si sta ottenendo.
Qual è la soluzione? Convertire i Word8s a INT con fromIntegral
prima di passarli a toTwosComp
:
convert :: B.ByteString -> [Octet]
convert = map convert' . B.unpack
where convert' b = head $ toTwosComp (fromIntegral b :: Int)
Personalmente, questo comportamento mi preoccupa un po ', e credo che toTwosComp
dovrebbe probabilmente fare una conversione stessa, rischia di Integer, in modo che funzioni con tipi integrali di ogni dimensione; ma ciò comporterebbe una penalizzazione delle prestazioni a cui gli sviluppatori potrebbero non piacere l'idea di. Eppure, questo è un fallimento piuttosto confuso che richiede una fonte di immersione per capire. Per fortuna, è molto facile lavorare.
È un errore in 'toTwosComp'. – augustss