ghcjs-base 0.8.0.4 → 0.8.1.0
raw patch · 4 files changed
+256/−2 lines, 4 filesdep ~time
Dependency ranges changed: time
Files
- ChangeLog.md +13/−0
- GHCJS/Int53.hs +184/−0
- GHCJS/Marshal/Internal.hs +51/−0
- ghcjs-base.cabal +8/−2
+ ChangeLog.md view
@@ -0,0 +1,13 @@+# Revision history for ghcjs-base++## 0.8.1.0++* `GHCJS.Int53`: `Int53` and `Word53`, integers a JavaScript Number holds+ exactly (up to 2^53 - 1), with checked (`toInt53`), saturating+ (`clampInt53`) and literal (`int53`) conversions and no `Num` instance.+ jsaddle has the same module for native GHC.+* `PToJSVal`, `PFromJSVal`, `ToJSVal` and `FromJSVal` instances for+ `Object` and `SomeJSArray` (`JSArray`, `MutableJSArray`) in+ `GHCJS.Marshal.Internal`; `fromJSVal` is `Nothing` for `null` and+ `undefined`. Packages with their own orphan instances for these types+ need to drop them.
+ GHCJS/Int53.hs view
@@ -0,0 +1,184 @@+-----------------------------------------------------------------------------+--+-- Module : GHCJS.Int53+-- License : MIT+--+-- | Integers that a JavaScript Number holds exactly.+--+-- A JavaScript Number is a double, so it represents every integer of+-- magnitude up to @2^53 - 1@ (@Number.MAX_SAFE_INTEGER@) exactly and loses+-- precision beyond that. Web IDL's @long long@ and @unsigned long long@+-- are such Numbers (its editors have proposed renaming them @int53@ /+-- @uint53@, <https://github.com/whatwg/webidl/issues/33>), and without+-- @[EnforceRange]@ a browser silently reduces an out of range argument+-- modulo @2^64@.+--+-- 'Int53' and 'Word53' make that range part of the type. They have no+-- 'Num' instance on purpose: do arithmetic in 'Int64', 'Integer', 'Double'+-- or any other numeric type and convert back with a checked conversion,+-- so the place where a value could go out of range is explicit.+--+-- Using them with the DOM bindings (jsaddle-dom / ghcjs-dom 0.10, where+-- every @long long@ is an 'Int53' and every @unsigned long long@ a+-- 'Word53'):+--+-- > size <- Blob.getSize blob -- Word53+-- > let half = fromWord53 size `div` (2 :: Integer)+-- > -- (toInt53 gives Maybe Int53: Nothing when out of range)+-- > part <- Blob.slice blob (Just (int53 0)) (toInt53 half) (Nothing :: Maybe Text)+--+-- Small literals go through 'int53' / 'word53' (every 'Int32' / 'Word32'+-- fits); computed values through 'toInt53' / 'toWord53' (or 'clampInt53'+-- / 'clampWord53' to saturate instead).+--+-- jsaddle provides a module with the same name and API for native GHC+-- (@Language.Javascript.JSaddle.Int53@ re-exports whichever applies); keep+-- the two in step.+--+-----------------------------------------------------------------------------+module GHCJS.Int53 (+ -- * Signed+ Int53+ , int53+ , toInt53+ , clampInt53+ , fromInt53+ , minInt53+ , maxInt53+ -- * Unsigned+ , Word53+ , word53+ , toWord53+ , clampWord53+ , fromWord53+ , maxWord53+) where++import Data.Int (Int32, Int64)+import Data.Word (Word32, Word64)++import GHCJS.Marshal (ToJSVal(..), FromJSVal(..))+import GHCJS.Marshal.Pure (PToJSVal(..), PFromJSVal(..))++-- | An integer in the range @[-(2^53 - 1), 2^53 - 1]@.+newtype Int53 = Int53 Int64 deriving (Eq, Ord)++-- | An integer in the range @[0, 2^53 - 1]@.+newtype Word53 = Word53 Word64 deriving (Eq, Ord)++-- | @2^53 - 1@ (@Number.MAX_SAFE_INTEGER@)+safeMax :: Integer+safeMax = 2 ^ (53 :: Int) - 1++minInt53, maxInt53 :: Int53+minInt53 = Int53 (fromInteger (negate safeMax))+maxInt53 = Int53 (fromInteger safeMax)++maxWord53 :: Word53+maxWord53 = Word53 (fromInteger safeMax)++instance Bounded Int53 where+ minBound = minInt53+ maxBound = maxInt53++instance Bounded Word53 where+ minBound = Word53 0+ maxBound = maxWord53++-- | Shown (and read) as a plain number.+instance Show Int53 where+ showsPrec p (Int53 i) = showsPrec p i++instance Show Word53 where+ showsPrec p (Word53 w) = showsPrec p w++instance Read Int53 where+ readsPrec p s = [ (x, rest) | (i, rest) <- readsPrec p s, Just x <- [toInt53 (i :: Integer)] ]++instance Read Word53 where+ readsPrec p s = [ (x, rest) | (i, rest) <- readsPrec p s, Just x <- [toWord53 (i :: Integer)] ]++-- | Every 'Int32' fits (use it for literals: @int53 0@).+int53 :: Int32 -> Int53+int53 = Int53 . fromIntegral++-- | Every 'Word32' fits (use it for literals: @word53 0@).+word53 :: Word32 -> Word53+word53 = Word53 . fromIntegral++-- | 'Nothing' when out of range.+toInt53 :: Integral a => a -> Maybe Int53+toInt53 a+ | abs i <= safeMax = Just (Int53 (fromInteger i))+ | otherwise = Nothing+ where i = toInteger a++-- | 'Nothing' when out of range.+toWord53 :: Integral a => a -> Maybe Word53+toWord53 a+ | i >= 0 && i <= safeMax = Just (Word53 (fromInteger i))+ | otherwise = Nothing+ where i = toInteger a++-- | Saturating conversion (like Web IDL's @[Clamp]@, without rounding).+clampInt53 :: Integral a => a -> Int53+clampInt53 a = Int53 (fromInteger (max (negate safeMax) (min safeMax (toInteger a))))++-- | Saturating conversion (like Web IDL's @[Clamp]@, without rounding).+clampWord53 :: Integral a => a -> Word53+clampWord53 a = Word53 (fromInteger (max 0 (min safeMax (toInteger a))))++-- | Always exact for 'Int64', 'Integer' and 'Double' (and for any type+-- wide enough for the value).+fromInt53 :: Num b => Int53 -> b+fromInt53 (Int53 i) = fromIntegral i++-- | Always exact for 'Word64', 'Int64', 'Integer' and 'Double' (and for any+-- type wide enough for the value).+fromWord53 :: Num b => Word53 -> b+fromWord53 (Word53 w) = fromIntegral w++fromDouble :: (Integer -> Maybe a) -> Double -> Maybe a+fromDouble f d+ | isNaN d || isInfinite d = Nothing+ | fromInteger i == d = f i+ | otherwise = Nothing+ where i = truncate d++-- | A JavaScript Number (exact).+instance PToJSVal Int53 where+ pToJSVal (Int53 i) = pToJSVal (fromIntegral i :: Double)+ {-# INLINE pToJSVal #-}++-- | A JavaScript Number (exact).+instance PToJSVal Word53 where+ pToJSVal (Word53 w) = pToJSVal (fromIntegral w :: Double)+ {-# INLINE pToJSVal #-}++-- | Unchecked: truncates (and saturates) whatever Number it is given.+instance PFromJSVal Int53 where+ pFromJSVal v = clampInt53 (truncate (pFromJSVal v :: Double) :: Integer)+ {-# INLINE pFromJSVal #-}++-- | Unchecked: truncates (and saturates) whatever Number it is given.+instance PFromJSVal Word53 where+ pFromJSVal v = clampWord53 (truncate (pFromJSVal v :: Double) :: Integer)+ {-# INLINE pFromJSVal #-}++instance ToJSVal Int53 where+ toJSVal = return . pToJSVal+ {-# INLINE toJSVal #-}++instance ToJSVal Word53 where+ toJSVal = return . pToJSVal+ {-# INLINE toJSVal #-}++-- | 'Nothing' unless the value is a Number holding an integer in range.+instance FromJSVal Int53 where+ fromJSVal v = (>>= fromDouble toInt53) <$> fromJSVal v+ fromJSValUnchecked v = fromJSVal v >>= maybe (fail "fromJSValUnchecked: not an Int53") return++-- | 'Nothing' unless the value is a Number holding an integer in range.+instance FromJSVal Word53 where+ fromJSVal v = (>>= fromDouble toWord53) <$> fromJSVal v+ fromJSValUnchecked v = fromJSVal v >>= maybe (fail "fromJSValUnchecked: not a Word53") return
GHCJS/Marshal/Internal.hs view
@@ -258,3 +258,54 @@ packJSS :: String -> JSString packJSS = JSS.JSString . Prim.toJSString++-- -----------------------------------------------------------------------------+-- Instances for ghcjs-base's own JSVal newtypes. They live here, with the+-- classes, so they are not orphans and need no extra imports. jsaddle+-- provides the same instances for native GHC.++instance PToJSVal Object where+ pToJSVal (OI.Object v) = v+ {-# INLINE pToJSVal #-}++-- | Unchecked: wraps any value.+instance PFromJSVal Object where+ pFromJSVal = OI.Object+ {-# INLINE pFromJSVal #-}++instance ToJSVal Object where+ toJSVal (OI.Object v) = return v+ {-# INLINE toJSVal #-}++-- | 'Nothing' for @null@ and @undefined@ (as for the ghcjs-dom types);+-- 'fromJSValUnchecked' wraps any value.+instance FromJSVal Object where+ fromJSVal v+ | F.isNull v || F.isUndefined v = return Nothing+ | otherwise = return (Just (OI.Object v))+ {-# INLINE fromJSVal #-}+ fromJSValUnchecked = return . OI.Object+ {-# INLINE fromJSValUnchecked #-}++instance PToJSVal (AI.SomeJSArray m) where+ pToJSVal (AI.SomeJSArray v) = v+ {-# INLINE pToJSVal #-}++-- | Unchecked: wraps any value.+instance PFromJSVal (AI.SomeJSArray m) where+ pFromJSVal = AI.SomeJSArray+ {-# INLINE pFromJSVal #-}++instance ToJSVal (AI.SomeJSArray m) where+ toJSVal (AI.SomeJSArray v) = return v+ {-# INLINE toJSVal #-}++-- | 'Nothing' for @null@ and @undefined@; 'fromJSValUnchecked' wraps any+-- value (no @Array.isArray@ check, like the other JSVal newtypes).+instance FromJSVal (AI.SomeJSArray m) where+ fromJSVal v+ | F.isNull v || F.isUndefined v = return Nothing+ | otherwise = return (Just (AI.SomeJSArray v))+ {-# INLINE fromJSVal #-}+ fromJSValUnchecked = return . AI.SomeJSArray+ {-# INLINE fromJSValUnchecked #-}
ghcjs-base.cabal view
@@ -1,10 +1,15 @@ cabal-version: 3.0 name: ghcjs-base-version: 0.8.0.4+version: 0.8.1.0 synopsis: base library for GHCJS+description:+ JavaScript types, marshalling (GHCJS.Marshal), foreign helpers and+ JavaScript.* data structure bindings for the GHC JavaScript backend+ (and legacy GHCJS). homepage: http://github.com/ghcjs/ghcjs-base license: MIT license-file: LICENSE+extra-doc-files: ChangeLog.md author: Luite Stegeman maintainer: stegeman@gmail.com category: Web@@ -70,6 +75,7 @@ GHCJS.Foreign GHCJS.Foreign.Export GHCJS.Foreign.Internal+ GHCJS.Int53 GHCJS.Marshal GHCJS.Marshal.Internal GHCJS.Marshal.Pure@@ -134,7 +140,7 @@ scientific >= 0.3.7 && < 0.4, vector >= 0.10 && < 0.14, containers >= 0.5 && < 0.9,- time >= 1.5 && < 1.15,+ time >= 1.5 && < 1.16, hashable >= 1.2 && < 1.6, unordered-containers >= 0.2 && < 0.3, attoparsec >= 0.11 && < 0.15,