ndmitchell / extra Goto Github PK
View Code? Open in Web Editor NEWExtra Haskell functions
License: Other
Extra Haskell functions
License: Other
Currently it is problematic to add, for example, nubOrd
to Data.List.NonEmpty.Extra
, because nubOrd
also exists in Data.List.Extra
, and they clash in TestGen.hs. One solution is to split TestGen.hs to multiple modules, one per .Extra
module.
Consider adding applyWhen from here.
I would like to suggest adding a couple of small functions to System.Directory.Extra:
filesWithExtension :: FilePath -- directory
-> String -- file extension
-> IO [FilePath]
fileWithExtension :: FilePath -- directory
-> String -- file extension
-> IO (Maybe FilePath)
How does that sound?
If it is okay I am happy to open a PR for it.
Most of the functions have trivial implementations. If a beginner couldn't write the function, it probably doesn't belong here.
Following #22 it offers very few benefits for an extra dependency.
O(n^2) and not a good idea.
Citing from http://hydra.cryp.to/build/1716096/nixlog/1/raw:
timeout 0.1 (sleep 2 >> print 1) == return Nothing
*** Failed! Falsifiable (after 1 test):
Test suite extra-test: FAIL
Copy-paste errors, uh, find a way.
Line 261 in dffea9e
Found this function on Hoogle thanks to a 1HaskellADay challenge. I thought the implementation was clever, but there is actually a much simpler version: https://twitter.com/xgrommx/status/1374497119143223299
Because everything is a monoid, if you're brave enough.
(I don't expect you to take action on this issue, I just found it hilarious enough to share.)
I can't seem to find the above module when adding extra-1.7.7
as a dependency to a project on GHC 8.10.2. Seeing as I wrote that library, I must not have regenerated the files properly when committing. I'm not sure how to do this properly since it was giving me errors when running the Generate.hs
file both through cabal
and ghci
, saying it couldn't find the clock
module...
It would be nice for this function:
(!?) :: [a] -> Int -> Maybe a
to exist in a convenient place. Would you consider adding it to extra
?
zipFrom :: Enum a => a -> [b] -> [(a, b)]
Also add zipWithFrom
-- | 'zip' against an enumeration.
-- Never truncates the output - raises an error if the enumeration runs out.
--
-- > \i xs -> zip [i..] xs == zipFrom i xs
-- > zipFrom False [1..3] == [(False,1),(True, 2)]
Never truncates the output
source:
Line 240 in f1f2085
The fix depends on the actual intention, but since this is a released function, I suggest to change this to something like
Truncates the output if the enumeration runs out
Happy to provide a PR if that is the intent of the function.
Hello, the notNull
defined in Data.List.Extra
, as a composition of not
and null
, is defined only for list, however, de null
function works with any Foldable
, so I think that it would make sense (and it would be very useful) for the notNull
function to also work for any Foldable
.
Some other functions of Data.List.Extra
also would make sense if there where for any Foldable
, such as allSame
and anySame
.
Implementation: sort, then remove adjacent duplicates
[1 of 4] Compiling TestUtil ( test/TestUtil.hs, /code/other-haskell/extra/dist-newstyle/build/x86_64-linux/ghcjs-8.4.0.1/extra-1.6.17/t/extra-test/build/extra-test/extra-test-tmp/TestUtil.js_o )
test/TestUtil.hs:10:6: error:
Conflicting exports for ‘isWindows’:
‘module X’ exports ‘X.isWindows’
imported from ‘System.FilePath’ at test/TestUtil.hs:33:1-27
(and originally defined in ‘System.FilePath.Posix’)
‘module X’ exports ‘X.isWindows’
imported from ‘System.Info.Extra’ at test/TestUtil.hs:34:1-29
|
10 | ,module X
This is no biggie, and I can workaround it easy by having an override
extra = dontCheckGHCJS super.extra;
but if it can be fixed, all the better!
Sometimes you want the function which takes two arguments and ignores the first. You can write that as:
flip const
\_ x -> x
const id
\_ -> id
Assuming that thing is called f
, and we pass around a WHNF f x
, the first two of those will leave the x
captured. But in my view, the first two are also clearer to read. I however, don't guarantee that's true, my reasoning may be wrong....
It seems like having a flipConst
or other function might be useful, with the best space behaviour it can do? Or maybe the restriction to only after people seq a function is just not helpful? Is there a good name for this function? Do lambdas really reduce differently for \x y -> ...
vs \x -> \y -> ...
? Or does the id
make it different?
CC @josephcsible who brought this up first in HLint.
We should deprecate replace
and splitOn
, and create alternatives for them that take a NonEmpty
instead of crashing at runtime when their first parameter is empty.
flipped foldMap: (Foldable t, Monoid c) => t a -> (a -> c) -> c
It is similar to whenJust
, but more pure, while whenJust
is inherently effectful since it returns unit.
It can be used e.g. in the HTML AST of an Elm-style render function, when you want to optionally render something. In that case you'd wanna render nothing at all (mempty) when you get a Nothing.
In that case, the instantiation would be Maybe a -> (a -> HTML) -> HTML
. If you had mbMyNumber
, you could do flippedFoldMap mbMyNumber renderNumber
instead of case mbMyNumber of Nothing -> mempty; Just x -> renderNumber x
.
I am posting the issue here because I think the function is very general, and I was expecting whenJust
to do just this. So maybe other people will have the same suspicion, and it would make sense to have this function next to the existing whenJust
. The problem is, I don't know what to call this flipped foldMap.
On IRC, dsal made me aware that whenJust = flip traverse_
. Maybe the fact that whenJust
could be more general than it is, means that the general version should also be available? It seems weird to argue that flip traverse_
should be specialized but flip foldMap
shouldn't. Either way, to have this added, we'd need a name, and the best name I can think of is whenJust
, but it is taken. Would love to hear whether you think this function belongs here or not, and what it should be called.
ghci> :t flip traverse_ :: Monad m => Maybe a -> (a -> m ()) -> m ()
flip traverse_ :: Monad m => Maybe a -> (a -> m ()) -> m ()
:: Monad m => Maybe a -> (a -> m ()) -> m ()
Thanks for your library, it has been very useful.
Seems like a good idea.
See the diff at haskell/haskell-language-server#1553. Inspired, but not directly following that code, my inclination would be:
modifyVar' :: Var a -> (a -> IO (a, b)) -> IO b
modifyVar_' :: Var a -> (a -> IO a) -> IO ()
writeVar' :: Var a -> a -> IO ()
All of which update the Var
, and then, without the lock held, evaluate the a
they just put inside the Var
. CC @pepeiborra - does that sound plausible? I think that Ghcide having a custom Control.Concurrent.Strict wrapper that only exposes the strict variants is still likely to be useful for that project, but it could be done on top of those three.
Following on from #16, should fileEq
on two files that don't exist be True
? I'm inclined to think it should, since then you have the property that fileEq a a == True
, which seems desirable, and also provides an implementation shortcut if met.
We can change it right now, since I only just released the new version, but I think it has to be today. CC @jacereda
Add a nub function that uses an Ord context and a balanced tree to run in O(n log n). Should not use containers, since that's a big dependency for a single function.
[ 9 of 15] Compiling System.Directory.Extra ( src/System/Directory/Extra.hs, dist/dist-sandbox-febc0bd9/build/System/Directory/Extra.o )
src/System/Directory/Extra.hs:7:5:
Ambiguous occurrence ‘withCurrentDirectory’
It could refer to either ‘System.Directory.Extra.withCurrentDirectory’,
defined at src/System/Directory/Extra.hs:28:1
or ‘System.Directory.withCurrentDirectory’,
imported from ‘System.Directory’ at src/System/Directory/Extra.hs:11:1-23
src/System/Directory/Extra.hs:7:5:
Conflicting exports for ‘withCurrentDirectory’:
‘module System.Directory’ exports ‘System.Directory.withCurrentDirectory’
imported from ‘System.Directory’ at src/System/Directory/Extra.hs:11:1-23
‘withCurrentDirectory’ exports ‘System.Directory.Extra.withCurrentDirectory’
defined at src/System/Directory/Extra.hs:28:1
Due to System.Directory
new version:
-- | Run an 'IO' action with the given working directory and restore the
-- original working directory afterwards, even if the given action fails due
-- to an exception.
--
-- The operation may fail with the same exceptions as 'getCurrentDirectory'
-- and 'setCurrentDirectory'.
--
-- @since 1.2.3.0
--
withCurrentDirectory :: FilePath -- ^ Directory to execute in
-> IO a -- ^ Action to be executed
-> IO a
withCurrentDirectory dir action =
bracket getCurrentDirectory setCurrentDirectory $ \ _ -> do
setCurrentDirectory dir
action
When I run 'cabal check', the test suite starts up, but it doesn't consume any CPU nor does it ever terminate. This happens only when I build the package with GHC 7.10.1-rc1; the normal 7.8.4 builds succeed just fine.
duration2 :: MonadIO m => m a -> m (Seconds, a)
duration2 x = do
start <- liftIO offsetTime
res <- x
end <- liftIO start
return (end, res)
That works. Is there anything else that should be MonadIO'd? Not such a fan when it's merely a return type and liftIO works, but duration takes an IO so is harder
Please either make it compatible with GHC 7.0 again or add a base >= 4.4
constraint for the next release...
Building extra-1.0...
Preprocessing library extra-1.0...
[ 1 of 15] Compiling System.Info.Extra ( src/System/Info/Extra.hs, dist/build/System/Info/Extra.o )
[ 2 of 15] Compiling Numeric.Extra ( src/Numeric/Extra.hs, dist/build/Numeric/Extra.o )
[ 3 of 15] Compiling Data.Tuple.Extra ( src/Data/Tuple/Extra.hs, dist/build/Data/Tuple/Extra.o )
[ 4 of 15] Compiling Data.List.Extra ( src/Data/List/Extra.hs, dist/build/Data/List/Extra.o )
[ 5 of 15] Compiling Data.IORef.Extra ( src/Data/IORef/Extra.hs, dist/build/Data/IORef/Extra.o )
[ 6 of 15] Compiling Data.Either.Extra ( src/Data/Either/Extra.hs, dist/build/Data/Either/Extra.o )
[ 7 of 15] Compiling Control.Monad.Extra ( src/Control/Monad/Extra.hs, dist/build/Control/Monad/Extra.o )
[ 8 of 15] Compiling System.Directory.Extra ( src/System/Directory/Extra.hs, dist/build/System/Directory/Extra.o )
[ 9 of 15] Compiling Control.Exception.Extra ( src/Control/Exception/Extra.hs, dist/build/Control/Exception/Extra.o )
[10 of 15] Compiling System.Environment.Extra ( src/System/Environment/Extra.hs, dist/build/System/Environment/Extra.o )
[11 of 15] Compiling System.Time.Extra ( src/System/Time/Extra.hs, dist/build/System/Time/Extra.o )
src/System/Time/Extra.hs:70:30: Not in scope: `forkIOWithUnmask'
This happened with GHC 8.4.1:
Test suite extra-test: RUNNING...
\(x :: IO Int) -> void (once x) == return ()
*** Failed! Falsifiable (after 4 tests):
<<IO>>
Test suite extra-test: FAIL
The error occurs only spuriously, though.
The obvious functions:
groupSortBy :: (a -> a -> Ordering) -> [a] -> [[a]]
groupSortOn :: Ord b => (a -> b) -> [a] -> [[a]]
Those are some functions I've been using a lot in parsers:
import qualified Control.Category as C
import Control.Monad
import Control.Applicative
composeMany :: (Alternative m, C.Category f) => m (f a a) -> m (f a a)
composeMany f = foldl (C.>>>) C.id <$> many f
($^) :: (Monad m) => m (a -> a) -> a -> m a
($^) f a = f `ap` return a
Where I'm parsing values that may come in any order, I create an initial state and a parser that returns a function updating that state, so I can just call composeMany parser $^ initialState
.
Would you mind adding them? I'd think ($^)
goes into Control.Monad.Extra, but I don't know where composeMany
would go. If you want them, I can add docs and prepare a PR.
First read http://chrisdone.com/posts/measuring-duration-in-haskell.
Now look at offsetTime
:
-- | Call once to start, then call repeatedly to get the elapsed time since the first
-- call. Values will usually increase, unless the system clock is updated
-- (if you need the guarantee, see 'offsetTimeIncrease').
offsetTime :: IO (IO Seconds)
offsetTime = do
start <- getCurrentTime
return $ do
end <- getCurrentTime
return $ fromRational $ toRational $ end `diffUTCTime` start
This is described as "the naive, inaccurate solution" in the blog post.
The "nice" implementation uses getTime Monotonic
from the clock package, and returns 1e-9 * fromRational (toNanoSecs $ end - start)
.
src/System/IO/Extra.hs:76:41: error:
Ambiguous occurrence ‘hGetContents'’
It could refer to
either ‘System.IO.hGetContents'’,
imported from ‘System.IO’ at src/System/IO/Extra.hs:23:1-16
(and originally defined in ‘GHC.IO.Handle.Text’)
or ‘System.IO.Extra.hGetContents'’,
defined at src/System/IO/Extra.hs:65:1
|
76 | readFile' file = withFile file ReadMode hGetContents'
| ^^^^^^^^^^^^^
src/System/IO/Extra.hs:80:79: error:
Ambiguous occurrence ‘hGetContents'’
It could refer to
either ‘System.IO.hGetContents'’,
imported from ‘System.IO’ at src/System/IO/Extra.hs:23:1-16
(and originally defined in ‘GHC.IO.Handle.Text’)
or ‘System.IO.Extra.hGetContents'’,
defined at src/System/IO/Extra.hs:65:1
|
80 | readFileEncoding' e file = withFile file ReadMode $ \h -> hSetEncoding h e >> hGetContents' h
| ^^^^^^^^^^^^^
src/System/IO/Extra.hs:88:53: error:
Ambiguous occurrence ‘hGetContents'’
It could refer to
either ‘System.IO.hGetContents'’,
imported from ‘System.IO’ at src/System/IO/Extra.hs:23:1-16
(and originally defined in ‘GHC.IO.Handle.Text’)
or ‘System.IO.Extra.hGetContents'’,
defined at src/System/IO/Extra.hs:65:1
|
88 | readFileBinary' file = withBinaryFile file ReadMode hGetContents'
| ^^^^^^^^^^^^^
src/System/IO/Extra.hs:121:16: error:
Ambiguous occurrence ‘readFile'’
It could refer to
either ‘System.IO.readFile'’,
imported from ‘System.IO’ at src/System/IO/Extra.hs:23:1-16
or ‘System.IO.Extra.readFile'’,
defined at src/System/IO/Extra.hs:76:1
|
121 | out <- readFile' file
| ^^^^^^^^^
cabal: Failed to build extra-1.5. See the build log above for details.
As a Hackage trustee, I have created revisions for these versions that should prevent users from getting these errors. See e.g. https://hackage.haskell.org/package/extra-1.7.6/revisions/.
The following pattern seems to be extremely ubiquitous: fmap concat $ forM
. A quick google search brings hundreds of matches.
We have concatMapM
in Control.Monad.Extra
, so perhaps we could add concatForM
too? One possible implementation is simply concatForM = flip concatMapM
. Can send a PR if you like this idea.
Noticed via Stackage's Haskell Platform build, which constrains QuickCheck.
$ cabal install extra --constraint 'QuickCheck < 2.7' --run-tests
...
test/TestGen.hs:34:90:
Not in scope: ‘===’
Perhaps you meant one of these:
‘==’ (imported from Prelude), ‘==>’ (imported from TestUtil)
test/TestGen.hs:35:90:
Not in scope: ‘===’
Perhaps you meant one of these:
‘==’ (imported from Prelude), ‘==>’ (imported from TestUtil)
test/TestGen.hs:36:90:
Not in scope: ‘===’
Perhaps you meant one of these:
‘==’ (imported from Prelude), ‘==>’ (imported from TestUtil)
Failed to install extra-0.3.1
cabal: Error: some packages failed to install:
extra-0.3.1 failed during the building phase. The exception was:
ExitFailure 1
do (a,_) <- duration $ sleep 1; return $ a >= 1 && a <= 1.1
*** Failed! Falsifiable (after 1 test):
extra-test: Test failed
CallStack (from HasCallStack):
error, called at test/TestUtil.hs:47:28 in main:TestUtil
Test suite extra-test: FAIL
The test failed two times in a row. The computer is not top notch, but is not terrible either: a rather old iMac with Core i3 550 @ 3.2GHz, but it was under very heavy load (compiling multiple Haskell packages and a couple of GHC versions at the same time). So, maybe, it would not be a bad idea to slightly increase the upper bound just in case...
The code needs thorough documentation and testing. Most of the code has been tested outside of extra, but it could do with additional testing.
firstJust :: (a -> Maybe b) -> [a] -> Maybe b
Find the first element of a list for which the operation returns Just, along with the result of the operation. Like find but useful where the function also computes some expensive information that can be reused. Particular useful when the function is monadic, see firstJustM.
firstJust id [Nothing,Just 3] == Just 3
firstJust id [Nothing,Nothing] == Nothing
should it not be ???
firstJust pure [Nothing,Just 3] == Just 3
firstJust pure [Nothing,Nothing] == Nothing
OR
firstJust Just [Nothing,Just 3] == Just 3
firstJust Just [Nothing,Nothing] == Nothing
This is probably more of a question than an issue but is there any reason that nubOrd
is not tail recursive, did you benchmark it to be faster as written? Also the red black tree implementation uses no strictness annotations, was it tested that they make no difference?
I just found myself wishing that concatMapM
had type (Monad m, Monoid o) => (a -> m o) -> [a] -> m o
or even (Foldable f, Monad m, Monoid o) => (a -> m o) -> f a -> m o
.
Many other functions like firstJust
, allSame
etc. could get similar generalizations.
Would it be ok to generalize the existing functions in Data.Monad.Extra and Data.List.Extra in this way? Would there be a point in adding generalized variants instead?
I've now found myself several times using the following function:
collect :: Ord k => [(k, v)] -> [(k, NonEmpty v)]
collect = map (second NonEmpty.fromList) . groupSort
Having the type tell you that the value lists are non-empty often turns out to be very handy.
Would you be interested in including such a function and possibly similar variants for groupSortOn
and groupSortBy
?
I'm not sure about the naming so far. Would there be a point in deprecating the existing group functions in Data.List.Extra
in favor of improved versions in Data.List.NonEmpty.Extra
?
semigroups
(and soon base
!) contains a few more group variants that might serve as inspiration…
I use NonEmpty
a lot and these are the functions I find useful:
-- | /O(n)/. Append an element to a list.
(|:) :: [a] -> a -> NonEmpty a
(|:) xs x = foldr cons (pure x) xs
-- | Append a list to a non-empty list.
appendl :: NonEmpty a -> [a] -> NonEmpty a
appendl (x :| xs) l = x :| (xs ++ l)
-- | Append a non-empty list to a list.
appendr :: [a] -> NonEmpty a -> NonEmpty a
appendr l nel = foldr cons nel l
There are certainly other useful functions for NonEmpty
(e.g., the counterparts of many functions in Data.List.Extra
), but I find these particularly useful since they obviate the need to use the partial fromList
. So hopefully this is a reasonable starting point.
Just to get you ready for what's coming:
[ 2 of 17] Compiling Data.Either.Extra ( src/Data/Either/Extra.hs, dist/build/Data/Either/Extra.o )
src/Data/Either/Extra.hs:9:22: error:
Ambiguous occurrence ‘fromLeft’
It could refer to either ‘Data.Either.fromLeft’,
imported from ‘Data.Either’ at src/Data/Either/Extra.hs:12:1-18
or ‘Data.Either.Extra.fromLeft’,
defined at src/Data/Either/Extra.hs:21:1
src/Data/Either/Extra.hs:9:32: error:
Ambiguous occurrence ‘fromRight’
It could refer to either ‘Data.Either.fromRight’,
imported from ‘Data.Either’ at src/Data/Either/Extra.hs:12:1-18
or ‘Data.Either.Extra.fromRight’,
defined at src/Data/Either/Extra.hs:30:1
Hoogling for spanEnd
I found your definition and the one in ghc's Util
, see
https://hackage.haskell.org/package/ghc-8.10.2/docs/src/Util.html#spanEnd
spanEnd :: (a -> Bool) -> [a] -> ([a], [a])
spanEnd p l = go l [] [] l
where
go yes _ rev_no [] = (yes, reverse rev_no)
go yes rev_yes rev_no (x:xs)
| p x = go yes (x : rev_yes) rev_no xs
| otherwise = go xs [] (x : rev_yes ++ rev_no) xs
This implementation is quite a bit more complicated than its specification spanEnd p l == reverse (span p (reverse l))
. This suggests there were good reasons for the convoluted implementation.
So maybe switch to GHC's implementation?!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.