Podemos reducir esto a
GHCi> toTwosComp (1 :: Word8)
*** Exception: divide by zero
Tenga en cuenta que esto funciona si se utiliza Word16, Int, Integer, o cualquier número de tipos, pero falla al utilizar Word8, como B.unpack
nos da! Entonces, ¿por qué falla? La respuesta se encuentra en el source code a Codec.Utils.toTwosComp. Puede ver que llama al toBase 256 (abs x)
, donde x es el argumento.
El tipo de toBase
- no exportó desde el módulo Codec.Utils, y sin una firma de tipo explícito en la fuente, pero se puede ver esto poniendo la definición en un archivo y pidiendo GHCi lo que el tipo es (:t toBase
) , es
toBase :: (Integral a, Num b) => a -> a -> [b]
Por lo tanto, la anotación de tipos explícita, toTwosComp
está llamando toBase (256 :: Word8) (abs x :: Word8)
. ¿Cuál es 256 :: Word8
?
GHCi> 256 :: Word8
0
¡Uy! 256> 255, por lo que no podemos mantenerlo en Word8 y se desborda silenciosamente. toBase
, en el proceso de conversión de base, se divide por la base que se está utilizando, por lo que termina dividiéndose por cero, produciendo el comportamiento que está obteniendo.
¿Cuál es la solución? Convertir los Word8s a INT con fromIntegral
antes de pasarlos a toTwosComp
:
convert :: B.ByteString -> [Octet]
convert = map convert' . B.unpack
where convert' b = head $ toTwosComp (fromIntegral b :: Int)
Personalmente, este comportamiento me preocupa un poco, y creo que toTwosComp
probablemente deba realizar dicha conversión en sí, probablemente entero, para que funcione con tipos integrales de todos los tamaños; pero esto incurriría en una pena de rendimiento que a los desarrolladores podría no gustarles. Aún así, se trata de una falla bastante confusa que requiere comprensión de buceo. Afortunadamente, es muy fácil evitarlo.
Es un error en 'toTwosComp'. – augustss