Skip to content

Commit 70660c3

Browse files
author
Ryan Scott
committed
Merge pull request #30 from haskell-compat/almost-all-your-base
More functions from base-4.7.0.0 and base-4.8.0.0
2 parents 95ae630 + 88d6486 commit 70660c3

File tree

14 files changed

+490
-0
lines changed

14 files changed

+490
-0
lines changed

CHANGES.markdown

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## Changes in next
2+
- Backport `bitDefault`, `testBitDefault`, and `popCountDefault` in
3+
`Data.Bits.Compat` to all versions of `base`
4+
- Backport `toIntegralSized` to `base-4.7`
5+
- Backport `nub` and `nubBy` (as well as `union` and `unionBy`, which are
6+
implemented in terms of them) to fix logic error in `Data.List.Compat`
7+
- Backport `byteSwap16`, `byteSwap32`, and `byteSwap64` to `Data.Word.Compat`
8+
- Backport `fillBytes` in `Foreign.Marshal.Utils.Compat`
9+
- Backport `showFFloatAlt` and `showGFloatAlt` to `Numeric.Compat`
10+
111
## Changes in 0.8.1.1
212
- Fixed Windows build
313

README.markdown

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ So far the following is covered.
8989
* `Text.Read.Compat.readMaybe`
9090
* `Text.Read.Compat.readEither`
9191
* `Data.Monoid.Compat.<>`
92+
* Added `bitDefault`, `testBitDefault`, and `popCountDefault` to `Data.Bits.Compat`
93+
* Added `toIntegralSized` to `Data.Bits.Compat` (if using `base-4.7`)
9294
* Added `bool` function to `Data.Bool.Compat`
9395
* Added `isLeft` and `isRight` to `Data.Either.Compat`
9496
* Added `withMVarMasked` function to `Control.Concurrent.MVar.Compat`
@@ -97,11 +99,15 @@ So far the following is covered.
9799
* `(&)` function to `Data.Function.Compat`
98100
* `($>)` and `void` functions to `Data.Functor.Compat`
99101
* `dropWhileEnd`, `isSubsequenceOf`, `sortOn`, and `uncons` functions to `Data.List.Compat`
102+
* Correct versions of `nub`, `nubBy`, `union`, and `unionBy` to `Data.List.Compat`
100103
* `makeVersion` function to `Data.Version.Compat`
101104
* `traceId`, `traceShowId`, `traceM`, and `traceShowM` functions to `Debug.Trace.Compat`
105+
* `byteSwap16`, `byteSwap32`, and `byteSwap64` to `Data.Word.Compat`
102106
* `calloc` and `callocBytes` functions to `Foreign.Marshal.Alloc.Compat`
103107
* `callocArray` and `callocArray0` functions to `Foreign.Marshal.Array.Compat`
108+
* `fillBytes` to `Foreign.Marshal.Utils.Compat`
104109
* Added `Data.List.Compat.scanl'`
110+
* `showFFloatAlt` and `showGFloatAlt` to `Numeric.Compat`
105111
* `lookupEnv`, `setEnv` and `unsetEnv` to `System.Environment.Compat`
106112

107113
## Supported versions of GHC/base

base-compat.cabal

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ library
4949
exposed-modules:
5050
Control.Concurrent.MVar.Compat
5151
Control.Monad.Compat
52+
Data.Bits.Compat
5253
Data.Bool.Compat
5354
Data.Either.Compat
5455
Data.Foldable.Compat
@@ -57,11 +58,14 @@ library
5758
Data.List.Compat
5859
Data.Monoid.Compat
5960
Data.Version.Compat
61+
Data.Word.Compat
6062
Debug.Trace.Compat
6163
Foreign.Compat
6264
Foreign.Marshal.Alloc.Compat
6365
Foreign.Marshal.Array.Compat
6466
Foreign.Marshal.Compat
67+
Foreign.Marshal.Utils.Compat
68+
Numeric.Compat
6569
Prelude.Compat
6670
System.Environment.Compat
6771
System.Exit.Compat

src/Data/Bits/Compat.hs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
{-# LANGUAGE CPP, NoImplicitPrelude #-}
2+
{-# LANGUAGE BangPatterns, PatternGuards #-}
3+
module Data.Bits.Compat (
4+
module Base
5+
, bitDefault
6+
, testBitDefault
7+
, popCountDefault
8+
#if MIN_VERSION_base(4,7,0)
9+
, toIntegralSized
10+
#endif
11+
) where
12+
13+
import Data.Bits as Base
14+
15+
#if !(MIN_VERSION_base(4,8,0))
16+
import Prelude
17+
#endif
18+
19+
#if !(MIN_VERSION_base(4,6,0))
20+
-- | Default implementation for 'bit'.
21+
--
22+
-- Note that: @bitDefault i = 1 `shiftL` i@
23+
--
24+
-- /Since: 4.6.0.0/
25+
bitDefault :: (Bits a, Num a) => Int -> a
26+
bitDefault = \i -> 1 `shiftL` i
27+
{-# INLINE bitDefault #-}
28+
29+
-- | Default implementation for 'testBit'.
30+
--
31+
-- Note that: @testBitDefault x i = (x .&. bit i) /= 0@
32+
--
33+
-- /Since: 4.6.0.0/
34+
testBitDefault :: (Bits a, Num a) => a -> Int -> Bool
35+
testBitDefault = \x i -> (x .&. bit i) /= 0
36+
{-# INLINE testBitDefault #-}
37+
38+
-- | Default implementation for 'popCount'.
39+
--
40+
-- This implementation is intentionally naive. Instances are expected to provide
41+
-- an optimized implementation for their size.
42+
--
43+
-- /Since: 4.6.0.0/
44+
popCountDefault :: (Bits a, Num a) => a -> Int
45+
popCountDefault = go 0
46+
where
47+
go !c 0 = c
48+
go c w = go (c+1) (w .&. (w - 1)) -- clear the least significant
49+
{-# INLINABLE popCountDefault #-}
50+
#endif
51+
52+
#if MIN_VERSION_base(4,7,0) && !(MIN_VERSION_base(4,8,0))
53+
-- | Attempt to convert an 'Integral' type @a@ to an 'Integral' type @b@ using
54+
-- the size of the types as measured by 'Bits' methods.
55+
--
56+
-- A simpler version of this function is:
57+
--
58+
-- > toIntegral :: (Integral a, Integral b) => a -> Maybe b
59+
-- > toIntegral x
60+
-- > | toInteger x == y = Just (fromInteger y)
61+
-- > | otherwise = Nothing
62+
-- > where
63+
-- > y = toInteger x
64+
--
65+
-- This version requires going through 'Integer', which can be inefficient.
66+
-- However, @toIntegralSized@ is optimized to allow GHC to statically determine
67+
-- the relative type sizes (as measured by 'bitSizeMaybe' and 'isSigned') and
68+
-- avoid going through 'Integer' for many types. (The implementation uses
69+
-- 'fromIntegral', which is itself optimized with rules for @base@ types but may
70+
-- go through 'Integer' for some type pairs.)
71+
--
72+
-- /Since: 4.8.0.0/
73+
74+
toIntegralSized :: (Integral a, Integral b, Bits a, Bits b) => a -> Maybe b
75+
toIntegralSized x -- See Note [toIntegralSized optimization]
76+
| maybe True (<= x) yMinBound
77+
, maybe True (x <=) yMaxBound = Just y
78+
| otherwise = Nothing
79+
where
80+
y = fromIntegral x
81+
82+
xWidth = bitSizeMaybe x
83+
yWidth = bitSizeMaybe y
84+
85+
yMinBound
86+
| isBitSubType x y = Nothing
87+
| isSigned x, not (isSigned y) = Just 0
88+
| isSigned x, isSigned y
89+
, Just yW <- yWidth = Just (negate $ bit (yW-1)) -- Assumes sub-type
90+
| otherwise = Nothing
91+
92+
yMaxBound
93+
| isBitSubType x y = Nothing
94+
| isSigned x, not (isSigned y)
95+
, Just xW <- xWidth, Just yW <- yWidth
96+
, xW <= yW+1 = Nothing -- Max bound beyond a's domain
97+
| Just yW <- yWidth = if isSigned y
98+
then Just (bit (yW-1)-1)
99+
else Just (bit yW-1)
100+
| otherwise = Nothing
101+
{-# INLINEABLE toIntegralSized #-}
102+
103+
-- | 'True' if the size of @a@ is @<=@ the size of @b@, where size is measured
104+
-- by 'bitSizeMaybe' and 'isSigned'.
105+
isBitSubType :: (Bits a, Bits b) => a -> b -> Bool
106+
isBitSubType x y
107+
-- Reflexive
108+
| xWidth == yWidth, xSigned == ySigned = True
109+
110+
-- Every integer is a subset of 'Integer'
111+
| ySigned, Nothing == yWidth = True
112+
| not xSigned, not ySigned, Nothing == yWidth = True
113+
114+
-- Sub-type relations between fixed-with types
115+
| xSigned == ySigned, Just xW <- xWidth, Just yW <- yWidth = xW <= yW
116+
| not xSigned, ySigned, Just xW <- xWidth, Just yW <- yWidth = xW < yW
117+
118+
| otherwise = False
119+
where
120+
xWidth = bitSizeMaybe x
121+
xSigned = isSigned x
122+
123+
yWidth = bitSizeMaybe y
124+
ySigned = isSigned y
125+
{-# INLINE isBitSubType #-}
126+
#endif

src/Data/List/Compat.hs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@ module Data.List.Compat (
2121
, minimum
2222
, minimumBy
2323
, notElem
24+
, nub
25+
, nubBy
2426
, null
2527
, or
2628
, product
2729
, sum
30+
, union
31+
, unionBy
2832
, mapAccumL
2933
, mapAccumR
3034

@@ -61,10 +65,14 @@ import Data.List as Base hiding (
6165
, minimum
6266
, minimumBy
6367
, notElem
68+
, nub
69+
, nubBy
6470
, null
6571
, or
6672
, product
6773
, sum
74+
, union
75+
, unionBy
6876
, mapAccumL
6977
, mapAccumR
7078
)
@@ -141,4 +149,54 @@ scanl' = scanlGo'
141149
scanlGo' f !q ls = q : (case ls of
142150
[] -> []
143151
x:xs -> scanlGo' f (f q x) xs)
152+
153+
-- | /O(n^2)/. The 'nub' function removes duplicate elements from a list.
154+
-- In particular, it keeps only the first occurrence of each element.
155+
-- (The name 'nub' means \`essence\'.)
156+
-- It is a special case of 'nubBy', which allows the programmer to supply
157+
-- their own equality test.
158+
nub :: (Eq a) => [a] -> [a]
159+
nub = nubBy (==)
160+
161+
162+
-- | The 'nubBy' function behaves just like 'nub', except it uses a
163+
-- user-supplied equality predicate instead of the overloaded '=='
164+
-- function.
165+
nubBy :: (a -> a -> Bool) -> [a] -> [a]
166+
-- stolen from HBC
167+
nubBy eq l = nubBy' l []
168+
where
169+
nubBy' [] _ = []
170+
nubBy' (y:ys) xs
171+
| elem_by eq y xs = nubBy' ys xs
172+
| otherwise = y : nubBy' ys (y:xs)
173+
174+
-- Not exported:
175+
-- Note that we keep the call to `eq` with arguments in the
176+
-- same order as in the reference (prelude) implementation,
177+
-- and that this order is different from how `elem` calls (==).
178+
-- See #2528, #3280 and #7913.
179+
-- 'xs' is the list of things we've seen so far,
180+
-- 'y' is the potential new element
181+
elem_by :: (a -> a -> Bool) -> a -> [a] -> Bool
182+
elem_by _ _ [] = False
183+
elem_by eq y (x:xs) = x `eq` y || elem_by eq y xs
184+
185+
-- | The 'union' function returns the list union of the two lists.
186+
-- For example,
187+
--
188+
-- > "dog" `union` "cow" == "dogcw"
189+
--
190+
-- Duplicates, and elements of the first list, are removed from the
191+
-- the second list, but if the first list contains duplicates, so will
192+
-- the result.
193+
-- It is a special case of 'unionBy', which allows the programmer to supply
194+
-- their own equality test.
195+
196+
union :: (Eq a) => [a] -> [a] -> [a]
197+
union = unionBy (==)
198+
199+
-- | The 'unionBy' function is the non-overloaded version of 'union'.
200+
unionBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
201+
unionBy eq xs ys = xs ++ foldl (flip (deleteBy eq)) (nubBy eq ys) xs
144202
#endif

src/Data/Word/Compat.hs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{-# LANGUAGE CPP, NoImplicitPrelude #-}
2+
module Data.Word.Compat (
3+
module Base
4+
, byteSwap16
5+
, byteSwap32
6+
, byteSwap64
7+
) where
8+
9+
import Data.Word as Base
10+
11+
#if !(MIN_VERSION_base(4,7,0))
12+
import Data.Bits
13+
14+
-- | Swap bytes in 'Word16'.
15+
--
16+
-- /Since: 4.7.0.0/
17+
byteSwap16 :: Word16 -> Word16
18+
byteSwap16 w = ((w `shiftR` 8) .&. 0x00ff)
19+
.|. ((w .&. 0x00ff) `shiftL` 8)
20+
21+
-- | Reverse order of bytes in 'Word32'.
22+
--
23+
-- /Since: 4.7.0.0/
24+
byteSwap32 :: Word32 -> Word32
25+
byteSwap32 w = ((w .&. 0xff000000) `shiftR` 24)
26+
.|. ((w .&. 0x00ff0000) `shiftR` 8)
27+
.|. ((w .&. 0x0000ff00) `shiftL` 8)
28+
.|. ((w .&. 0x000000ff) `shiftL` 24)
29+
30+
-- | Reverse order of bytes in 'Word64'.
31+
--
32+
-- /Since: 4.7.0.0/
33+
byteSwap64 :: Word64 -> Word64
34+
byteSwap64 w = ((w .&. 0xff00000000000000) `shiftR` 56)
35+
.|. ((w .&. 0x00ff000000000000) `shiftR` 40)
36+
.|. ((w .&. 0x0000ff0000000000) `shiftR` 24)
37+
.|. ((w .&. 0x000000ff00000000) `shiftR` 8)
38+
.|. ((w .&. 0x00000000ff000000) `shiftL` 8)
39+
.|. ((w .&. 0x0000000000ff0000) `shiftL` 24)
40+
.|. ((w .&. 0x000000000000ff00) `shiftL` 40)
41+
.|. ((w .&. 0x00000000000000ff) `shiftL` 56)
42+
#endif

src/Foreign/Marshal/Compat.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ module Foreign.Marshal.Compat (
33
module Base
44
, module Alloc
55
, module Array
6+
, module Utils
67
) where
78
import Foreign.Marshal as Base
89

910
import Foreign.Marshal.Alloc.Compat as Alloc
1011
import Foreign.Marshal.Array.Compat as Array
12+
import Foreign.Marshal.Utils.Compat as Utils

src/Foreign/Marshal/Utils/Compat.hs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{-# LANGUAGE CPP, NoImplicitPrelude #-}
2+
{-# LANGUAGE ForeignFunctionInterface #-}
3+
module Foreign.Marshal.Utils.Compat (
4+
module Base
5+
, fillBytes
6+
) where
7+
8+
import Foreign.Marshal.Utils as Base
9+
10+
#if !(MIN_VERSION_base(4,8,0))
11+
import Data.Word (Word8)
12+
import Foreign.C.Types
13+
import Foreign.Ptr
14+
import Prelude
15+
16+
-- |Fill a given number of bytes in memory area with a byte value.
17+
--
18+
-- /Since: 4.8.0.0/
19+
fillBytes :: Ptr a -> Word8 -> Int -> IO ()
20+
fillBytes dest char size = do
21+
_ <- memset dest (fromIntegral char) (fromIntegral size)
22+
return ()
23+
24+
foreign import ccall unsafe "string.h" memset :: Ptr a -> CInt -> CSize -> IO (Ptr a)
25+
#endif

0 commit comments

Comments
 (0)