diff --git a/LICENSE b/LICENSE index 58b02997..311379c1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,20 +1,26 @@ -The MIT License (MIT) +Copyright 2018 PureScript -Copyright (c) 2014 PureScript +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bower.json b/bower.json index a8fc8a4c..4e217646 100644 --- a/bower.json +++ b/bower.json @@ -1,8 +1,7 @@ { "name": "purescript-arrays", "homepage": "https://github.com/purescript/purescript-arrays", - "description": "Array utility functions", - "license": "MIT", + "license": "BSD-3-Clause", "repository": { "type": "git", "url": "git://github.com/purescript/purescript-arrays.git" @@ -16,18 +15,22 @@ "package.json" ], "dependencies": { - "purescript-foldable-traversable": "^3.3.0", - "purescript-nonempty": "^4.0.0", - "purescript-partial": "^1.2.0", - "purescript-st": "^3.0.0", - "purescript-tailrec": "^3.0.0", - "purescript-tuples": "^4.0.0", - "purescript-unfoldable": "^3.0.0", - "purescript-unsafe-coerce": "^3.0.0" + "purescript-bifunctors": "^4.0.0", + "purescript-control": "^4.0.0", + "purescript-foldable-traversable": "^4.0.0", + "purescript-maybe": "^4.0.0", + "purescript-nonempty": "^5.0.0", + "purescript-partial": "^2.0.0", + "purescript-prelude": "^4.0.0", + "purescript-st": "^4.0.0", + "purescript-tailrec": "^4.0.0", + "purescript-tuples": "^5.0.0", + "purescript-unfoldable": "^4.0.0", + "purescript-unsafe-coerce": "^4.0.0" }, "devDependencies": { - "purescript-assert": "^3.0.0", - "purescript-console": "^3.0.0", - "purescript-const": "^3.0.0" + "purescript-assert": "^4.0.0", + "purescript-console": "^4.0.0", + "purescript-const": "^4.0.0" } } diff --git a/package.json b/package.json index 132cefcd..66150396 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "test": "pulp test" }, "devDependencies": { - "eslint": "^3.17.1", - "pulp": "^10.0.4", - "purescript-psa": "^0.5.0-rc.1", - "rimraf": "^2.6.1" + "eslint": "^4.19.1", + "pulp": "^12.2.0", + "purescript-psa": "^0.6.0", + "rimraf": "^2.6.2" } } diff --git a/src/Data/Array.js b/src/Data/Array.js index 07b65395..3cf115cf 100644 --- a/src/Data/Array.js +++ b/src/Data/Array.js @@ -40,9 +40,7 @@ var replicatePolyfill = function (count) { }; // In browsers that have Array.prototype.fill we use it, as it's faster. -exports.replicate = typeof Array.prototype.fill === "function" ? - replicate : - replicatePolyfill; +exports.replicate = typeof Array.prototype.fill === "function" ? replicate : replicatePolyfill; exports.fromFoldableImpl = (function () { function Cons(head, tail) { diff --git a/src/Data/Array.purs b/src/Data/Array.purs index 2376238a..4ddcfafc 100644 --- a/src/Data/Array.purs +++ b/src/Data/Array.purs @@ -25,7 +25,7 @@ -- | `Data.Foldable.or` tests whether an array of `Boolean` values contains -- | at least one `true` value. -- | * `Traversable`, which provides the PureScript version of a for-loop, --- | allowing you to iterate over an array and accumulate effects. +-- | allowing you to STAI.iterate over an array and accumulate effects. -- | module Data.Array ( fromFoldable @@ -90,7 +90,9 @@ module Data.Array , groupBy , nub + , nubEq , nubBy + , nubByEq , union , unionBy , delete @@ -114,22 +116,24 @@ module Data.Array ) where import Prelude + import Control.Alt ((<|>)) import Control.Alternative (class Alternative) import Control.Lazy (class Lazy, defer) import Control.Monad.Rec.Class (class MonadRec, Step(..), tailRecM2) -import Control.Monad.ST (pureST) -import Data.Array.ST (unsafeFreeze, emptySTArray, pokeSTArray, pushSTArray, modifySTArray, withArray) -import Data.Array.ST.Iterator (iterator, iterate, pushWhile) +import Control.Monad.ST as ST +import Data.Array.ST as STA +import Data.Array.ST.Iterator as STAI +import Data.Array.NonEmpty.Internal (NonEmptyArray) import Data.Foldable (class Foldable, foldl, foldr, traverse_) import Data.Foldable (foldl, foldr, foldMap, fold, intercalate, elem, notElem, find, findMap, any, all) as Exports import Data.Maybe (Maybe(..), maybe, isJust, fromJust) -import Data.NonEmpty (NonEmpty, (:|)) import Data.Traversable (scanl, scanr) as Exports import Data.Traversable (sequence, traverse) -import Data.Tuple (Tuple(..), uncurry) +import Data.Tuple (Tuple(..), fst, snd) import Data.Unfoldable (class Unfoldable, unfoldr) import Partial.Unsafe (unsafePartial) +import Unsafe.Coerce (unsafeCoerce) -- | Convert an `Array` into an `Unfoldable` structure. toUnfoldable :: forall f. Unfoldable f => Array ~> f @@ -299,7 +303,7 @@ last xs = xs !! (length xs - 1) -- | `Nothing` if the array is empty -- | -- | ```purescript --- | tail [1, 2, 3, 4] = Just [2, 3, 4] +-- | tail [1, 2, 3, 4] = Just [2, 3, 4] -- | tail [] = Nothing -- | ``` -- | @@ -348,7 +352,7 @@ foreign import uncons' -- | Break an array into its last element and all preceding elements. -- | -- | ```purescript --- | unsnoc [1, 2, 3] = Just {init: [1, 2], last: 3} +-- | unsnoc [1, 2, 3] = Just {init: [1, 2], last: 3} -- | unsnoc [] = Nothing -- | ``` -- | @@ -511,7 +515,7 @@ foreign import _updateAt -- | array, or returning `Nothing` if the index is out of bounds. -- | -- | ```purescript --- | modifyAt 1 toUpper ["Hello", "World"] = Just ["Hello", "WORLD"] +-- | modifyAt 1 toUpper ["Hello", "World"] = Just ["Hello", "WORLD"] -- | modifyAt 10 toUpper ["Hello", "World"] = Nothing -- | ``` -- | @@ -525,10 +529,10 @@ modifyAt i f xs = maybe Nothing go (xs !! i) -- | index is out-of-bounds. -- | -- | ```purescript --- | alterAt 1 (stripSuffix $ Pattern "!") ["Hello", "World!"] +-- | alterAt 1 (stripSuffix $ Pattern "!") ["Hello", "World!"] -- | = Just ["Hello", "World"] -- | --- | alterAt 1 (stripSuffix $ Pattern "!!!!!") ["Hello", "World!"] +-- | alterAt 1 (stripSuffix $ Pattern "!!!!!") ["Hello", "World!"] -- | = Just ["Hello"] -- | -- | alterAt 10 (stripSuffix $ Pattern "!") ["Hello", "World!"] = Nothing @@ -629,7 +633,7 @@ mapMaybe f = concatMap (maybe [] singleton <<< f) -- | ``` -- | catMaybes :: forall a. Array (Maybe a) -> Array a -catMaybes = mapMaybe id +catMaybes = mapMaybe identity -- | Apply a function to each element in an array, supplying a generated -- | zero-based index integer along with the element, creating an array @@ -656,7 +660,7 @@ mapWithIndex f xs = -- | updateAtIndices :: forall t a. Foldable t => t (Tuple Int a) -> Array a -> Array a updateAtIndices us xs = - pureST (withArray (\res -> traverse_ (uncurry $ pokeSTArray res) us) xs) + ST.run (STA.withArray (\res -> traverse_ (\(Tuple i a) -> STA.poke i a res) us) xs) -- | Apply a function to the element at the specified indices, -- | creating a new array. Out-of-bounds indices will have no effect. @@ -669,7 +673,7 @@ updateAtIndices us xs = -- | modifyAtIndices :: forall t a. Foldable t => t Int -> (a -> a) -> Array a -> Array a modifyAtIndices is f xs = - pureST (withArray (\res -> traverse_ (\i -> modifySTArray res i f) is) xs) + ST.run (STA.withArray (\res -> traverse_ (\i -> STA.modify i f res) is) xs) -------------------------------------------------------------------------------- -- Sorting --------------------------------------------------------------------- @@ -836,7 +840,7 @@ span p arr = -- | ```purescript -- | group [1,1,2,2,1] == [NonEmpty 1 [1], NonEmpty 2 [2], NonEmpty 1 []] -- | ``` -group :: forall a. Eq a => Array a -> Array (NonEmpty Array a) +group :: forall a. Eq a => Array a -> Array (NonEmptyArray a) group xs = groupBy eq xs -- | Sort and then group the elements of an array into arrays. @@ -844,7 +848,7 @@ group xs = groupBy eq xs -- | ```purescript -- | group' [1,1,2,2,1] == [NonEmpty 1 [1,1],NonEmpty 2 [2]] -- | ``` -group' :: forall a. Ord a => Array a -> Array (NonEmpty Array a) +group' :: forall a. Ord a => Array a -> Array (NonEmptyArray a) group' = group <<< sort -- | Group equal, consecutive elements of an array into arrays, using the @@ -855,17 +859,18 @@ group' = group <<< sort -- | = [NonEmpty 1 [3], NonEmpty 2 [] , NonEmpty 4 [], NonEmpty 3 [3]] -- | ``` -- | -groupBy :: forall a. (a -> a -> Boolean) -> Array a -> Array (NonEmpty Array a) +groupBy :: forall a. (a -> a -> Boolean) -> Array a -> Array (NonEmptyArray a) groupBy op xs = - pureST do - result <- emptySTArray - iter <- iterator (xs !! _) - iterate iter \x -> void do - sub <- emptySTArray - pushWhile (op x) iter sub - sub_ <- unsafeFreeze sub - pushSTArray result (x :| sub_) - unsafeFreeze result + ST.run do + result <- STA.empty + iter <- STAI.iterator (xs !! _) + STAI.iterate iter \x -> void do + sub <- STA.empty + STAI.pushWhile (op x) iter sub + _ <- STA.push x sub + grp <- STA.unsafeFreeze sub + STA.push ((unsafeCoerce :: Array ~> NonEmptyArray) grp) result + STA.unsafeFreeze result -- | Remove the duplicates from an array, creating a new array. -- | @@ -873,20 +878,56 @@ groupBy op xs = -- | nub [1, 2, 1, 3, 3] = [1, 2, 3] -- | ``` -- | -nub :: forall a. Eq a => Array a -> Array a -nub = nubBy eq +nub :: forall a. Ord a => Array a -> Array a +nub = nubBy compare + +-- | Remove the duplicates from an array, creating a new array. +-- | +-- | This less efficient version of `nub` only requires an `Eq` instance. +-- | +-- | ```purescript +-- | nubEq [1, 2, 1, 3, 3] = [1, 2, 3] +-- | ``` +-- | +nubEq :: forall a. Eq a => Array a -> Array a +nubEq = nubByEq eq + +-- | Remove the duplicates from an array, where element equality is determined +-- | by the specified ordering, creating a new array. +-- | +-- | ```purescript +-- | nubBy compare [1, 3, 4, 2, 2, 1] == [1, 3, 4, 2] +-- | ``` +-- | +nubBy :: forall a. (a -> a -> Ordering) -> Array a -> Array a +nubBy comp xs = case head indexedAndSorted of + Nothing -> [] + Just x -> map snd $ sortWith fst $ ST.run do + -- TODO: use NonEmptyArrays here to avoid partial functions + result <- STA.unsafeThaw $ singleton x + ST.foreach indexedAndSorted \pair@(Tuple i x') -> do + lst <- snd <<< unsafePartial (fromJust <<< last) <$> STA.unsafeFreeze result + when (comp lst x' /= EQ) $ void $ STA.push pair result + STA.unsafeFreeze result + where + indexedAndSorted :: Array (Tuple Int a) + indexedAndSorted = sortBy (\x y -> comp (snd x) (snd y)) + (mapWithIndex Tuple xs) -- | Remove the duplicates from an array, where element equality is determined -- | by the specified equivalence relation, creating a new array. -- | +-- | This less efficient version of `nubBy` only requires an equivalence +-- | relation. +-- | -- | ```purescript --- | nubBy (\a b -> a `mod` 3 == b `mod` 3) [1, 3, 4, 5, 6] = [1,3,5] +-- | nubByEq (\a b -> a `mod` 3 == b `mod` 3) [1, 3, 4, 5, 6] = [1,3,5] -- | ``` -- | -nubBy :: forall a. (a -> a -> Boolean) -> Array a -> Array a -nubBy eq xs = +nubByEq :: forall a. (a -> a -> Boolean) -> Array a -> Array a +nubByEq eq xs = case uncons xs of - Just o -> o.head : nubBy eq (filter (\y -> not (o.head `eq` y)) o.tail) + Just o -> o.head : nubByEq eq (filter (\y -> not (o.head `eq` y)) o.tail) Nothing -> [] -- | Calculate the union of two arrays. Note that duplicates in the first array @@ -911,7 +952,7 @@ union = unionBy (==) -- | ``` -- | unionBy :: forall a. (a -> a -> Boolean) -> Array a -> Array a -> Array a -unionBy eq xs ys = xs <> foldl (flip (deleteBy eq)) (nubBy eq ys) xs +unionBy eq xs ys = xs <> foldl (flip (deleteBy eq)) (nubByEq eq ys) xs -- | Delete the first element of an array which is equal to the specified value, -- | creating a new array. @@ -1016,7 +1057,7 @@ zipWithA f xs ys = sequence (zipWith f xs ys) -- | discarded. -- | -- | ```purescript --- | zip [1, 2, 3] ["a", "b"] = [Tuple 1 "a", Tuple 2 "b"] +-- | zip [1, 2, 3] ["a", "b"] = [Tuple 1 "a", Tuple 2 "b"] -- | ``` -- | zip :: forall a b. Array a -> Array b -> Array (Tuple a b) @@ -1031,15 +1072,15 @@ zip = zipWith Tuple -- | unzip :: forall a b. Array (Tuple a b) -> Tuple (Array a) (Array b) unzip xs = - pureST do - fsts <- emptySTArray - snds <- emptySTArray - iter <- iterator (xs !! _) - iterate iter \(Tuple fst snd) -> do - void $ pushSTArray fsts fst - void $ pushSTArray snds snd - fsts' <- unsafeFreeze fsts - snds' <- unsafeFreeze snds + ST.run do + fsts <- STA.empty + snds <- STA.empty + iter <- STAI.iterator (xs !! _) + STAI.iterate iter \(Tuple fst snd) -> do + void $ STA.push fst fsts + void $ STA.push snd snds + fsts' <- STA.unsafeFreeze fsts + snds' <- STA.unsafeFreeze snds pure $ Tuple fsts' snds' -- | Perform a fold using a monadic step function. @@ -1065,12 +1106,12 @@ foldRecM f a array = tailRecM2 go a 0 -- | ```purescript -- | unsafePartial $ unsafeIndex ["a", "b", "c"] 1 = "b" -- | ``` --- | --- | Using `unsafeIndex` with an out-of-range index will not immediately raise a runtime error. --- | Instead, the result will be undefined. Most attempts to subsequently use the result will --- | cause a runtime error, of course, but this is not guaranteed, and is dependent on the backend; --- | some programs will continue to run as if nothing is wrong. For example, in the JavaScript backend, --- | the expression `unsafePartial (unsafeIndex [true] 1)` has type `Boolean`; +-- | +-- | Using `unsafeIndex` with an out-of-range index will not immediately raise a runtime error. +-- | Instead, the result will be undefined. Most attempts to subsequently use the result will +-- | cause a runtime error, of course, but this is not guaranteed, and is dependent on the backend; +-- | some programs will continue to run as if nothing is wrong. For example, in the JavaScript backend, +-- | the expression `unsafePartial (unsafeIndex [true] 1)` has type `Boolean`; -- | since this expression evaluates to `undefined`, attempting to use it in an `if` statement will cause -- | the else branch to be taken. unsafeIndex :: forall a. Partial => Array a -> Int -> a diff --git a/src/Data/Array/NonEmpty.purs b/src/Data/Array/NonEmpty.purs index 46fa59cc..d987fd8f 100644 --- a/src/Data/Array/NonEmpty.purs +++ b/src/Data/Array/NonEmpty.purs @@ -1,5 +1,5 @@ module Data.Array.NonEmpty - ( NonEmptyArray + ( module Data.Array.NonEmpty.Internal , fromArray , fromNonEmpty , toArray @@ -8,6 +8,7 @@ module Data.Array.NonEmpty , fromFoldable , fromFoldable1 , toUnfoldable + , toUnfoldable1 , singleton , (..), range , replicate @@ -66,6 +67,8 @@ module Data.Array.NonEmpty , nub , nubBy + , nubEq + , nubByEq , union , union' , unionBy @@ -93,73 +96,28 @@ module Data.Array.NonEmpty import Prelude -import Control.Alt (class Alt) import Control.Alternative (class Alternative) import Control.Lazy (class Lazy) import Control.Monad.Rec.Class (class MonadRec) import Data.Array as A +import Data.Array.NonEmpty.Internal (NonEmptyArray) import Data.Bifunctor (bimap) -import Data.Eq (class Eq1) import Data.Foldable (class Foldable) -import Data.FoldableWithIndex (class FoldableWithIndex) -import Data.FunctorWithIndex (class FunctorWithIndex) import Data.Maybe (Maybe(..), fromJust) import Data.NonEmpty (NonEmpty, (:|)) -import Data.Ord (class Ord1) -import Data.Semigroup.Foldable (class Foldable1, foldMap1Default) -import Data.Semigroup.Traversable (class Traversable1, sequence1Default) -import Data.Traversable (class Traversable) -import Data.TraversableWithIndex (class TraversableWithIndex) -import Data.Tuple (Tuple) +import Data.Semigroup.Foldable (class Foldable1) +import Data.Tuple (Tuple(..)) import Data.Unfoldable (class Unfoldable) +import Data.Unfoldable1 (class Unfoldable1, unfoldr1) import Partial.Unsafe (unsafePartial) - -newtype NonEmptyArray a = NonEmptyArray (Array a) - -instance showNonEmptyArray :: Show a => Show (NonEmptyArray a) where - show (NonEmptyArray xs) = "(NonEmptyArray " <> show xs <> ")" - -derive newtype instance eqNonEmptyArray :: Eq a => Eq (NonEmptyArray a) -derive newtype instance eq1NonEmptyArray :: Eq1 NonEmptyArray - -derive newtype instance ordNonEmptyArray :: Ord a => Ord (NonEmptyArray a) -derive newtype instance ord1NonEmptyArray :: Ord1 NonEmptyArray - -derive newtype instance semigroupNonEmptyArray :: Semigroup (NonEmptyArray a) - -derive newtype instance functorNonEmptyArray :: Functor NonEmptyArray -derive newtype instance functorWithIndexNonEmptyArray :: FunctorWithIndex Int NonEmptyArray - -derive newtype instance foldableNonEmptyArray :: Foldable NonEmptyArray -derive newtype instance foldableWithIndexNonEmptyArray :: FoldableWithIndex Int NonEmptyArray - -instance foldable1NonEmptyArray :: Foldable1 NonEmptyArray where - foldMap1 = foldMap1Default - fold1 = fold1Impl (<>) - -derive newtype instance traversableNonEmptyArray :: Traversable NonEmptyArray -derive newtype instance traversableWithIndexNonEmptyArray :: TraversableWithIndex Int NonEmptyArray - -instance traversable1NonEmptyArray :: Traversable1 NonEmptyArray where - traverse1 = traverse1Impl apply map - sequence1 = sequence1Default - -derive newtype instance applyNonEmptyArray :: Apply NonEmptyArray - -derive newtype instance applicativeNonEmptyArray :: Applicative NonEmptyArray - -derive newtype instance bindNonEmptyArray :: Bind NonEmptyArray - -derive newtype instance monadNonEmptyArray :: Monad NonEmptyArray - -derive newtype instance altNonEmptyArray :: Alt NonEmptyArray +import Unsafe.Coerce (unsafeCoerce) -- | Internal - adapt an Array transform to NonEmptyArray -- -- Note that this is unsafe: if the transform returns an empty array, this can -- explode at runtime. unsafeAdapt :: forall a b. (Array a -> Array b) -> NonEmptyArray a -> NonEmptyArray b -unsafeAdapt f = NonEmptyArray <<< adaptAny f +unsafeAdapt f = unsafeFromArray <<< adaptAny f -- | Internal - adapt an Array transform to NonEmptyArray, -- with polymorphic result. @@ -175,18 +133,21 @@ adaptMaybe f = unsafePartial $ fromJust <<< f <<< toArray fromArray :: forall a. Array a -> Maybe (NonEmptyArray a) fromArray xs - | A.length xs > 0 = Just (NonEmptyArray xs) - | otherwise = Nothing + | A.length xs > 0 = Just (unsafeFromArray xs) + | otherwise = Nothing -- | INTERNAL unsafeFromArray :: forall a. Array a -> NonEmptyArray a -unsafeFromArray = NonEmptyArray +unsafeFromArray = unsafeCoerce + +unsafeFromArrayF :: forall f a. f (Array a) -> f (NonEmptyArray a) +unsafeFromArrayF = unsafeCoerce fromNonEmpty :: forall a. NonEmpty Array a -> NonEmptyArray a fromNonEmpty (x :| xs) = cons' x xs toArray :: forall a. NonEmptyArray a -> Array a -toArray (NonEmptyArray xs) = xs +toArray = unsafeCoerce toNonEmpty :: forall a. NonEmptyArray a -> NonEmpty Array a toNonEmpty = uncons >>> \{head: x, tail: xs} -> x :| xs @@ -200,8 +161,15 @@ fromFoldable1 = unsafeFromArray <<< A.fromFoldable toUnfoldable :: forall f a. Unfoldable f => NonEmptyArray a -> f a toUnfoldable = adaptAny A.toUnfoldable +toUnfoldable1 :: forall f a. Unfoldable1 f => NonEmptyArray a -> f a +toUnfoldable1 xs = unfoldr1 f 0 + where + len = length xs + f i = Tuple (unsafePartial unsafeIndex xs i) $ + if i < (len - 1) then Just (i + 1) else Nothing + singleton :: forall a. a -> NonEmptyArray a -singleton = NonEmptyArray <<< A.singleton +singleton = unsafeFromArray <<< A.singleton range :: Int -> Int -> NonEmptyArray Int range x y = unsafeFromArray $ A.range x y @@ -210,14 +178,14 @@ infix 8 range as .. -- | Replicate an item at least once replicate :: forall a. Int -> a -> NonEmptyArray a -replicate i x = NonEmptyArray $ A.replicate (max 1 i) x +replicate i x = unsafeFromArray $ A.replicate (max 1 i) x some :: forall f a . Alternative f => Lazy (f (Array a)) => f a -> f (NonEmptyArray a) -some = map NonEmptyArray <<< A.some +some = unsafeFromArrayF <<< A.some length :: forall a. NonEmptyArray a -> Int length = adaptAny A.length @@ -281,19 +249,19 @@ findLastIndex :: forall a. (a -> Boolean) -> NonEmptyArray a -> Maybe Int findLastIndex x = adaptAny $ A.findLastIndex x insertAt :: forall a. Int -> a -> NonEmptyArray a -> Maybe (NonEmptyArray a) -insertAt i x = map NonEmptyArray <<< A.insertAt i x <<< toArray +insertAt i x = unsafeFromArrayF <<< A.insertAt i x <<< toArray deleteAt :: forall a. Int -> NonEmptyArray a -> Maybe (Array a) deleteAt i = adaptAny $ A.deleteAt i updateAt :: forall a. Int -> a -> NonEmptyArray a -> Maybe (NonEmptyArray a) -updateAt i x = map NonEmptyArray <<< A.updateAt i x <<< toArray +updateAt i x = unsafeFromArrayF <<< A.updateAt i x <<< toArray updateAtIndices :: forall t a. Foldable t => t (Tuple Int a) -> NonEmptyArray a -> NonEmptyArray a updateAtIndices pairs = unsafeAdapt $ A.updateAtIndices pairs modifyAt :: forall a. Int -> (a -> a) -> NonEmptyArray a -> Maybe (NonEmptyArray a) -modifyAt i f = map NonEmptyArray <<< A.modifyAt i f <<< toArray +modifyAt i f = unsafeFromArrayF <<< A.modifyAt i f <<< toArray modifyAtIndices :: forall t a. Foldable t => t Int -> (a -> a) -> NonEmptyArray a -> NonEmptyArray a modifyAtIndices is f = unsafeAdapt $ A.modifyAtIndices is f @@ -305,7 +273,7 @@ reverse :: forall a. NonEmptyArray a -> NonEmptyArray a reverse = unsafeAdapt A.reverse concat :: forall a. NonEmptyArray (NonEmptyArray a) -> NonEmptyArray a -concat = NonEmptyArray <<< A.concat <<< toArray <<< map toArray +concat = unsafeFromArray <<< A.concat <<< toArray <<< map toArray concatMap :: forall a b. (a -> NonEmptyArray b) -> NonEmptyArray a -> NonEmptyArray b concatMap = flip bind @@ -371,12 +339,18 @@ span -> { init :: Array a, rest :: Array a } span f = adaptAny $ A.span f -nub :: forall a. Eq a => NonEmptyArray a -> NonEmptyArray a +nub :: forall a. Ord a => NonEmptyArray a -> NonEmptyArray a nub = unsafeAdapt A.nub -nubBy :: forall a. (a -> a -> Boolean) -> NonEmptyArray a -> NonEmptyArray a +nubEq :: forall a. Eq a => NonEmptyArray a -> NonEmptyArray a +nubEq = unsafeAdapt A.nubEq + +nubBy :: forall a. (a -> a -> Ordering) -> NonEmptyArray a -> NonEmptyArray a nubBy f = unsafeAdapt $ A.nubBy f +nubByEq :: forall a. (a -> a -> Boolean) -> NonEmptyArray a -> NonEmptyArray a +nubByEq f = unsafeAdapt $ A.nubByEq f + union :: forall a. Eq a => NonEmptyArray a -> NonEmptyArray a -> NonEmptyArray a union = unionBy (==) @@ -441,7 +415,7 @@ zipWith -> NonEmptyArray a -> NonEmptyArray b -> NonEmptyArray c -zipWith f xs ys = NonEmptyArray $ A.zipWith f (toArray xs) (toArray ys) +zipWith f xs ys = unsafeFromArray $ A.zipWith f (toArray xs) (toArray ys) zipWithA @@ -451,13 +425,13 @@ zipWithA -> NonEmptyArray a -> NonEmptyArray b -> m (NonEmptyArray c) -zipWithA f xs ys = NonEmptyArray <$> A.zipWithA f (toArray xs) (toArray ys) +zipWithA f xs ys = unsafeFromArrayF $ A.zipWithA f (toArray xs) (toArray ys) zip :: forall a b. NonEmptyArray a -> NonEmptyArray b -> NonEmptyArray (Tuple a b) -zip xs ys = NonEmptyArray $ toArray xs `A.zip` toArray ys +zip xs ys = unsafeFromArray $ toArray xs `A.zip` toArray ys unzip :: forall a b. NonEmptyArray (Tuple a b) -> Tuple (NonEmptyArray a) (NonEmptyArray b) -unzip = bimap NonEmptyArray NonEmptyArray <<< A.unzip <<< toArray +unzip = bimap unsafeFromArray unsafeFromArray <<< A.unzip <<< toArray foldM :: forall m a b. Monad m => (a -> b -> m a) -> a -> NonEmptyArray b -> m a foldM f acc = adaptAny $ A.foldM f acc @@ -467,14 +441,3 @@ foldRecM f acc = adaptAny $ A.foldRecM f acc unsafeIndex :: forall a. Partial => NonEmptyArray a -> Int -> a unsafeIndex = adaptAny A.unsafeIndex - --- we use FFI here to avoid the unnecessary copy created by `tail` -foreign import fold1Impl :: forall a. (a -> a -> a) -> NonEmptyArray a -> a - -foreign import traverse1Impl - :: forall m a b - . (forall a' b'. (m (a' -> b') -> m a' -> m b')) - -> (forall a' b'. (a' -> b') -> m a' -> m b') - -> (a -> m b) - -> NonEmptyArray a - -> m (NonEmptyArray b) diff --git a/src/Data/Array/NonEmpty.js b/src/Data/Array/NonEmpty/Internal.js similarity index 100% rename from src/Data/Array/NonEmpty.js rename to src/Data/Array/NonEmpty/Internal.js diff --git a/src/Data/Array/NonEmpty/Internal.purs b/src/Data/Array/NonEmpty/Internal.purs new file mode 100644 index 00000000..1603bd05 --- /dev/null +++ b/src/Data/Array/NonEmpty/Internal.purs @@ -0,0 +1,67 @@ +module Data.Array.NonEmpty.Internal (NonEmptyArray) where + +import Prelude + +import Control.Alt (class Alt) +import Data.Eq (class Eq1) +import Data.Foldable (class Foldable) +import Data.FoldableWithIndex (class FoldableWithIndex) +import Data.FunctorWithIndex (class FunctorWithIndex) +import Data.Ord (class Ord1) +import Data.Semigroup.Foldable (class Foldable1, foldMap1Default) +import Data.Semigroup.Traversable (class Traversable1, sequence1Default) +import Data.Traversable (class Traversable) +import Data.TraversableWithIndex (class TraversableWithIndex) +import Data.Unfoldable1 (class Unfoldable1) + +newtype NonEmptyArray a = NonEmptyArray (Array a) + +instance showNonEmptyArray :: Show a => Show (NonEmptyArray a) where + show (NonEmptyArray xs) = "(NonEmptyArray " <> show xs <> ")" + +derive newtype instance eqNonEmptyArray :: Eq a => Eq (NonEmptyArray a) +derive newtype instance eq1NonEmptyArray :: Eq1 NonEmptyArray + +derive newtype instance ordNonEmptyArray :: Ord a => Ord (NonEmptyArray a) +derive newtype instance ord1NonEmptyArray :: Ord1 NonEmptyArray + +derive newtype instance semigroupNonEmptyArray :: Semigroup (NonEmptyArray a) + +derive newtype instance functorNonEmptyArray :: Functor NonEmptyArray +derive newtype instance functorWithIndexNonEmptyArray :: FunctorWithIndex Int NonEmptyArray + +derive newtype instance foldableNonEmptyArray :: Foldable NonEmptyArray +derive newtype instance foldableWithIndexNonEmptyArray :: FoldableWithIndex Int NonEmptyArray + +instance foldable1NonEmptyArray :: Foldable1 NonEmptyArray where + foldMap1 = foldMap1Default + fold1 = fold1Impl (<>) + +derive newtype instance unfoldable1NonEmptyArray :: Unfoldable1 NonEmptyArray +derive newtype instance traversableNonEmptyArray :: Traversable NonEmptyArray +derive newtype instance traversableWithIndexNonEmptyArray :: TraversableWithIndex Int NonEmptyArray + +instance traversable1NonEmptyArray :: Traversable1 NonEmptyArray where + traverse1 = traverse1Impl apply map + sequence1 = sequence1Default + +derive newtype instance applyNonEmptyArray :: Apply NonEmptyArray + +derive newtype instance applicativeNonEmptyArray :: Applicative NonEmptyArray + +derive newtype instance bindNonEmptyArray :: Bind NonEmptyArray + +derive newtype instance monadNonEmptyArray :: Monad NonEmptyArray + +derive newtype instance altNonEmptyArray :: Alt NonEmptyArray + +-- we use FFI here to avoid the unncessary copy created by `tail` +foreign import fold1Impl :: forall a. (a -> a -> a) -> NonEmptyArray a -> a + +foreign import traverse1Impl + :: forall m a b + . (forall a' b'. (m (a' -> b') -> m a' -> m b')) + -> (forall a' b'. (a' -> b') -> m a' -> m b') + -> (a -> m b) + -> NonEmptyArray a + -> m (NonEmptyArray b) diff --git a/src/Data/Array/ST.js b/src/Data/Array/ST.js index e142a3be..54ddf4d1 100644 --- a/src/Data/Array/ST.js +++ b/src/Data/Array/ST.js @@ -1,17 +1,13 @@ "use strict"; -exports.runSTArray = function (f) { - return f; -}; - -exports.emptySTArray = function () { +exports.empty = function () { return []; }; -exports.peekSTArrayImpl = function (just) { +exports.peekImpl = function (just) { return function (nothing) { - return function (xs) { - return function (i) { + return function (i) { + return function (xs) { return function () { return i >= 0 && i < xs.length ? just(xs[i]) : nothing; }; @@ -20,9 +16,9 @@ exports.peekSTArrayImpl = function (just) { }; }; -exports.pokeSTArray = function (xs) { - return function (i) { - return function (a) { +exports.poke = function (i) { + return function (a) { + return function (xs) { return function () { var ret = i >= 0 && i < xs.length; if (ret) xs[i] = a; @@ -32,18 +28,18 @@ exports.pokeSTArray = function (xs) { }; }; -exports.pushAllSTArray = function (xs) { - return function (as) { +exports.pushAll = function (as) { + return function (xs) { return function () { return xs.push.apply(xs, as); }; }; }; -exports.spliceSTArray = function (xs) { - return function (i) { - return function (howMany) { - return function (bs) { +exports.splice = function (i) { + return function (howMany) { + return function (bs) { + return function (xs) { return function () { return xs.splice.apply(xs, [i, howMany].concat(bs)); }; diff --git a/src/Data/Array/ST.purs b/src/Data/Array/ST.purs index ab4006d8..687de520 100644 --- a/src/Data/Array/ST.purs +++ b/src/Data/Array/ST.purs @@ -4,16 +4,15 @@ module Data.Array.ST ( STArray(..) - , Assoc() - , runSTArray + , Assoc , withArray - , emptySTArray - , peekSTArray - , pokeSTArray - , pushSTArray - , modifySTArray - , pushAllSTArray - , spliceSTArray + , empty + , peek + , poke + , push + , modify + , pushAll + , splice , sort , sortBy , sortWith @@ -25,9 +24,8 @@ module Data.Array.ST ) where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.ST (ST) +import Control.Monad.ST (ST, kind Region) import Data.Maybe (Maybe(..)) import Unsafe.Coerce (unsafeCoerce) @@ -38,29 +36,18 @@ import Unsafe.Coerce (unsafeCoerce) -- | -- | The runtime representation of a value of type `STArray h a` is the same as that of `Array a`, -- | except that mutation is allowed. -foreign import data STArray :: Type -> Type -> Type +foreign import data STArray :: Region -> Type -> Type -- | An element and its index. type Assoc a = { value :: a, index :: Int } --- | **DEPRECATED**: Use `unsafeFreeze` together with `runST` instead. --- | --- | Freeze a mutable array, creating an immutable array. Use this function as you would use --- | `runST` to freeze a mutable reference. --- | --- | The rank-2 type prevents the reference from escaping the scope of `runSTArray`. -foreign import runSTArray - :: forall a r - . (forall h. Eff (st :: ST h | r) (STArray h a)) - -> Eff r (Array a) - -- | Perform an effect requiring a mutable array on a copy of an immutable array, -- | safely returning the result as an immutable array. withArray - :: forall a b r h - . (STArray h a -> Eff (st :: ST h | r) b) + :: forall h a b + . (STArray h a -> ST h b) -> Array a - -> Eff (st :: ST h | r) (Array a) + -> ST h (Array a) withArray f xs = do result <- thaw xs _ <- f result @@ -68,31 +55,31 @@ withArray f xs = do -- | O(1). Convert a mutable array to an immutable array, without copying. The mutable -- | array must not be mutated afterwards. -unsafeFreeze :: forall a r h. STArray h a -> Eff (st :: ST h | r) (Array a) +unsafeFreeze :: forall h a. STArray h a -> ST h (Array a) unsafeFreeze = pure <<< (unsafeCoerce :: STArray h a -> Array a) -- | O(1) Convert an immutable array to a mutable array, without copying. The input -- | array must not be used afterward. -unsafeThaw :: forall a r h. Array a -> Eff (st :: ST h | r) (STArray h a) +unsafeThaw :: forall h a. Array a -> ST h (STArray h a) unsafeThaw = pure <<< (unsafeCoerce :: Array a -> STArray h a) -- | Create an empty mutable array. -foreign import emptySTArray :: forall a h r. Eff (st :: ST h | r) (STArray h a) +foreign import empty :: forall h a. ST h (STArray h a) -- | Create a mutable copy of an immutable array. -thaw :: forall a h r. Array a -> Eff (st :: ST h | r) (STArray h a) +thaw :: forall h a. Array a -> ST h (STArray h a) thaw = copyImpl -- | Sort a mutable array in place. -sort :: forall a h r. Ord a => STArray h a -> Eff (st :: ST h | r) (STArray h a) +sort :: forall a h. Ord a => STArray h a -> ST h (STArray h a) sort = sortBy compare -- | Sort a mutable array in place using a comparison function. sortBy - :: forall a h r + :: forall a h . (a -> a -> Ordering) -> STArray h a - -> Eff (st :: ST h | r) (STArray h a) + -> ST h (STArray h a) sortBy comp = sortByImpl comp' where comp' x y = case comp x y of @@ -101,80 +88,75 @@ sortBy comp = sortByImpl comp' LT -> -1 foreign import sortByImpl - :: forall a h r + :: forall a h . (a -> a -> Int) -> STArray h a - -> Eff (st :: ST h | r) (STArray h a) + -> ST h (STArray h a) -- | Sort a mutable array in place based on a projection. sortWith - :: forall a b h r + :: forall a b h . Ord b => (a -> b) -> STArray h a - -> Eff (st :: ST h | r) (STArray h a) + -> ST h (STArray h a) sortWith f = sortBy (comparing f) -- | Create an immutable copy of a mutable array. -freeze :: forall a h r. STArray h a -> Eff (st :: ST h | r) (Array a) +freeze :: forall h a. STArray h a -> ST h (Array a) freeze = copyImpl -foreign import copyImpl :: forall a b h r. a -> Eff (st :: ST h | r) b +foreign import copyImpl :: forall h a b. a -> ST h b -- | Read the value at the specified index in a mutable array. -peekSTArray - :: forall a h r - . STArray h a - -> Int - -> Eff (st :: ST h | r) (Maybe a) -peekSTArray = peekSTArrayImpl Just Nothing +peek + :: forall h a + . Int + -> STArray h a + -> ST h (Maybe a) +peek = peekImpl Just Nothing -foreign import peekSTArrayImpl - :: forall a h e r +foreign import peekImpl + :: forall h a r . (a -> r) -> r - -> STArray h a -> Int - -> (Eff (st :: ST h | e) r) + -> STArray h a + -> (ST h r) -- | Change the value at the specified index in a mutable array. -foreign import pokeSTArray - :: forall a h r - . STArray h a -> Int -> a -> Eff (st :: ST h | r) Boolean +foreign import poke :: forall h a. Int -> a -> STArray h a -> ST h Boolean -- | Append an element to the end of a mutable array. Returns the new length of -- | the array. -pushSTArray :: forall a h r. STArray h a -> a -> Eff (st :: ST h | r) Int -pushSTArray arr a = pushAllSTArray arr [a] +push :: forall h a. a -> STArray h a -> ST h Int +push a = pushAll [a] -- | Append the values in an immutable array to the end of a mutable array. -- | Returns the new length of the mutable array. -foreign import pushAllSTArray - :: forall a h r - . STArray h a - -> Array a - -> Eff (st :: ST h | r) Int +foreign import pushAll + :: forall h a + . Array a + -> STArray h a + -> ST h Int -- | Mutate the element at the specified index using the supplied function. -modifySTArray :: forall a h r. STArray h a -> Int -> (a -> a) -> Eff (st :: ST h | r) Boolean -modifySTArray xs i f = do - entry <- peekSTArray xs i +modify :: forall h a. Int -> (a -> a) -> STArray h a -> ST h Boolean +modify i f xs = do + entry <- peek i xs case entry of - Just x -> pokeSTArray xs i (f x) + Just x -> poke i (f x) xs Nothing -> pure false -- | Remove and/or insert elements from/into a mutable array at the specified index. -foreign import spliceSTArray - :: forall a h r - . STArray h a - -> Int +foreign import splice + :: forall h a + . Int -> Int -> Array a - -> Eff (st :: ST h | r) (Array a) + -> STArray h a + -> ST h (Array a) -- | Create an immutable copy of a mutable array, where each element -- | is labelled with its index in the original array. -foreign import toAssocArray - :: forall a h r - . STArray h a - -> Eff (st :: ST h | r) (Array (Assoc a)) +foreign import toAssocArray :: forall h a. STArray h a -> ST h (Array (Assoc a)) diff --git a/src/Data/Array/ST/Iterator.purs b/src/Data/Array/ST/Iterator.purs index c41b2942..09daf0ed 100644 --- a/src/Data/Array/ST/Iterator.purs +++ b/src/Data/Array/ST/Iterator.purs @@ -10,68 +10,71 @@ module Data.Array.ST.Iterator ) where import Prelude -import Control.Monad.Eff (Eff, whileE) -import Control.Monad.ST (ST, STRef, newSTRef, readSTRef, writeSTRef, modifySTRef) -import Data.Array.ST (STArray, pushSTArray) +import Control.Monad.ST (ST) +import Control.Monad.ST as ST +import Control.Monad.ST.Ref (STRef) +import Control.Monad.ST.Ref as STRef +import Data.Array.ST (STArray) +import Data.Array.ST as STA import Data.Maybe (Maybe(..), isNothing) -- | This type provides a slightly easier way of iterating over an array's -- | elements in an STArray computation, without having to keep track of -- | indices. -data Iterator h a = Iterator (Int -> Maybe a) (STRef h Int) +data Iterator r a = Iterator (Int -> Maybe a) (STRef r Int) -- | Make an Iterator given an indexing function into an array (or anything -- | else). If `xs :: Array a`, the standard way to create an iterator over -- | `xs` is to use `iterator (xs !! _)`, where `(!!)` comes from `Data.Array`. -iterator :: forall a h r. (Int -> Maybe a) -> Eff (st :: ST h | r) (Iterator h a) +iterator :: forall r a. (Int -> Maybe a) -> ST r (Iterator r a) iterator f = - Iterator f <$> newSTRef 0 + Iterator f <$> STRef.new 0 -- | Perform an action once for each item left in an iterator. If the action -- | itself also advances the same iterator, `iterate` will miss those items -- | out. -iterate :: forall a h r. Iterator h a -> (a -> Eff (st :: ST h | r) Unit) -> Eff (st :: ST h | r) Unit +iterate :: forall r a. Iterator r a -> (a -> ST r Unit) -> ST r Unit iterate iter f = do - break <- newSTRef false - whileE (not <$> readSTRef break) do + break <- STRef.new false + ST.while (not <$> STRef.read break) do mx <- next iter case mx of Just x -> f x - Nothing -> void $ writeSTRef break true + Nothing -> void $ STRef.write true break -- | Get the next item out of an iterator, advancing it. Returns Nothing if the -- | Iterator is exhausted. -next :: forall a h r. Iterator h a -> Eff (st :: ST h | r) (Maybe a) +next :: forall r a. Iterator r a -> ST r (Maybe a) next (Iterator f currentIndex) = do - i <- readSTRef currentIndex - _ <- modifySTRef currentIndex (_ + 1) + i <- STRef.read currentIndex + _ <- STRef.modify (_ + 1) currentIndex pure (f i) -- | Get the next item out of an iterator without advancing it. -peek :: forall a h r. Iterator h a -> Eff (st :: ST h | r) (Maybe a) +peek :: forall r a. Iterator r a -> ST r (Maybe a) peek (Iterator f currentIndex) = do - i <- readSTRef currentIndex + i <- STRef.read currentIndex pure (f i) -- | Check whether an iterator has been exhausted. -exhausted :: forall a h r. Iterator h a -> Eff (st :: ST h | r) Boolean +exhausted :: forall r a. Iterator r a -> ST r Boolean exhausted = map isNothing <<< peek -- | Extract elements from an iterator and push them on to an STArray for as -- | long as those elements satisfy a given predicate. -pushWhile :: forall a h r. (a -> Boolean) -> Iterator h a -> STArray h a -> Eff (st :: ST h | r) Unit +pushWhile :: forall r a. (a -> Boolean) -> Iterator r a -> STArray r a -> ST r Unit pushWhile p iter array = do - break <- newSTRef false - whileE (not <$> readSTRef break) do + break <- STRef.new false + ST.while (not <$> STRef.read break) do mx <- peek iter case mx of Just x | p x -> do - _ <- pushSTArray array x + _ <- STA.push x array void $ next iter _ -> - void $ writeSTRef break true + void $ STRef.write true break -- | Push the entire remaining contents of an iterator onto an STArray. -pushAll :: forall a h r. Iterator h a -> STArray h a -> Eff (st :: ST h | r) Unit +pushAll :: forall r a. Iterator r a -> STArray r a -> ST r Unit pushAll = pushWhile (const true) diff --git a/src/Data/Array/ST/Partial.js b/src/Data/Array/ST/Partial.js index ccaea3b4..37f4c860 100644 --- a/src/Data/Array/ST/Partial.js +++ b/src/Data/Array/ST/Partial.js @@ -1,16 +1,16 @@ "use strict"; -exports.peekSTArrayImpl = function (xs) { - return function (i) { +exports.peekImpl = function (i) { + return function (xs) { return function () { return xs[i]; }; }; }; -exports.pokeSTArrayImpl = function (xs) { - return function (i) { - return function (a) { +exports.pokeImpl = function (i) { + return function (a) { + return function (xs) { return function () { xs[i] = a; return {}; diff --git a/src/Data/Array/ST/Partial.purs b/src/Data/Array/ST/Partial.purs index 7f265e21..58253396 100644 --- a/src/Data/Array/ST/Partial.purs +++ b/src/Data/Array/ST/Partial.purs @@ -3,43 +3,38 @@ -- | This module is particularly helpful when performance is very important. module Data.Array.ST.Partial - ( peekSTArray - , pokeSTArray + ( peek + , poke ) where -import Control.Monad.Eff (Eff) import Control.Monad.ST (ST) import Data.Array.ST (STArray) import Data.Unit (Unit) -- | Read the value at the specified index in a mutable array. -peekSTArray - :: forall a h r +peek + :: forall h a . Partial - => STArray h a - -> Int - -> Eff (st :: ST h | r) a -peekSTArray = peekSTArrayImpl + => Int + -> STArray h a + -> ST h a +peek = peekImpl -foreign import peekSTArrayImpl - :: forall a h r - . STArray h a - -> Int - -> Eff (st :: ST h | r) a +foreign import peekImpl :: forall h a. Int -> STArray h a -> ST h a -- | Change the value at the specified index in a mutable array. -pokeSTArray - :: forall a h r +poke + :: forall h a . Partial - => STArray h a - -> Int + => Int -> a - -> Eff (st :: ST h | r) Unit -pokeSTArray = pokeSTArrayImpl + -> STArray h a + -> ST h Unit +poke = pokeImpl -foreign import pokeSTArrayImpl - :: forall a h r - . STArray h a - -> Int +foreign import pokeImpl + :: forall h a + . Int -> a - -> Eff (st :: ST h | r) Unit \ No newline at end of file + -> STArray h a + -> ST h Unit diff --git a/test/Test/Data/Array.purs b/test/Test/Data/Array.purs index f97314b8..53556ac6 100644 --- a/test/Test/Data/Array.purs +++ b/test/Test/Data/Array.purs @@ -2,24 +2,20 @@ module Test.Data.Array (testArray) where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (log, CONSOLE) - -import Data.Const (Const(..)) -import Data.Array as A import Data.Array ((:), (\\), (!!)) +import Data.Array as A +import Data.Array.NonEmpty as NEA +import Data.Const (Const(..)) import Data.Foldable (for_, foldMapDefaultR, class Foldable, all, traverse_) import Data.Maybe (Maybe(..), isNothing, fromJust) -import Data.NonEmpty ((:|)) -import Data.NonEmpty as NE import Data.Tuple (Tuple(..)) import Data.Unfoldable (replicateA) - +import Effect (Effect) +import Effect.Console (log) import Partial.Unsafe (unsafePartial) +import Test.Assert (assert) -import Test.Assert (assert, ASSERT) - -testArray :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testArray :: Effect Unit testArray = do log "singleton should construct an array with a single value" @@ -239,7 +235,7 @@ testArray = do assert $ A.sortBy (flip compare) [1, 3, 2, 5, 6, 4] == [6, 5, 4, 3, 2, 1] log "sortWith should reorder a list into ascending order based on the result of compare over a projection" - assert $ A.sortWith id [1, 3, 2, 5, 6, 4] == [1, 2, 3, 4, 5, 6] + assert $ A.sortWith identity [1, 3, 2, 5, 6, 4] == [1, 2, 3, 4, 5, 6] log "take should keep the specified number of items from the front of an array, discarding the rest" assert $ (A.take 1 [1, 2, 3]) == [1] @@ -302,20 +298,32 @@ testArray = do testBigSpan 100000 log "group should group consecutive equal elements into arrays" - assert $ A.group [1, 2, 2, 3, 3, 3, 1] == [NE.singleton 1, 2 :| [2], 3:| [3, 3], NE.singleton 1] + assert $ A.group [1, 2, 2, 3, 3, 3, 1] == [NEA.singleton 1, nea [2, 2], nea [3, 3, 3], NEA.singleton 1] log "group' should sort then group consecutive equal elements into arrays" - assert $ A.group' [1, 2, 2, 3, 3, 3, 1] == [1 :| [1], 2 :| [2], 3 :| [3, 3]] + assert $ A.group' [1, 2, 2, 3, 3, 3, 1] == [nea [1, 1], nea [2, 2], nea [3, 3, 3]] log "groupBy should group consecutive equal elements into arrays based on an equivalence relation" - assert $ A.groupBy (\x y -> odd x && odd y) [1, 1, 2, 2, 3, 3] == [1 :| [1], NE.singleton 2, NE.singleton 2, 3 :| [3]] + assert $ A.groupBy (\x y -> odd x && odd y) [1, 1, 2, 2, 3, 3] == [nea [1, 1], NEA.singleton 2, NEA.singleton 2, nea [3, 3]] log "nub should remove duplicate elements from the list, keeping the first occurence" assert $ A.nub [1, 2, 2, 3, 4, 1] == [1, 2, 3, 4] + log "nub should preserve order" + assert $ A.nub [1, 3, 4, 2, 2, 1] == [1, 3, 4, 2] + + log "nubEq should remove duplicate elements from the list, keeping the first occurence" + assert $ A.nubEq [1, 2, 2, 3, 4, 1] == [1, 2, 3, 4] + + log "nubEq should preserve order" + assert $ A.nubEq [1, 3, 4, 2, 2, 1] == [1, 3, 4, 2] + log "nubBy should remove duplicate items from the list using a supplied predicate" + assert $ A.nubBy compare [1, 3, 4, 2, 2, 1] == [1, 3, 4, 2] + + log "nubByEq should remove duplicate items from the list using a supplied predicate" let nubPred = \x y -> if odd x then false else x == y - assert $ A.nubBy nubPred [1, 2, 2, 3, 3, 4, 4, 1] == [1, 2, 3, 3, 4, 1] + assert $ A.nubByEq nubPred [1, 2, 2, 3, 3, 4, 4, 1] == [1, 2, 3, 3, 4, 1] log "union should produce the union of two arrays" assert $ A.union [1, 2, 3] [2, 3, 4] == [1, 2, 3, 4] @@ -378,6 +386,8 @@ testArray = do , [4,0,0,1,25,36,458,5842,23757] ] +nea :: Array ~> NEA.NonEmptyArray +nea = unsafePartial fromJust <<< NEA.fromArray nil :: Array Int nil = [] diff --git a/test/Test/Data/Array/NonEmpty.purs b/test/Test/Data/Array/NonEmpty.purs index 1b92db8b..fbc576fa 100644 --- a/test/Test/Data/Array/NonEmpty.purs +++ b/test/Test/Data/Array/NonEmpty.purs @@ -2,21 +2,24 @@ module Test.Data.Array.NonEmpty (testNonEmptyArray) where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) +import Data.Array as A import Data.Array.NonEmpty as NEA import Data.Const (Const(..)) import Data.Foldable (for_, sum, traverse_) import Data.FunctorWithIndex (mapWithIndex) import Data.Maybe (Maybe(..), fromJust) import Data.Monoid.Additive (Additive(..)) +import Data.NonEmpty ((:|)) import Data.Semigroup.Foldable (foldMap1) import Data.Semigroup.Traversable (traverse1) import Data.Tuple (Tuple(..)) +import Data.Unfoldable1 as U1 +import Effect (Effect) +import Effect.Console (log) import Partial.Unsafe (unsafePartial) -import Test.Assert (ASSERT, assert) +import Test.Assert (assert) -testNonEmptyArray :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testNonEmptyArray :: Effect Unit testNonEmptyArray = do let fromArray :: forall a. Array a -> NEA.NonEmptyArray a fromArray = unsafePartial fromJust <<< NEA.fromArray @@ -196,7 +199,7 @@ testNonEmptyArray = do assert $ NEA.sortBy (flip compare) (fromArray [1, 3, 2, 5, 6, 4]) == fromArray [6, 5, 4, 3, 2, 1] log "sortWith should reorder a list into ascending order based on the result of compare over a projection" - assert $ NEA.sortWith id (fromArray [1, 3, 2, 5, 6, 4]) == fromArray [1, 2, 3, 4, 5, 6] + assert $ NEA.sortWith identity (fromArray [1, 3, 2, 5, 6, 4]) == fromArray [1, 2, 3, 4, 5, 6] log "take should keep the specified number of items from the front of an array, discarding the rest" assert $ NEA.take 1 (fromArray [1, 2, 3]) == [1] @@ -234,9 +237,12 @@ testNonEmptyArray = do log "nub should remove duplicate elements from the list, keeping the first occurence" assert $ NEA.nub (fromArray [1, 2, 2, 3, 4, 1]) == fromArray [1, 2, 3, 4] - log "nubBy should remove duplicate items from the list using a supplied predicate" + log "nubEq should remove duplicate elements from the list, keeping the first occurence" + assert $ NEA.nubEq (fromArray [1, 2, 2, 3, 4, 1]) == fromArray [1, 2, 3, 4] + + log "nubByEq should remove duplicate items from the list using a supplied predicate" let nubPred = \x y -> if odd x then false else x == y - assert $ NEA.nubBy nubPred (fromArray [1, 2, 2, 3, 3, 4, 4, 1]) == fromArray [1, 2, 3, 3, 4, 1] + assert $ NEA.nubByEq nubPred (fromArray [1, 2, 2, 3, 3, 4, 4, 1]) == fromArray [1, 2, 3, 3, 4, 1] log "union should produce the union of two arrays" assert $ NEA.union (fromArray [1, 2, 3]) (fromArray [2, 3, 4]) == fromArray [1, 2, 3, 4] @@ -285,13 +291,19 @@ testNonEmptyArray = do , fromArray [4,0,0,1,25,36,458,5842,23757] ]) + log "toUnfoldable1" + assert $ NEA.toUnfoldable1 (NEA.range 0 9) == 0 :| A.range 1 9 + + log "Unfoldable instance" + assert $ U1.range 0 9 == NEA.range 0 9 + log "foldl should work" -- test through sum assert $ sum (fromArray [1, 2, 3, 4]) == 10 log "foldMap1 should work" assert $ foldMap1 Additive (fromArray [1, 2, 3, 4]) == Additive 10 - + log "traverse1 should work" assert $ traverse1 Just (fromArray [1, 2, 3, 4]) == NEA.fromArray [1, 2, 3, 4] diff --git a/test/Test/Data/Array/Partial.purs b/test/Test/Data/Array/Partial.purs index f27f52a5..27eaf757 100644 --- a/test/Test/Data/Array/Partial.purs +++ b/test/Test/Data/Array/Partial.purs @@ -2,16 +2,13 @@ module Test.Data.Array.Partial (testArrayPartial) where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (log, CONSOLE) - import Data.Array.Partial (init, last, tail, head) - +import Effect (Effect) +import Effect.Console (log) import Partial.Unsafe (unsafePartial) +import Test.Assert (assert) -import Test.Assert (assert, ASSERT) - -testArrayPartial :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testArrayPartial :: Effect Unit testArrayPartial = do log "head should return the first item in an array" diff --git a/test/Test/Data/Array/ST.purs b/test/Test/Data/Array/ST.purs index 518675da..51f62514 100644 --- a/test/Test/Data/Array/ST.purs +++ b/test/Test/Data/Array/ST.purs @@ -1,188 +1,191 @@ module Test.Data.Array.ST (testArrayST) where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (log, CONSOLE) -import Control.Monad.ST (ST, pureST) -import Data.Array.ST (STArray, emptySTArray, freeze, peekSTArray, pokeSTArray, pushAllSTArray, pushSTArray, sort, sortBy, sortWith, spliceSTArray, thaw, toAssocArray, unsafeThaw, unsafeFreeze) + +import Control.Monad.ST (ST) +import Control.Monad.ST as ST +import Data.Array.ST (STArray) +import Data.Array.ST as STA import Data.Foldable (all) import Data.Maybe (Maybe(..), isNothing) -import Test.Assert (assert, ASSERT) +import Effect (Effect) +import Effect.Console (log) +import Test.Assert (assert) -run :: forall a. (forall h. Eff (st :: ST h) (STArray h a)) -> Array a -run act = pureST (act >>= unsafeFreeze) +run :: forall a. (forall r. ST r (STArray r a)) -> Array a +run act = ST.run (act >>= STA.unsafeFreeze) -testArrayST :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testArrayST :: Effect Unit testArrayST = do - log "emptySTArray should produce an empty array" + log "empty should produce an empty array" - assert $ run emptySTArray == nil + assert $ run STA.empty == nil log "thaw should produce an STArray from a standard array" - assert $ run (thaw [1, 2, 3]) == [1, 2, 3] + assert $ run (STA.thaw [1, 2, 3]) == [1, 2, 3] log "freeze should produce a standard array from an STArray" - assert $ pureST (do - arr <- thaw [1, 2, 3] - freeze arr) == [1, 2, 3] + assert $ ST.run (do + arr <- STA.thaw [1, 2, 3] + STA.freeze arr) == [1, 2, 3] log "unsafeThaw should produce an STArray from a standard array" - assert $ run (unsafeThaw [1, 2, 3]) == [1, 2, 3] + assert $ run (STA.unsafeThaw [1, 2, 3]) == [1, 2, 3] - log "pushSTArray should append a value to the end of the array" + log "push should append a value to the end of the array" assert $ run (do - arr <- emptySTArray - void $ pushSTArray arr 1 - void $ pushSTArray arr 2 + arr <- STA.empty + void $ STA.push 1 arr + void $ STA.push 2 arr pure arr) == [1, 2] assert $ run (do - arr <- thaw [1, 2, 3] - void $ pushSTArray arr 4 + arr <- STA.thaw [1, 2, 3] + void $ STA.push 4 arr pure arr) == [1, 2, 3, 4] - log "pushSTArray should return the new length of the array" + log "push should return the new length of the array" - assert $ pureST (do - arr <- thaw [unit, unit, unit] - pushSTArray arr unit) == 4 + assert $ ST.run (do + arr <- STA.thaw [unit, unit, unit] + STA.push unit arr) == 4 - log "pushAllSTArray should append multiple values to the end of the array" + log "pushAll should append multiple values to the end of the array" assert $ run (do - arr <- emptySTArray - void $ pushAllSTArray arr [1, 2] + arr <- STA.empty + void $ STA.pushAll [1, 2] arr pure arr) == [1, 2] assert $ run (do - arr <- thaw [1, 2, 3] - void $ pushAllSTArray arr [4, 5, 6] + arr <- STA.thaw [1, 2, 3] + void $ STA.pushAll [4, 5, 6] arr pure arr) == [1, 2, 3, 4, 5, 6] - log "pushAllSTArray should return the new length of the array" + log "pushAll should return the new length of the array" - assert $ pureST (do - arr <- thaw [unit, unit, unit] - pushAllSTArray arr [unit, unit]) == 5 + assert $ ST.run (do + arr <- STA.thaw [unit, unit, unit] + STA.pushAll [unit, unit] arr) == 5 - log "peekSTArray should return Nothing when peeking a value outside the array bounds" + log "peek should return Nothing when peeking a value outside the array bounds" - assert $ isNothing $ pureST (do - arr <- emptySTArray - peekSTArray arr 0) + assert $ isNothing $ ST.run (do + arr <- STA.empty + STA.peek 0 arr) - assert $ isNothing $ pureST (do - arr <- thaw [1] - peekSTArray arr 1) + assert $ isNothing $ ST.run (do + arr <- STA.thaw [1] + STA.peek 1 arr) - assert $ isNothing $ pureST (do - arr <- emptySTArray - peekSTArray arr (-1)) + assert $ isNothing $ ST.run (do + arr <- STA.empty + STA.peek (-1) arr) - log "peekSTArray should return the value at the specified index" + log "peek should return the value at the specified index" - assert $ pureST (do - arr <- thaw [1] - peekSTArray arr 0) == Just 1 + assert $ ST.run (do + arr <- STA.thaw [1] + STA.peek 0 arr) == Just 1 - assert $ pureST (do - arr <- thaw [1, 2, 3] - peekSTArray arr 2) == Just 3 + assert $ ST.run (do + arr <- STA.thaw [1, 2, 3] + STA.peek 2 arr) == Just 3 - log "pokeSTArray should return true when a value has been updated succesfully" + log "poke should return true when a value has been updated succesfully" - assert $ pureST (do - arr <- thaw [1] - pokeSTArray arr 0 10) + assert $ ST.run (do + arr <- STA.thaw [1] + STA.poke 0 10 arr) - assert $ pureST (do - arr <- thaw [1, 2, 3] - pokeSTArray arr 2 30) + assert $ ST.run (do + arr <- STA.thaw [1, 2, 3] + STA.poke 2 30 arr) - log "pokeSTArray should return false when attempting to modify a value outside the array bounds" + log "poke should return false when attempting to modify a value outside the array bounds" - assert $ not $ pureST (do - arr <- emptySTArray - pokeSTArray arr 0 10) + assert $ not $ ST.run (do + arr <- STA.empty + STA.poke 0 10 arr) - assert $ not $ pureST (do - arr <- thaw [1, 2, 3] - pokeSTArray arr 3 100) + assert $ not $ ST.run (do + arr <- STA.thaw [1, 2, 3] + STA.poke 3 100 arr) - assert $ not $ pureST (do - arr <- thaw [1, 2, 3] - pokeSTArray arr (-1) 100) + assert $ not $ ST.run (do + arr <- STA.thaw [1, 2, 3] + STA.poke (-1) 100 arr) - log "pokeSTArray should replace the value at the specified index" + log "poke should replace the value at the specified index" assert $ run (do - arr <- thaw [1] - void $ pokeSTArray arr 0 10 + arr <- STA.thaw [1] + void $ STA.poke 0 10 arr pure arr) == [10] - log "pokeSTArray should do nothing when attempting to modify a value outside the array bounds" + log "poke should do nothing when attempting to modify a value outside the array bounds" assert $ run (do - arr <- thaw [1] - void $ pokeSTArray arr 1 2 + arr <- STA.thaw [1] + void $ STA.poke 1 2 arr pure arr) == [1] log "sort should reorder a list into ascending order based on the result of compare" assert $ run ( - sort =<< unsafeThaw [1, 3, 2, 5, 6, 4] + STA.sort =<< STA.unsafeThaw [1, 3, 2, 5, 6, 4] ) == [1, 2, 3, 4, 5, 6] log "sortBy should reorder a list into ascending order based on the result of a comparison function" assert $ run ( - sortBy (flip compare) =<< unsafeThaw [1, 3, 2, 5, 6, 4] + STA.sortBy (flip compare) =<< STA.unsafeThaw [1, 3, 2, 5, 6, 4] ) == [6, 5, 4, 3, 2, 1] log "sortWith should reorder a list into ascending order based on the result of compare over a projection" assert $ run ( - sortWith id =<< unsafeThaw [1, 3, 2, 5, 6, 4] + STA.sortWith identity =<< STA.unsafeThaw [1, 3, 2, 5, 6, 4] ) == [1, 2, 3, 4, 5, 6] - log "spliceSTArray should be able to delete multiple items at a specified index" + log "splice should be able to delete multiple items at a specified index" assert $ run (do - arr <- thaw [1, 2, 3, 4, 5] - void $ spliceSTArray arr 1 3 [] + arr <- STA.thaw [1, 2, 3, 4, 5] + void $ STA.splice 1 3 [] arr pure arr) == [1, 5] - log "spliceSTArray should return the items removed" + log "splice should return the items removed" - assert $ pureST (do - arr <- thaw [1, 2, 3, 4, 5] - spliceSTArray arr 1 3 []) == [2, 3, 4] + assert $ ST.run (do + arr <- STA.thaw [1, 2, 3, 4, 5] + STA.splice 1 3 [] arr) == [2, 3, 4] - log "spliceSTArray should be able to insert multiple items at a specified index" + log "splice should be able to insert multiple items at a specified index" assert $ run (do - arr <- thaw [1, 2, 3, 4, 5] - void $ spliceSTArray arr 1 0 [0, 100] + arr <- STA.thaw [1, 2, 3, 4, 5] + void $ STA.splice 1 0 [0, 100] arr pure arr) == [1, 0, 100, 2, 3, 4, 5] - log "spliceSTArray should be able to delete and insert at the same time" + log "splice should be able to delete and insert at the same time" assert $ run (do - arr <- thaw [1, 2, 3, 4, 5] - void $ spliceSTArray arr 1 2 [0, 100] + arr <- STA.thaw [1, 2, 3, 4, 5] + void $ STA.splice 1 2 [0, 100] arr pure arr) == [1, 0, 100, 4, 5] log "toAssocArray should return all items in the array with the correct indices and values" - assert $ all (\{ value: v, index: i } -> v == i + 1) $ pureST (do - arr <- thaw [1, 2, 3, 4, 5] - toAssocArray arr) + assert $ all (\{ value: v, index: i } -> v == i + 1) $ ST.run (do + arr <- STA.thaw [1, 2, 3, 4, 5] + STA.toAssocArray arr) - assert $ all (\{ value: v, index: i } -> v == (i + 1) * 10) $ pureST (do - arr <- thaw [10, 20, 30, 40, 50] - toAssocArray arr) + assert $ all (\{ value: v, index: i } -> v == (i + 1) * 10) $ ST.run (do + arr <- STA.thaw [10, 20, 30, 40, 50] + STA.toAssocArray arr) nil :: Array Int nil = [] diff --git a/test/Test/Data/Array/ST/Partial.purs b/test/Test/Data/Array/ST/Partial.purs index 3d7ed748..89c6f9eb 100644 --- a/test/Test/Data/Array/ST/Partial.purs +++ b/test/Test/Data/Array/ST/Partial.purs @@ -2,27 +2,24 @@ module Test.Data.Array.ST.Partial (testArraySTPartial) where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (log, CONSOLE) -import Control.Monad.ST (pureST) - +import Control.Monad.ST as ST import Data.Array.ST (thaw, unsafeFreeze) -import Data.Array.ST.Partial (peekSTArray, pokeSTArray) - +import Data.Array.ST.Partial as STAP +import Effect (Effect) +import Effect.Console (log) import Partial.Unsafe (unsafePartial) +import Test.Assert (assert) -import Test.Assert (assert, ASSERT) - -testArraySTPartial :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testArraySTPartial :: Effect Unit testArraySTPartial = do log "peekSTArray should return the value at the specified index" - assert $ 2 == pureST do + assert $ 2 == ST.run do a <- thaw [1, 2, 3] - unsafePartial $ peekSTArray a 1 + unsafePartial $ STAP.peek 1 a log "pokeSTArray should modify the value at the specified index" - assert $ [1, 4, 3] == pureST do + assert $ [1, 4, 3] == ST.run do a <- thaw [1, 2, 3] - unsafePartial $ pokeSTArray a 1 4 - unsafeFreeze a \ No newline at end of file + unsafePartial $ STAP.poke 1 4 a + unsafeFreeze a diff --git a/test/Test/Main.purs b/test/Test/Main.purs index d2850e71..86c40291 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -2,17 +2,14 @@ module Test.Main where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE) - -import Test.Assert (ASSERT) +import Effect (Effect) import Test.Data.Array (testArray) import Test.Data.Array.Partial (testArrayPartial) import Test.Data.Array.ST (testArrayST) import Test.Data.Array.ST.Partial (testArraySTPartial) import Test.Data.Array.NonEmpty (testNonEmptyArray) -main :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +main :: Effect Unit main = do testArray testArrayST