Skip to content

Feature: sconcat and stimes. #580

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions benchmarks/haskell/Benchmarks/Pure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import qualified Data.Text.Encoding as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TB
import qualified Data.Text.Lazy.Encoding as TL
import Data.Semigroup
import Data.List.NonEmpty (NonEmpty((:|)))

data Env = Env
{ bsa :: !BS.ByteString
Expand Down Expand Up @@ -83,6 +85,14 @@ benchmark kind ~Env{..} =
[ benchT $ nf T.concat tl
, benchTL $ nf TL.concat tll
]
, bgroup "sconcat"
[ benchT $ nf sconcat (T.empty :| tl)
, benchTL $ nf sconcat (TL.empty :| tll)
]
, bgroup "stimes"
[ benchT $ nf (stimes (10 :: Int)) ta
, benchTL $ nf (stimes (10 :: Int)) tla
]
, bgroup "cons"
[ benchT $ nf (T.cons c) ta
, benchTL $ nf (TL.cons c) tla
Expand Down
13 changes: 13 additions & 0 deletions src/Data/Text.hs
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,22 @@ instance Read Text where
readsPrec p str = [(pack x,y) | (x,y) <- readsPrec p str]

-- | @since 1.2.2.0
--
-- Beware: @stimes@ will crash if the given number does not fit into
-- an @Int@.
instance Semigroup Text where
(<>) = append

stimes howManyTimes
| howManyTimes < 0 = P.error "Data.Text.stimes: given number is negative!"
| otherwise =
let howManyTimesInt = P.fromIntegral howManyTimes :: Int
in if P.fromIntegral howManyTimesInt == howManyTimes
then replicate howManyTimesInt
else P.error "Data.Text.stimes: given number does not fit into an Int!"

sconcat = concat . NonEmptyList.toList

instance Monoid Text where
mempty = empty
mappend = (<>)
Expand Down
2 changes: 2 additions & 0 deletions tests/Tests/Properties.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Tests.Properties.Read (testRead)
import Tests.Properties.Text (testText)
import Tests.Properties.Transcoding (testTranscoding)
import Tests.Properties.Validate (testValidate)
import Tests.Properties.CornerCases (testCornerCases)

tests :: TestTree
tests =
Expand All @@ -30,5 +31,6 @@ tests =
testBuilder,
testLowLevel,
testRead,
testCornerCases,
testValidate
]
39 changes: 39 additions & 0 deletions tests/Tests/Properties/CornerCases.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

-- | Check that the definitions that are partial crash in the expected ways or
-- return sensible defaults.
module Tests.Properties.CornerCases (testCornerCases) where

import Control.Exception
import Data.Either
import Data.Semigroup
import Data.Text
import Test.QuickCheck
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.QuickCheck (testProperty)
import Tests.QuickCheckUtils ()

testCornerCases :: TestTree
testCornerCases =
testGroup
"corner cases"
[ testGroup
"stimes"
$ let specimen = stimes :: Integer -> Text -> Text
in [ testProperty
"given a negative number, evaluate to error call"
$ \(Negative number) text ->
(ioProperty . fmap isLeft . try @ErrorCall . evaluate) $
specimen
(fromIntegral (number :: Int))
text
, testProperty
"given a number that does not fit into Int, evaluate to error call"
$ \(NonNegative number) text ->
(ioProperty . fmap isLeft . try @ErrorCall . evaluate) $
specimen
(fromIntegral (number :: Int) + fromIntegral (maxBound :: Int) + 1)
text
]
]
5 changes: 5 additions & 0 deletions tests/Tests/Properties/Instances.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Tests.Properties.Instances
) where

import Data.Binary (encode, decodeOrFail)
import Data.Semigroup
import Data.String (IsString(fromString))
import Test.QuickCheck
import Test.Tasty (TestTree, testGroup)
Expand Down Expand Up @@ -37,6 +38,9 @@ t_Show = show `eq` (show . T.pack)
tl_Show = show `eq` (show . TL.pack)
t_mappend s = mappend s`eqP` (unpackS . mappend (T.pack s))
tl_mappend s = mappend s`eqP` (unpackS . mappend (TL.pack s))
t_stimes = \ number -> eq
((stimes :: Int -> String -> String) number . unSqrt)
(unpackS . (stimes :: Int -> T.Text -> T.Text) number . T.pack . unSqrt)
t_mconcat = (mconcat . unSqrt) `eq` (unpackS . mconcat . L.map T.pack . unSqrt)
tl_mconcat = (mconcat . unSqrt) `eq` (unpackS . mconcat . L.map TL.pack . unSqrt)
t_mempty = mempty === (unpackS (mempty :: T.Text))
Expand Down Expand Up @@ -71,6 +75,7 @@ testInstances =
testProperty "tl_Show" tl_Show,
testProperty "t_mappend" t_mappend,
testProperty "tl_mappend" tl_mappend,
testProperty "t_stimes" t_stimes,
testProperty "t_mconcat" t_mconcat,
testProperty "tl_mconcat" tl_mconcat,
testProperty "t_mempty" t_mempty,
Expand Down
1 change: 1 addition & 0 deletions text.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ test-suite tests
Tests.Properties.Substrings
Tests.Properties.Text
Tests.Properties.Transcoding
Tests.Properties.CornerCases
Tests.Properties.Validate
Tests.QuickCheckUtils
Tests.RebindableSyntaxTest
Expand Down
Loading