relude 0.1.1 → 1.2.2.2
raw patch · 69 files changed
Files
- CHANGELOG.md +521/−27
- CONTRIBUTING.md +0/−50
- LICENSE +1/−1
- README.md +715/−209
- benchmark/Main.hs +41/−13
- relude.cabal +201/−93
- src/Relude.hs +254/−69
- src/Relude/Applicative.hs +59/−31
- src/Relude/Base.hs +46/−78
- src/Relude/Bool.hs +27/−5
- src/Relude/Bool/Guard.hs +150/−45
- src/Relude/Bool/Reexport.hs +25/−10
- src/Relude/Container.hs +33/−10
- src/Relude/Container/One.hs +203/−23
- src/Relude/Container/Reexport.hs +39/−18
- src/Relude/Debug.hs +283/−67
- src/Relude/DeepSeq.hs +77/−21
- src/Relude/Enum.hs +149/−0
- src/Relude/Exception.hs +36/−22
- src/Relude/Extra.hs +98/−0
- src/Relude/Extra/Bifunctor.hs +69/−4
- src/Relude/Extra/CallStack.hs +58/−0
- src/Relude/Extra/Enum.hs +40/−43
- src/Relude/Extra/Foldable.hs +68/−0
- src/Relude/Extra/Foldable1.hs +445/−0
- src/Relude/Extra/Group.hs +22/−9
- src/Relude/Extra/Lens.hs +276/−0
- src/Relude/Extra/Map.hs +155/−51
- src/Relude/Extra/Newtype.hs +110/−0
- src/Relude/Extra/Tuple.hs +185/−0
- src/Relude/Extra/Type.hs +170/−0
- src/Relude/File.hs +193/−0
- src/Relude/Foldable.hs +25/−4
- src/Relude/Foldable/Fold.hs +121/−88
- src/Relude/Foldable/Reexport.hs +28/−3
- src/Relude/Function.hs +48/−11
- src/Relude/Functor.hs +26/−4
- src/Relude/Functor/Fmap.hs +70/−15
- src/Relude/Functor/Reexport.hs +29/−14
- src/Relude/Lifted.hs +100/−12
- src/Relude/Lifted/Concurrent.hs +89/−34
- src/Relude/Lifted/Env.hs +44/−0
- src/Relude/Lifted/Exit.hs +48/−27
- src/Relude/Lifted/File.hs +49/−44
- src/Relude/Lifted/Handle.hs +70/−0
- src/Relude/Lifted/IORef.hs +141/−25
- src/Relude/Lifted/Terminal.hs +54/−0
- src/Relude/List.hs +119/−12
- src/Relude/List/NonEmpty.hs +325/−0
- src/Relude/List/Reexport.hs +41/−18
- src/Relude/List/Safe.hs +0/−75
- src/Relude/Monad.hs +80/−12
- src/Relude/Monad/Either.hs +133/−78
- src/Relude/Monad/Maybe.hs +84/−40
- src/Relude/Monad/Reexport.hs +33/−24
- src/Relude/Monad/Trans.hs +112/−33
- src/Relude/Monoid.hs +134/−20
- src/Relude/Nub.hs +113/−20
- src/Relude/Numeric.hs +101/−0
- src/Relude/Print.hs +115/−63
- src/Relude/String.hs +53/−10
- src/Relude/String/Conversion.hs +425/−71
- src/Relude/String/Reexport.hs +216/−19
- src/Relude/Unsafe.hs +59/−23
- test/Doctest.hs +10/−8
- test/Spec.hs +12/−11
- test/Test/Relude/Container/One.hs +114/−0
- test/Test/Relude/Gen.hs +36/−0
- test/Test/Relude/Property.hs +50/−75
CHANGELOG.md view
@@ -1,57 +1,551 @@-Change log-==========+# Changelog -0.1.1-=====+`relude` uses [PVP Versioning][1].+The changelog is available [on GitHub][2]. -* [#44](https://github.com/kowainik/relude/issues/44)- Implement parser deriviation from pretty-printers+## 1.2.2.2 – Aug 5, 2025 -0.1.0-=====+- Documentation improvements -* [#7](https://github.com/kowainik/relude/issues/7):+## 1.2.2.1 – Jul 29, 2025++- Allow containers-0.8.++- Documentation improvements++## 1.2.2.0 – Oct 13, 2024++- Support GHC-9.10 & GHC-9.8.+- Allow hashable-1.5++## 1.2.1.0 – Oct 4, 2023++- [#439](https://github.com/kowainik/relude/issues/439):+ Support GHC-9.6.+- Upgrade dependencies. Allow:+ - `ghc-prim-0.10`+ - `text-2.1`+ - `bytestring-2.12`+ - `containers-0.7`+ - `deepseq-1.5`+ - `hedgehod-1.3`+ - `doctest-0.22`+- Upgrade tests to work with GHC-9.6.++## 1.2.0.0 – Mar 1, 2023++- [#420](https://github.com/kowainik/relude/issues/420):+ Support GHC-9.4.+- Reexport `type (~)` from `Data.Type.Equality` for GHC-9.4.+- Add `-Wredundant-strictness-flags` and `-Wforall-identifier` warnings for+ GHC-9.4.+- [#430](https://github.com/kowainik/relude/issues/430):+ Upgrade dependencies:+ - `ghc-prim-0.9`+ - `mtl-2.3`+ - `hedgehog-1.2`+ - `doctest-0.21`+- Drop support of GHC-8.2.++## 1.1.0.0 — Jun 9, 2022++- [#388](https://github.com/kowainik/relude/issues/388):+ Support GHC-9.2.+- [#363](https://github.com/kowainik/relude/issues/363):+ Remove the `Option` type, reexported from `Data.Semigroup`+ (which was removed in `base-4.16`, GHC 9.2)++ **Migration guide:** Use `Maybe` instead of `Option`.++- [#372](https://github.com/kowainik/relude/issues/372):+ Warn on usages of `readFileText`, `readFileLText`, `readFile` and `readFile'`.++ > _NOTE:_ This is a **breaking change** if you build with `-Werror`+ > and use the above functions. Use `readFileBS` / `readFileLBS`+ > instead to convert to `Text` / `LText` / `String` using+ > `decodeUtf8` and similar functions.++- [#394](https://github.com/kowainik/relude/issues/394):+ Support `hashable-1.4`.+- [#408](https://github.com/kowainik/relude/issues/408):+ Switch benchmarks from `criterion` to `tasty-bench`.+- [#404](https://github.com/kowainik/relude/issues/404):+ Fix condidion for `ordNubOn`, `intNub`, `intNubOn` export for `Relude.Nub` module.+ Use min version of `containers-0.6.0` instead of min version of GHC `8.4`.+- Minor documentation improvements and fixes.++## 1.0.0.1 — Mar 15, 2021++- Minor documentation changes.++## 1.0.0.0 — Mar 12, 2021++- [#353](https://github.com/kowainik/relude/issues/353):+ Reexport most common modules from the following libraries:++ - `containers`+ - `unordered-containers`+ - `text`+ - `bytestring`++ Now, when using `relude`, you don't need to add these libraries to+ your `.cabal` file to enjoy their main API. Try removing them from+ your `.cabal` file after upgrading to this version of `relude` to+ see if you still need them.++ To utilise this feature, update the `mixin` part of your package+ configuration (if you're using the mixins approach) to the following:++ ```cabal+ mixins: base hiding (Prelude)+ , relude (Relude as Prelude)+ , relude+ ```++- [#345](https://github.com/kowainik/relude/issues/345):+ Support GHC-9.0.+- Upgrade minor GHC versions to GHC-8.10.4 and GHC-8.8.4.+- [#268](https://github.com/kowainik/relude/issues/268):+ Drop support of GHC-8.0.2.+- [#270](https://github.com/kowainik/relude/issues/270):+ Standardise `universe`, `universeNonEmpty` and `inverseMap` functions that+ previously were introduced in the `Relude.Extra.Enum` module. `Relude.Enum`+ module created that is exported in the main `Relude` module by default.++ **Migration guide:** If you were using any of these functions you can now+ remove `Relude.Extra.Enum` from your imports and explicit `mixins` section+ as they are available for you with the `Relude` module.++- Remove the `Eq` constraint on `universeNonEmpty`+- [#269](https://github.com/kowainik/relude/issues/269):+ Remove the `Relude.Extra.Validation` module.++ **Migration guide:**+ If you use `Relude.Extra.Validation` in you project you need to:++ 1. Add `validation-selective` into the `build-depends` section of your+ `.cabal` file.+ 2. Change imports of `Relude.Extra.Validation` to `Validation`:++ ```haskell+ -- Was:+ import Relude.Extra.Validation (Validation (..), ..)+ -- Became:+ import Validation (Validation (..), ..)+ ```++- [#346](https://github.com/kowainik/relude/issues/346),+ [#347](https://github.com/kowainik/relude/issues/347):+ Reimplement `ordNub` through `nubOrd` from `containers`.+ Add `ordNubOn`, `intNub` and `intNubOn` functions.+- [#327](https://github.com/kowainik/relude/issues/327):+ Add `infinitely` as more strictly typed `forever`.+- [#311](https://github.com/kowainik/relude/issues/311):+ Add `maybeAt` function — the non-operator version of `!!?` with its+ arguments flipped.+- [#314](https://github.com/kowainik/relude/issues/314):+ Add lifted versions of functions to work with `Handle`:++ - `hFlush`+ - `hIsEOF`+ - `hSetBuffering`+ - `hGetBuffering`++- [#305](https://github.com/kowainik/relude/issues/305):+ Add lifted versions of functions to work with environment:++ - `getArgs`+ - `lookupEnv`++- Add lifted version of the `readFile'` function.+- Reexport the `BufferMode` type from `base`.+- [#309](https://github.com/kowainik/relude/issues/309):+ Reexport `span` from `Data.List`.+- [#319](https://github.com/kowainik/relude/issues/319):+ Implement `partitionWith`.+- [#307](https://github.com/kowainik/relude/issues/307):+ Add `foldr1` to `Foldable1`.+- [#316](https://github.com/kowainik/relude/issues/316):+ Add `average` and `average1` — efficient functions for finding+ average on foldable structures.+- [#306](https://github.com/kowainik/relude/issues/306):+ Add `maximumOn1` and `minimumOn1` to `Foldable1`.+- [#301](https://github.com/kowainik/relude/issues/301):+ Add `traceShowWith` to `Relude.Debug`.+- [#304](https://github.com/kowainik/relude/issues/304),+ [#317](https://github.com/kowainik/relude/issues/317):+ Various documentation improvements.+- Updates to `relude`-specific `.hlint` rules.++Thanks @googleson78, @sushi-shi, @rektrex, @aleator, @mjgpy3, @dalpd,+@Bodigrim for helping with this release!++## 0.7.0.0 — May 14, 2020++- [#253](https://github.com/kowainik/relude/issues/253):+ Support GHC-8.10. Upgrade GHC-8.8 to 8.8.3.+- Significant documentation improvements:+ - Add high-level description to each reexported module.+ - Add String Conversion Table.+ - Add `NonEmpty` lists functions tables.+ - Add `@since` annotations.+ - Improve README.+ - Inline some external reexports into explicit lists of exports.+ - Rewrite top-level `cabal` description.+- [#234](https://github.com/kowainik/relude/issues/234):+ Reexport `scanl1`, `scanr1`, `scanl'` from `Data.List`.+- [#256](https://github.com/kowainik/relude/issues/256):+ Make `cycle` total function.+- [#233](https://github.com/kowainik/relude/issues/233):+ Add `etaReaderT` to `Relude.Monad.Trans` to help with performance.+- [#294](https://github.com/kowainik/relude/issues/294):+ Add `atomicModifyIORef_` and `atomicModifyIORef'_`.+- [#293](https://github.com/kowainik/relude/issues/293):+ Add `memptyIfFalse` and `memptyIfTrue` functions.+- Reexport `NonEmpty` functions from `Relude.List.NonEmpty` instead of+ `Relude.List.Reexport`.+- [#239](https://github.com/kowainik/relude/issues/239):+ Reexport more STM functions that work with `TMVar` from+ `Relude.Lifted.Concurrent`.+- [#227](https://github.com/kowainik/relude/issues/227):+ Create `Relude.Extra` module+- [#228](https://github.com/kowainik/relude/issues/228):+ Add `universeNonEmpty` function.+- [#249](https://github.com/kowainik/relude/issues/249):+ **Breaking change:** Fix infix of the `Relude.Extra.Lens` `(^.)` operator.+ Change it to `infixl 8`.+- Reexport partial `read` from `Relude.Unsafe` for consistency.+- [#244](https://github.com/kowainik/relude/issues/244):+ Remove deprecated functions: `prec`, `dupe` and `mapBoth`.++ **Migration rules:**++ - `prec`: use `prev` instead+ - `dupe`: use `dup` instead+ - `mapBoth`: use `bimapBoth` instead++- [#246](https://github.com/kowainik/relude/issues/246):+ Deprecate `Relude.Extra.Validation` in favour of+ [`validation-selective`](https://hackage.haskell.org/package/validation-selective)++ **Migration rules:**+ If you use `Relude.Extra.Validation` in you project you need to:++ 1. Add `validation-selective` into the `build-depends` section of your+ `.cabal` file.+ 2. Change imports of `Relude.Extra.Validation` to `Validation`:++ ```haskell+ -- Was:+ import Relude.Extra.Validation (Validation (..), ..)+ -- Became:+ import Validation (Validation (..), ..)+ ```++- [#196](https://github.com/kowainik/relude/issues/196):+ Deprecate `mapToFst` and `mapToSnd`. Introduce `toFst` and `toSnd`+ in `Relude.Extra.Tuple` as shorter aliases for `mapToFst`. Implement+ `fmapToFst` and `fmapToSnd`. Add more HLint rules for+ `Relude.Extra.Tuple` functions.++ **Migration rules:**++ - Replace `mapToFst` with `toFst`+ - Replace `mapToSnd` with `toSnd`+ - You can now use `fmapToFst` and `fmapToSnd` instead of+ `[f]map (mapToFst f)` and `[f]map (mapToSnd f)`++- [#286](https://github.com/kowainik/relude/issues/286):+ **Breaking change:** `readEither` is not polymorphic over the first argument+ anymore. Now it takes `String`.++ **Migration rules:** Use one of the conversion function from the+ `Relude.String.Conversion` module to covert your old input value into+ `String`.++ For example, if you had++ ```haskell+ readEither @Text @Int myText+ ```++ Now it should become:++ ```haskell+ readEither @Int (toString myText)+ ```++- [#281](https://github.com/kowainik/relude/issues/281):+ Move `One` property tests from `doctest` to `hedgehog`.+ Significant test time boost.+- [#264](https://github.com/kowainik/relude/issues/264):+ Support Dhall-16.0.0 in HLint rules.++## 0.6.0.0 — Oct 30, 2019++- [#171](https://github.com/kowainik/relude/issues/171):+ Add custom type errors to various functions and instances.++ - `head`, `tail`, `last`, `init`+ - `words`, `unwords`, `lines`, `unlines`+ - `error`+ - `ToText`, `ToLText`, `ToString` instances for bytestrings+ - `Foldable1` instance for ordinary lists+ - `Monad` instance for `Validation`++ (by [@vrom911](https://github.com/vrom911), [@chshersh](https://github.com/chshersh))++- [#164](https://github.com/kowainik/relude/issues/164):+ Reexport `ShortByteString`, `toShort`/`fromShort` functions.+ (by [@vrom911](https://github.com/vrom911))+- [#182](https://github.com/kowainik/relude/issues/182):+ Support GHC-8.8.1.+ (by [@chshersh](https://github.com/chshersh))+- [#168](https://github.com/kowainik/relude/pull/168),+ [#197](https://github.com/kowainik/relude/pull/197):+ Improve documentation significantly (more and better examples, better wording).+ (by [@chshersh](https://github.com/chshersh),+ [@vrom911](https://github.com/vrom911),+ [@Cmdv](https://github.com/Cmdv))+- [#177](https://github.com/kowainik/relude/issues/177):+ Improve usage of performance pragmas.+ (by [@chshersh](https://github.com/chshersh))+- [#167](https://github.com/kowainik/relude/issues/167):+ Rename functions (and deprecate old versions):++ - `prec` to `prev`+ - `dupe` to `dup`++ (by [@Cmdv](https://github.com/Cmdv), [@chshersh](https://github.com/chshersh))++- [#192](https://github.com/kowainik/relude/issues/192):+ Reexport `foldMap'` from `Data.Foldable`.+ (by [@tfausak](https://github.com/tfausak))+- [#201](https://github.com/kowainik/relude/issues/201):+ Implement `!!?` as a safe equivalent of `!!` that returns a `Maybe`.+ (by [@kutyel](https://github.com/kutyel))+- [#203](https://github.com/kowainik/relude/issues/203):+ Implement the `guarded` combinator.+ (by [@JonathanLorimer](https://github.com/JonathanLorimer))+- [#214](https://github.com/kowainik/relude/issues/214):+ Add `mapMaybeM` function.+ (by [@vrom911](https://github.com/vrom911))+- [#174](https://github.com/kowainik/relude/issues/174):+ Implement `bimapBoth` in `Relude.Extra.Tuple` module,+ mark `mapBoth` as DEPRECATED.+ (by [@astynax](https://github.com/astynax))+- [#221](https://github.com/kowainik/relude/issues/221):+ Improve documentation for the `Validation` module significantly.+ (by [@chshersh](https://github.com/chshersh))+- [#176](https://github.com/kowainik/relude/issues/176):+ Implement property-based tests for `Validation` laws.+ (by [@astynax](https://github.com/astynax))+- [#172](https://github.com/kowainik/relude/issues/172):+ Add `Monoid` and `Semigroup` instances for the `Validation` type.+ (by [@mauriciofierrom](https://github.com/mauriciofierrom))+- [#156](https://github.com/kowainik/relude/issues/156):+ Implement helper type-level functions in `Relude.Extra.Type`.+ (by [@TheMatten](https://github.com/TheMatten))+- [#170](https://github.com/kowainik/relude/issues/170):+ Implement `Elem` type family.+ (by [@kutyel](https://github.com/kutyel))+- [#165](https://github.com/kowainik/relude/pull/165):+ Re-export `GHC.Float.atan2`.+ (by [@ethercrow](https://github.com/ethercrow))+- [#155](https://github.com/kowainik/relude/issue/155):+ Implement `foldlSC` — short-circuting list fold — in `Relude.Extra.Foldable`.+ (by [@josephcsible](https://github.com/josephcsible))+- [#158](https://github.com/kowainik/relude/issue/158):+ Support GHC-8.6.5.+ (by [@chshersh](https://github.com/chshersh))+- [#148](https://github.com/kowainik/relude/issues/148):+ Migrate HLint rules to the latest Dhall spec.+ (by [@vrom911](https://github.com/vrom911))+- [#178](https://github.com/kowainik/relude/issues/178):+ Made `die` be polymorphic in its return type.+ (by [@ilyakooo0](https://github.com/ilyakooo0))+- [#162](https://github.com/kowainik/relude/pull/162),+ [#189](https://github.com/kowainik/relude/pull/189),+ [#190](https://github.com/kowainik/relude/pull/190),+ [#191](https://github.com/kowainik/relude/pull/191),+ [#193](https://github.com/kowainik/relude/pull/193),+ [#194](https://github.com/kowainik/relude/pull/194),+ [#195](https://github.com/kowainik/relude/pull/195):+ Various refactorings and code improvements:++ - **Breaking change:** Reorder type parameters to `asumMap`+ - Implement `andM`, `orM`, `allM`, and `anyM` in terms of `&&^` and `||^`+ - Use `foldr` instead of explicit recursion and `toList`+ - Use `mapToFst` instead of `zip` to improve list fusion in `inverseMap`+ - Implement `foldMap1` for `NonEmpty` in terms of `foldr`+ - Use `$>` instead of `*>` and `pure` where possible+ - Implement `asumMap` and `foldMapA` by coercing `foldMap`+ - Return Failure early in `<*` and `*>` too++ (by [@josephcsible](https://github.com/josephcsible))++- [#187](https://github.com/kowainik/relude/issues/187):+ Remove `tasty` and `tasty-hedgehog` dependencies and their redundant imports.+ (by [@dalpd](https://github.com/dalpd))++## 0.5.0 — Mar 18, 2019++- [#127](https://github.com/kowainik/relude/issues/127):+ Implement `Relude.Extra.Lens` module.+- [#125](https://github.com/kowainik/relude/issues/125):+ Moved many numerical functions and types in `Relude.Numeric`.+ Reexport `toIntegralSized` from `Data.Bits`.+ Add `integerToBounded` and `integerToNatural` in `Relude.Numeric`.+- [#121](https://github.com/kowainik/relude/issues/121):+ Reexport `Ap` from `Data.Monoid`. Change definition of `foldMapA` to use `Ap`.+- [#129](https://github.com/kowainik/relude/issues/129):+ Add `appliedTo` and `chainedTo` as named versions of operators `=<<` and `<**>`.+- [#138](https://github.com/kowainik/relude/issues/138):+ Add `RealFloat` to `Relude.Numeric`.+- [#144](https://github.com/kowainik/relude/issues/144):+ Add `traverseToSnd` and friends to `Relude.Extra.Tuple`.+- [#140](https://github.com/kowainik/relude/issues/140):+ Improve text of custom compile-time error messages for `elem` functions.+- [#136](https://github.com/kowainik/relude/issues/136):+ Cover `Relude.Extra.*` modules with custom HLint rules.+- [#146](https://github.com/kowainik/relude/issues/146):+ Improve documentation for `Relude.File` file: be more explicit about system+ locale issues.+- Improve documentation for `One` typeclass and add tests.+- Support ghc-8.6.4 and ghc-8.4.4.+ Drop support for ghc-8.6.1 and ghc-8.4.3.++## 0.4.0 — Nov 6, 2018++- [#70](https://github.com/kowainik/relude/issues/70):+ Reexport `Contravariant` for GHC >= 8.6.1.+- [#103](https://github.com/kowainik/relude/pull/104):+ Drop `utf8-string` dependency and improve performance of conversion functions.+- [#98](https://github.com/kowainik/relude/issues/98):+ Reexport `Bifoldable` related stuff from `base`.+- [#99](https://github.com/kowainik/relude/issues/99):+ Reexport `Bitraversable` related stuff from `base`.+- [#100](https://github.com/kowainik/relude/issues/100):+ Add `Relude.Extra.Validation` with `Validation`data type.+- [#89](https://github.com/kowainik/relude/issues/89):+ Add `Relude.Extra.Type` module containing a `typeName` function.+- [#92](https://github.com/kowainik/relude/issues/92)+ Add `Relude.Extra.Tuple` module, containing+ `dupe`, `mapToFst`, `mapToSnd`, and `mapBoth` functions.+- [#97](https://github.com/kowainik/relude/issues/97):+ Add `(&&^)` and `(||^)` operators.+- [#81](https://github.com/kowainik/relude/issues/81):+ Add `asumMap` to `Foldable` functions.+- [#80](https://github.com/kowainik/relude/issues/80):+ Add hlint rules for `whenLeft`, `whenLeftM`, `whenRight` and `whenRightM`.+- [#79](https://github.com/kowainik/relude/issues/79):+ Add HLint rules for `One` typeclass.+- Remove `openFile` and `hClose`.+- [#83](https://github.com/kowainik/relude/pull/83):+ Make documentation for `nub` functions prettier.+- [#109](https://github.com/kowainik/relude/issues/109):+ Use Dhall v3.0.0 for hlint file generation.++## 0.3.0++- [#41](https://github.com/kowainik/relude/issues/41):+ Add `Foldable1`.+- [#63](https://github.com/kowainik/relude/issues/63):+ Remove `Print` typeclass.+ Add `put[L]BS[Ln]` functions.+ `trace` functions now take `String` as argument instead of `Text`.++ **Important:** this is a breaking change. If you used polymorphic `putStrLn`+ you need to remove type application or switch to one of the monomorphic+ functions. Also, you can't abstract over `Print` typeclass anymore.++- [#66](https://github.com/kowainik/relude/issues/66):+ Export `(>>>)` and `(<<<)` from `Control.Category`.+- [#59](https://github.com/kowainik/relude/issues/59):+ Introduce `flap` function and its operator version `??`.+- [#64](https://github.com/kowainik/relude/issues/64):+ Improve performance of functions from `Foldable1`.+ Add `foldl1'` function.+- Reexport `uncons` from `base`.+- Rewrite `die` implementation to use `die` from `base`.+- [#19](https://github.com/kowainik/relude/issues/19):+ Rewrite `.hlint.yaml` to Dhall.+- Move `stdin`- and `stdout`-related functions to new module `Relude.Lifted.Terminal`.+- [#67](https://github.com/kowainik/relude/issues/67):+ Add HLint rules for `put*` functions.+- [#22](https://github.com/kowainik/relude/issues/22):+ `readFile`, `writeFile` and `appendFile` now work with `String`.+ Add lifted version of `hClose`.+ Add `readFile`, `writeFile` and `appendFile` alternatives for `Text` and `ByteString`.+- [#61](https://github.com/kowainik/relude/issues/61):+ Add `under2` and `underF2` functions to `Relude.Extra.Newtype`.+- [#60](https://github.com/kowainik/relude/issues/60):+ Add `hoistMaybe` and `hoistEither` functions.++## 0.2.0++- [#43](https://github.com/kowainik/relude/issues/43):+ Implement `Relude.Extra.Newtype` module.+- [#46](https://github.com/kowainik/relude/issues/46):+ Add a function that returns its own name.+- [#48](https://github.com/kowainik/relude/issues/48):+ Export `<&>` from `base`.+ Also reexport `fromLeft` and `fromRight` from `base` where possible.+- [#49](https://github.com/kowainik/relude/issues/49):+ Speed up and refactor property tests.+- [#54](https://github.com/kowainik/relude/issues/54):+ Improve documentation.+ Add more examples to documentation and more tests.+ Reexport `withReader` and `withReaderT`.+ Remove `safeHead`.+ Rename `Relude.List.Safe` to `Relude.List.NonEmpty`.++## 0.1.1++- [#44](https://github.com/kowainik/relude/issues/44):+ Implement parser deriviation from pretty-printers.++## 0.1.0++- [#7](https://github.com/kowainik/relude/issues/7): Remove `Container.Class.Container`. Export `Foldable`.-* [#2](https://github.com/kowainik/relude/issues/2):+- [#2](https://github.com/kowainik/relude/issues/2): Remove `microlens` from dependencies.-* [#10](https://github.com/kowainik/relude/issues/10):+- [#10](https://github.com/kowainik/relude/issues/10): Remove `VarArg` module.-* [#9](https://github.com/kowainik/relude/issues/9):+- [#9](https://github.com/kowainik/relude/issues/9): Remove `safe-exceptions` from dependencies. Reexport `Exception` and `SomeException` from `Control.Exception` instead.-* [#11](https://github.com/kowainik/relude/issues/11):+- [#11](https://github.com/kowainik/relude/issues/11): Remove `TypeOps` module and `type-operators` dependency.-* [#13](https://github.com/kowainik/relude/issues/13):+- [#13](https://github.com/kowainik/relude/issues/13): Remove `list`, `getContents`, `interact`, `getArgs`, `note` functions. Remove `Lifted.ST` module. Rename `Lifted.Env` to `Lifted.Exit`.-* [#16](https://github.com/kowainik/relude/issues/16):+- [#16](https://github.com/kowainik/relude/issues/16): Rename `whenLeft`, `whenRight`, `whenLeftM`, `whenRightM` to `whenLeft_` and `whenRight_`, `whenLeftM_` and `whenRightM_`. Add `whenLeft`, `whenRight`, `whenLeftM`, `whenRightM` which return the value.-* [#18](https://github.com/kowainik/relude/issues/18):+- [#18](https://github.com/kowainik/relude/issues/18): Add `LazyStrict` type class for conversions.-* `map` is not `fmap` anymore. Reexport `map` from `Data.List`-* [#12](https://github.com/kowainik/relude/issues/12):+- `map` is not `fmap` anymore. Reexport `map` from `Data.List`+- [#12](https://github.com/kowainik/relude/issues/12): Remove `liquid-haskell` support.-* [#20](https://github.com/kowainik/relude/issues/20):+- [#20](https://github.com/kowainik/relude/issues/20): Add `viaNonEmpty` function.-* [#21](https://github.com/kowainik/relude/issues/21):+- [#21](https://github.com/kowainik/relude/issues/21): Add `MonadFail` instance for `Either`.-* [#17](https://github.com/kowainik/relude/issues/17):+- [#17](https://github.com/kowainik/relude/issues/17): Add `foldMapA` and `foldMapM` functions.-* [#4](https://github.com/kowainik/relude/issues/4):+- [#4](https://github.com/kowainik/relude/issues/4): Rename package to `Relude`.-* [#14](https://github.com/kowainik/relude/issues/14):+- [#14](https://github.com/kowainik/relude/issues/14): Add `Relude.Extra.*` modules which are not exported by default but have useful functions.-* [#8](https://github.com/kowainik/relude/issues/8):+- [#8](https://github.com/kowainik/relude/issues/8): Introduce `StaticMap` and `DynamicMap` type classes as universal interface for Map-like structures.--`relude` uses [PVP Versioning][1].-The change log is available [on GitHub][2]. [1]: https://pvp.haskell.org [2]: https://github.com/kowainik/relude/releases
− CONTRIBUTING.md
@@ -1,50 +0,0 @@-# Contributing to `relude`--## :wave: Greetings Traveler!--We're glad you're reading this, we really appreciate the effort you're-putting in. Thank you for your help in making this library awesome! :sparkles:--### How to contribute--#### Report bugs or feature request--If you have found any bugs or have proposals on how to make this project better,-don't hesitate to create issues-[here](https://github.com/kowainik/relude/issues/new) in free format.--#### Create a PR--We love receiving pull requests from everyone. But, please, don't create a PR-without a corresponding issue. It's always better to discuss your future-work first. Even if such an issue exists it's still better to express your willing-to do that issue under comment section. Thus you will show that you're doing-that issue, and nobody else will accidentally do it in parallel with you. Furthermore you-also can discuss the best way to implement that issue!--To get started with this you should first fork, then clone the repo:-- git clone git@github.com:your-username/relude.git--Make your changes and consider the following check list to go through before submitting your pull request.--#### :white_check_mark: Check list--- [ ] Project compiles-- [ ] New/fixed features work as expected-- [ ] Old features do not break after the change-- [ ] `stylish-haskell` with config in this repo root was used to format code-- [ ] _Recommended:_ Commit messages are in the proper format. If the commit- addresses an issue start the first line of the commit with the issue number in- square parentheses.- + **_Example:_** `[#42] Short commit description`--If you fix bugs or add new features, please add tests.--After everything above is done, commit and push to your fork.-Now you are ready to [submit a pull request][pr]!-------------Thanks for spending time on reading this contributing guide! :sparkling_heart:--[pr]: https://github.com/kowainik/relude/compare/
LICENSE view
@@ -1,5 +1,5 @@ The MIT License (MIT)-Copyright (c) 2016-2017, Stephen Diehl, 2017, Serokell, 2018, Kowainik+Copyright (c) 2016, Stephen Diehl, 2016-2018, Serokell, 2018, Kowainik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to
README.md view
@@ -1,311 +1,817 @@-Relude-=========+# relude -[](https://travis-ci.org/kowainik/relude)-[](https://hackage.haskell.org/package/relude)+++[](https://github.com/kowainik/relude/actions)+[](https://hackage.haskell.org/package/relude) [](http://stackage.org/lts/package/relude) [](http://stackage.org/nightly/package/relude) [](https://opensource.org/licenses/MIT) -`relude` is a custom prelude based on `universum`. `relude` tries to achieve the following goals:+**`relude`** is a safe, performant, user-friendly and lightweight Haskell+standard library. -1. **Avoid all** [**partial functions**](https://www.reddit.com/r/haskell/comments/5n51u3/why_are_partial_functions_as_in_head_tail_bad/)- (like `head :: [a] -> a`). The types of partial functions lie about their- behavior and usage of such functions can lead to the unexpected bugs. Though- you can still use some unsafe functions from `Relude.Unsafe` module, but they- are not exported by default.-2. **Type-safety**. We like to make invalid states unrepresantable. And if it's- possible to express this concept through the types then we will do it.- - _Example:_- ```haskell- whenNotNull :: Applicative f => [a] -> (NonEmpty a -> f ()) -> f ()- ```- instead of- ```haskell- whenNotNull :: Applicative f => [a] -> ([a] -> f ()) -> f ()- ```-3. **Performance.** Prefer `Text` over [`String`](https://www.reddit.com/r/haskell/comments/29jw0s/whats_wrong_with_string/),- use spaceleak-free functions (like our custom `sum` and `product`), introduce- `{-# INLINE #-}` and `{-# SPECIALIZE #-}` pragmas where appropriate.-4. **Minimalism** (low number of dependencies). We don't force users of `relude` to- stick to some specific lens or text formatting or logging library.-5. **Convenience** (like lifted to `MonadIO` functions, more reexports). But we- want to bring common types and functions (like `containers` and `bytestrng`)- into scope because they are used in almost every application anyways.-6. **Provide excellent documentation.**- * Tutorial- * Migration guide from `Prelude`- * Haddock with examples for (almost) every function- (all examples are tested with [`doctest`](http://hackage.haskell.org/package/doctest))- * Documentation regarding [internal module structure]((http://hackage.haskell.org/package/relude/docs/Relude.html))- * `relude`-specific [HLint](http://hackage.haskell.org/package/hlint) rules:- [`.hlint.yaml`](.hlint.yaml)-7. **User-friendliness.** Ability to quickly migrate to `relude` if you're familiar- with the common libraries like `text` and `containers`.-8. **Exploration.** Experiment with new ideas and proposals without introducing- breaking changes.+The default `Prelude` is not perfect and doesn't always satisfy one's needs.+At this stage you may want to try an alternative prelude library. In this README+we are going to give you convincing reasons to consider using `relude` as such+alternative in your next project. -This README contains introduction to `Relude` and a tutorial on how to use it.+`relude` has some strong goals and principles that it sticks to. That principles+defines the library decisions and might tell you more about the priorities of+the library. So below you can find the key design principles behind `relude`: -Structure of this tutorial---------------------------+1. **Productivity.** You can be more productive with a "non-standard" standard+ library, and `relude` helps you with writing safer and more+ efficient code faster. +2. **Total programming**. Usage of+ [_partial functions_](https://www.reddit.com/r/haskell/comments/5n51u3/why_are_partial_functions_as_in_head_tail_bad/)+ can lead to unexpected bugs and runtime exceptions in pure+ code. The types of partial functions lie about their behaviour. And+ even if it is not always possible to rely only on total functions,+ `relude` strives to encourage best-practices and reduce the+ chances of introducing a bug.++| **Partial** | **Total** |+| ------------------------------- | ------------------------------------------ |+| `head :: [a] -> a` | `head :: NonEmpty a -> a` |+| `tail :: [a] -> [a]` | `tail :: NonEmpty a -> [a]` |+| `read :: Read a => String -> a` | `readMaybe :: Read a => String -> Maybe a` |+| `fromJust :: Maybe a -> a` | `fromMaybe :: a -> Maybe a -> a` |++3. **Type-safety**. We use the _"make invalid states unrepresentable"_ motto as one+ of our guiding principles. If it is possible, we express this concept through the+ types.++ _Example:_ Here the function's name type and actions are aligned with each other++ ```haskell+ whenNotNull :: Applicative f => [a] -> (NonEmpty a -> f ()) -> f ()+ ```++ While in here, the type could represent an unreachable state:++ ```haskell+ whenNotNull :: Applicative f => [a] -> ([a] -> f ()) -> f ()+ ```++4. **Performance.** We prefer `Text` over [`String`](https://www.reddit.com/r/haskell/comments/29jw0s/whats_wrong_with_string/),+ use space-leaks-free functions (e.g. our custom performant `sum` and `product`), introduce+ `{-# INLINE #-}` and `{-# SPECIALIZE #-}` pragmas where+ appropriate, and make efficient container types+ (e.g. `Map`, `HashMap`, `Set`) more accessible.++5. **Minimalism** (low number of dependencies). We do not force users of+ `relude` to stick to any specific lens or text formatting or logging+ library. Where possible, `relude` depends only on boot libraries.+ The [Dependency graph](https://raw.githubusercontent.com/kowainik/relude/main/relude-dependency-graph.png)+ of `relude` can give you a clearer picture.++6. **Convenience**. Despite minimalism, we want to bring commonly used+ types and functions into scope, and make available functions easier+ to use. Some examples of conveniences:++ - No need to add `containers`, `unordered-containers`, `text` and+ `bytestring` to dependencies in your `.cabal` file to use the+ main API of these libraries+ - No need to import types like `NonEmpty`, `Text`, `Set`, `Reader[T]`, `MVar`, `STM`+ - Functions like `liftIO`, `fromMaybe`, `sortWith` are available by default as well+ - `IO` actions are lifted to `MonadIO`++7. **Excellent documentation.**++ - Tutorial+ - Migration guide from `Prelude`+ - Haddock for every function with examples tested by+ [`doctest`](http://hackage.haskell.org/package/doctest)+ - Documentation on [internal module structure](http://hackage.haskell.org/package/relude/docs/Relude.html)+ - `relude`-specific [HLint](http://hackage.haskell.org/package/hlint) rules:+ [`.hlint.yaml`](https://github.com/kowainik/relude/blob/main/.hlint.yaml)++8. **User-friendliness.** Anyone should be able to quickly migrate to `relude`. Only+ some basic familiarity with the common libraries like `text` and `containers`+ should be enough (but not necessary).++9. **Exploration.** We have space to experiment with new ideas and proposals+ without introducing breaking changes. `relude` uses the approach with+ `Extra.*` modules which are not exported by default. The chosen approach makes it quite+ easy for us to provide new functionality without breaking anything and let+ the users decide to use it or not.++In addition to our key design principles, the following list of+**anti-goals** describes what `relude` is trying to avoid:++1. **Rewrite `base` from the ground up.** With `relude` you don't need+ to unlearn what you already knew, you can leverage existing+ knowledge to achieve higher productivity.+2. **Invent custom abstractions.** Learning abstractions is hard, so+ we do our best not to introduce new overwhelming concepts.+3. **Rename common definitions.** If something is called `foo` in+ `base`, it's also called `foo` in `relude`. So, `relude` doesn't+ rename any existing abstractions, but it may introduce a few new+ ones, if their benefits outweigh learning curve.++This README contains an introduction to `relude` and a tutorial on how to use it.++For a general introduction to alternative preludes, check the excellent+blog post by [Type Classes](https://typeclasses.com/) that highlights+`relude`.++- [No implicit Prelude](https://typeclasses.com/ghc/no-implicit-prelude)++For guiding development principles behind `relude` and comparison with+`base`, check out the following talk:++- [](https://www.youtube.com/watch?v=qwAmiJ5M_zM)++## Structure of this tutorial+ This tutorial has several parts: -1. [Motivation.](#motivation-)-2. [Get started.](#get-started-)-3. [Difference from `Prelude`.](#difference-from-prelude-)-4. [Reexports.](#reexports-)-5. [What's new?](#whats-new-)-6. [Migration guide.](#migration-guide-)+1. [When to use an alternative prelude?](#when-to-use-an-alternative-prelude)+2. [Get started](#get-started)+ - [Mixins](#mixins)+ - [NoImplicitPrelude](#noimplicitprelude)+ - [base-noprelude (not recommended)](#base-noprelude)+3. [Difference from Prelude](#difference-from-prelude)+4. [Reexports](#reexports)+5. [What's new?](#whats-new)+6. [Migration guide](#migration-guide)+7. [Comparison with other alternative preludes](#comparison-with-other-alternative-preludes)+ - [Relude vs Protolude](#relude-vs-protolude)+8. [For developers](#for-developers) -This is neither a tutorial on _Haskell_ nor tutorial on each function contained-in `Relude`. For detailed documentation of every function together with examples-and usage, see [_Haddock documentation_](http://hackage.haskell.org/package/relude).+This is neither a tutorial on _Haskell Standard Library_ nor a tutorial on each+function contained in `relude`. For latter see the detailed documentation of+every data type, type class and function together with examples and usages in+the [_Haddock documentation for `relude`_](http://hackage.haskell.org/package/relude). -Motivation [↑](#structure-of-this-tutorial)-------------------------------------------+## When to use an alternative prelude? -We decided to base `relude` on `universum` due to the following reasons:+[[Back to the Table of Contents] ↑](#structure-of-this-tutorial) -1. `universum` helps to achieve our goals more than any other custom prelude.-2. We worked on `universum` a lot (just check contributors statistics) and we- know its internal structure.+The module with the name `Prelude` is a module imported by default in every Haskell+source file of your project. If you want to use some data types or functions+which are not exposed by `Prelude`, you need to import them, adding necessary+libraries to your project dependencies. Unlike ordinary libraries, alternative+preludes provide a different set of available by default functions and data+types by replacing the `Prelude` module. -The motivation to create another alternative prelude instead of modifying-existing one is that it's hard to change preludes in any way. `relude`-uses approach with `Extra.*` modules which are not exported by default so it's-quite easy to bring something new (that satisfies `relude` goals) and let users-decide to use it or not.+Replacing default `Prelude` from `base` has the following _disadvantages_: -Unlike `universum`, we are:+1. Increased entry threshold: you need to _learn_ a different standard library.+ - `relude` tries to lower this threshold as much as possible: it comes with+ the excellent documentation, no custom abstractions, and behavior is+ changed only for a small subset of functions.+2. Extra dependencies: adding more libraries to dependencies increases build+ times and maintenance burden.+ - `relude` depends only on the boot libraries (almost) which results in small+ build time, follows [PVP](https://pvp.haskell.org/) and cares about+ backwards compatibility. -1. Not trying to replace `Foldable` with custom `Container` type class. We only- forbid `elem` and `notElem` functions for sets due to performance reasons.-2. Have less dependencies: no `vector`, no `microlens`, no `safe-exceptions`, no `type-operators`.-3. Have a lot of other different improvements.+However, using an alternative prelude, specifically `relude`, has the following+**advantages**: -Get started [↑](#structure-of-this-tutorial)---------------------------------------------+1. Increased code safety: no partial functions, no space-leak functions.+2. Increased productivity: no need to import common functions and data types,+ more common idioms are provided.+3. Increased performance: some functions in `relude` are faster than in the+ default `Prelude`. -If you want to start using `relude` in your project and explore it with the help-of compiler, set everything up according to the instructions below.+Taking into consideration all the above points, we put together our+recommendations when to use `relude`: -### `base-noprelude`+1. When you develop an application (e.g. CLI tool, web-app). In that case,+ greater productivity is more important than a low number of dependencies.+2. When writing a big framework. Some of them can be bigger than applications. -This is the recommended way to use custom prelude. It requires you to perform-the following steps:+And when you may want to stay with the default standard: -1. Replace `base` dependency with corresponding version of `base-noprelude` in- your `.cabal` file.-2. Add the following `Prelude` module to your project (both to filesystem and to `exposed-modules`):- ```haskell- module Prelude- ( module Relude- ) where+1. When you write a small library that is supposed to be used by other people in+ their projects. - import Relude- ```-3. Optionally modify your `Prelude` to include more or less functions. Probably- you want to hide something from `Relude` module. Or maybe you want to add- something from `Relude.Extra.*` modules!+## Get started -This is a very convenient way to add a custom prelude to your project because-you don't need to import module manually inside each file and enable the-`NoImplicitPrelude` extension.+[[Back to the Table of Contents] ↑](#structure-of-this-tutorial) -### Per-file configuration+If you want to start using `relude` in your project, you can set the library up+for you by one of the following ways. -Disable the built-in prelude at the top of your file:+### Mixins +[[Back to the Table of Contents] ↑](#structure-of-this-tutorial)++This is the recommended way to use a custom prelude.++You can use the Cabal feature `mixins` to replace the default `Prelude` with+`Relude` without the need to add extra dependencies or to import `Relude`+manually in each module.+For this you need to add the following lines into your `.cabal` file:+ ```haskell+ mixins: base hiding (Prelude)+ , relude (Relude as Prelude)+ , relude+```++> **NOTE:** this requires Cabal version to be at least `2.2`++The above syntax does the following:++1. Makes all modules of `base` available except `Prelude`.+2. Renames the `Relude` module in `relude` to `Prelude`.+3. Additionally allows importing all other modules from `relude`+ (extra modules and reexports from other libraries).++See the following complete example of how your `.cabal` file may look+like after the set up:++```cabal+cabal-version: 2.2+name: prelude-example+version: 0.0.0.0++library+ exposed-modules: Example+ build-depends: base >= 4.14 && < 4.17+ , relude ^>= 1.1.0.0++ mixins: base hiding (Prelude)+ , relude (Relude as Prelude)+ , relude++ default-language: Haskell2010+```++> **NOTE:** if you use [`summoner`](https://github.com/kowainik/summoner) to+> generate a Haskell project, the tool automatically creates the `mixins`+> field when you specify a custom prelude.++If you want to restrict allowed modules in `relude` to a specific list+(e.g. use only `Relude.Extra.Enum` or `Relude.Unsafe` or `Data.Text`+from `text`), you can alternatively list them explicitly under the+first `mixins` entry field as well, like this:++```cabal+ mixins: base hiding (Prelude)+ , relude (Relude as Prelude+ , Relude.Extra.Enum+ , Relude.Unsafe+ , Data.Text+ )+```++If you want to bring only all `Extra.*` modules into scope, you can add+a single `Relude.Extra` module to `mixins`, and after that you can import all+extra functions and data types from `Relude.Extra`. This is the+easiest way to bring all functions and types from `relude` to your project+(excluding `Relude.Unsafe`).++```cabal+ mixins: base hiding (Prelude)+ , relude (Relude as Prelude+ , Relude.Extra+ )+```++> **NOTE:** due to the existing [stack issue](https://github.com/commercialhaskell/stack/issues/5077),+> `mixins` technique doesn't work with the `stack repl` at the moment. Please,+> consider this before using this method of turning `relude` on in the project.+>+> If having `stack repl` crucial for your workflow, see the following options of+> how to use Relude in your project.++### NoImplicitPrelude++[[Back to the Table of Contents] ↑](#structure-of-this-tutorial)++For this option, you need to disable the default `Prelude` module first.+To disable the built-in prelude on module basis, you can add the following+pragma at the top of your file:++```haskell {-# LANGUAGE NoImplicitPrelude #-} ``` -Or directly in your project `.cabal` file, if you want to use in every module by default:+if you want to disable the default `Prelude` for every module by default, you+can specify this directly in your project `.cabal` file: ```haskell default-extensions: NoImplicitPrelude ``` -Then add the following import to your modules:+Then you need to add `relude` as a dependency of your project. +After doing all above, you can now use `Relude` in any module of your project by+adding a single import:+ ```haskell import Relude ``` -Difference from Prelude [↑](#structure-of-this-tutorial)---------------------------------------------------------+### base-noprelude -* `head`, `tail`, `last`, `init` work with `NonEmpty a` instead of `[a]`.-* `undefined` triggers a compiler warning, because you probably don't want to- leave `undefined` in your code. Either use `throwIO`, `Except`, `error` or- `bug`.-* Multiple sorting functions are available without imports:- + `sortBy :: (a -> a -> Ordering) -> [a] -> [a]`: sorts list using given custom comparator.- + `sortWith :: Ord b => (a -> b) -> [a] -> [a]`: sorts a list based on some property of its elements.- + `sortOn :: Ord b => (a -> b) -> [a] -> [a]`: just like `sortWith`, but more- time-efficient if function is calculated slowly (though less- space-efficient). So you should write `sortOn length` (would sort elements- by length) but `sortWith fst` (would sort list of pairs by first element).-* Functions `sum` and `product` are strict now, which makes them more efficient.-* If you try to do something like `putStrLn "hi"`, you'll get an error message if- `OverloadedStrings` is enabled – it happens because the compiler doesn't know what- type to infer for the string. Use `putTextLn` in this case.-* Since `show` doesn't come from `Show` anymore, you need to export `Show` from- `Text.Show` module if you want to implement `Show` instance manually.-* You can't call `elem` and `notElem` functions over `Set` and `HashSet`. These- functions are forbidden for these two types because of the performance reasons.-* `error` takes `Text`.-* `lookup` doesn't work on list of pairs.+[[Back to the Table of Contents] ↑](#structure-of-this-tutorial) -Reexports [↑](#structure-of-this-tutorial)-------------------------------------------+:warning: _This strategy is not reccommended. The package `base-noprelude` is currently not being maintained and its use is likely to cause issues with newer versions of ghc and libraries._ -### Commonly used libraries+Alternatively, you can use the `base-noprelude` trick to enable+alternative preludes. This approach can be helpful if you want to have+your own `Prelude` module with some custom functions, not provided by+`relude`. To use the trick, perform the following steps: -First of all, we reexport some generally useful modules: `Control.Applicative`,-`Data.Traversable`, `Data.Monoid`, `Control.DeepSeq`, `Data.List`, and lots of-others. Just remove unneeded imports after importing `Relude` (you can use-`.hlint.yaml` file for this).+1. Replace the `base` dependency with the corresponding version of `base-noprelude` in+ your `.cabal` file.+2. Add the `relude` dependency to your `.cabal` file.+3. Create the file called `Prelude.hs` in your source directory with+ the following content: -Then, some commonly used types: `Map/HashMap/IntMap`, `Set/HashSet/IntSet`,-`Seq`, `Text` and `ByteString` (as well as synonyms `LText` and `LByteString`-for lazy versions).+ ```haskell+ module Prelude+ ( module Relude+ ) where + import Relude+ ```++4. Add this module to `exposed-modules` in your `.cabal` file:+ ```cabal+ exposed-modules: Prelude+ ```+5. Optionally modify your `Prelude` module to include more or fewer functions.+ Potentially, you can hide something from the `Relude` module. Or maybe you+ want to add something from `Relude.Extra.*` modules!+ Customize the module for your needs.++This is a very convenient way to add a custom prelude to your project because+you don't need to import module manually inside each file and enable the+`NoImplicitPrelude` extension.++## Difference from Prelude++[[Back to the Table of Contents] ↑](#structure-of-this-tutorial)++Main differences from `Prelude` can be grouped into the following categories:++- Changed behavior of common functions+ - `head`, `tail`, `last`, `init` work with `NonEmpty a` instead of `[a]`.+ * `lines`, `unlines`, `words`, `unwords` work with `Text` instead of `String`.+ - `show` is polymorphic over the return type.+ - Functions `sum` and `product` are strict now, which makes them more efficient.+ - You can't call `elem` and `notElem` functions over `Set` and `HashSet`.+ These functions are forbidden for these two types due to performance reasons.+ - `error` takes `Text`.+ - `undefined` triggers a compiler warning, because you probably don't want to+ leave `undefined` in your code. Either use `throwIO`, `Except`, `error` or+ `bug`.+- Not reexported+ - `read`+ - `lookup` for lists+ - `log`+- Completely new functions are brought into scope+ - See the [What's new?](#whats-new) section for a detailed overview.+- New reexports+ - See the [Reexports](#reexports) section for a detailed overview.++## Reexports++[[Back to the Table of Contents] ↑](#structure-of-this-tutorial)++`relude` reexports some parts of the following libraries:++- [`base`](http://hackage.haskell.org/package/base)+- [`bytestring`](http://hackage.haskell.org/package/bytestring)+- [`containers`](http://hackage.haskell.org/package/containers)+- [`deepseq`](http://hackage.haskell.org/package/deepseq)+- [`ghc-prim`](http://hackage.haskell.org/package/ghc-prim)+- [`hashable`](http://hackage.haskell.org/package/hashable)+- [`mtl`](http://hackage.haskell.org/package/mtl)+- [`stm`](http://hackage.haskell.org/package/stm)+- [`text`](http://hackage.haskell.org/package/text)+- [`transformers`](http://hackage.haskell.org/package/transformers)+- [`unordered-containers`](http://hackage.haskell.org/package/unordered-containers)++If you want to clean up your imports after switching to `relude`, you can use+the `relude`-specific+[`.hlint.yaml`](https://github.com/kowainik/relude/blob/main/.hlint.yaml)+configuration for this task. With this config, `HLint` will produce+warnings and hints on how to have more benefits from `relude`.++### base++Multiple sorting functions are available for different use-cases:++- `sortBy :: (a -> a -> Ordering) -> [a] -> [a]`: sorts a list using given+ custom comparator.+- `sortWith :: Ord b => (a -> b) -> [a] -> [a]`: sorts a list based on some+ property of its elements.+- `sortOn :: Ord b => (a -> b) -> [a] -> [a]`: similar to `sortWith`, but more+ time-efficient if function is calculated slowly (though less+ space-efficient). So you should write `sortOn length` (would sort elements+ by length) but `sortWith fst` (would sort list of pairs by first element).++`readMaybe` and `readEither` are similar to `read` but unlike it, they are total+and return either `Maybe` or `Either` with a parse error.++`(&)` is the reverse application. The following three expressions are+semantically equivalent:++- `g (f x)`+- `g $ f $ x`+- `x & f & g`++Some generally useful modules from `base` package are exported, e.g.+`Control.Applicative`, `Data.Traversable`, `Data.Monoid`, `Data.List`,+and many more.+ `liftIO` and `MonadIO` are exported by default. A lot of `IO` functions are generalized to `MonadIO`. -`deepseq` is exported. For instance, if you want to force deep evaluation of-some value (in IO), you can write `evaluateNF a`. WHNF evaluation is possible-with `evaluateWHNF a`.+[`Bifunctor`](http://hackage.haskell.org/package/base/docs/Data-Bifunctor.html)+type class with useful instances is exported. -We also reexport big chunks of these libraries: `mtl`, `stm`.+- `first` and `second` functions apply a function to the first and+ second part of a `Bifunctor` (`fst` and `snd` for tuples, `Left` and+ `Right` for `Either`).+- `bimap` takes two functions and applies them to the first and second parts respectively. -[`Bifunctor`](http://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Bifunctor.html)-type class with useful instances is exported.+`trace`, `traceM`, `traceShow`, etc. are available by default. However, GHC will+warn you if you accidentally leave them in code. Same goes for the `undefined`+function. -* `first` and `second` functions apply a function to first/second part of a tuple (for tuples).-* `bimap` takes two functions and applies them to first and second parts respectively.+We also have `data Undefined = Undefined` (which also comes with the warning). -### Text+`relude` reexports `Exception` type from the `base` package and introduces the+`bug` function as an alternative to `error`. There is also a very convenient+`Exc` pattern-synonym to handle exceptions of different types. -We export `Text` and `LText`, and some functions work with `Text` instead of `String` –-specifically, IO functions (`readFile`, `putStrLn`, etc) and `show`. In fact, `show`-is polymorphic and can produce strict or lazy `Text`, `String`, or `ByteString`.-Also, `toText/toLText/toString` can convert `Text|LText|String` types to-`Text/LText/String`. If you want to convert to and from `ByteString` use-`encodeUtf8/decodeUtf8` functions.+See+[`Relude.Exception`](http://hackage.haskell.org/package/relude/docs/Relude-Exception.html)+module for details on exceptions. -### Debugging and `undefined`s+### containers & unordered-containers -`trace`, `traceM`, `traceShow`, etc. are available by default. GHC will warn you-if you accidentally leave them in code, however (same for `undefined`).+The following types from these two packages are exported: -We also have `data Undefined = Undefined` (which, too, comes with warnings).+- Maps: strict versions of `Map`, `HashMap`, `IntMap`.+- Sets: `Set`, `HashSet`, `IntSet`.+- Sequences: `Seq`. -### Exceptions+### text & bytestring -TODO: write about reexports, `Bug` and `Exc` pattern.+`relude` exports `Text` and `ByteString` (as well as their lazy versions —+`LText` and `LByteString`). -What's new? [↑](#structure-of-this-tutorial)---------------------------------------------+Also, some functions now work+with `Text` instead of `String` – `words`, `lines`, etc. -Finally, we can move to part describing the new cool features we bring with `relude`.+In addition, `relude` provides specialised versions of the `IO` functions to+work with `Text` and `ByteString` — `readFileText`, `writeFileBS`, etc. -* Safe analogue for `head` function: `safeHead :: [a] -> Maybe a` or you can- use our `viaNonEmpty` function to get `Maybe a`: `viaNonEmpty head :: [a] -> Maybe a`.-* `uncons` splits a list at the first element.-* `ordNub` and `sortNub` are _O(n log n)_ versions of `nub` (which is quadratic)- and `hashNub` and `unstableNub` are almost _O(n)_ versions of `nub`.-* `(&)` – reverse application. `x & f & g` instead of `g $ f $ x` is useful sometimes.-* `whenM`, `unlessM`, `ifM`, `guardM` are available and do what you expect- them to do (e.g. `whenM (doesFileExist "foo")`).-* General fold functions:+`show` is polymorphic and can produce strict or lazy `Text` or+`ByteString` as well as `String`.++Also, `toText|toLText|toString` can convert `Text|LText|String` types to+`Text|LText|String`. If you want to convert to and from `ByteString` use+`encodeUtf8|decodeUtf8` functions.++### transformers & mtl++The following parts of these two libraries are exported:++- Transformers: `State[T]`, `Reader[T]`, `ExceptT`, `MaybeT`.+- Classes: `MonadReader`, `MonadState`.++### Deepseq++All the main parts of the `deepseq` library are exported.+For instance, if you want to force the deep evaluation of+some value (in `IO`), you can write `evaluateNF a`.+Additionally, the WHNF evaluation is possible+with provided `evaluateWHNF`.++## What's new?++[[Back to the Table of Contents] ↑](#structure-of-this-tutorial)++Finally, let's move to part describing the new cool features we bring with+`relude`.++### Available by default++- Safe analogue for the list functions:+ ```haskell+ head :: NonEmpty a -> a -- the first element of the list+ tail :: NonEmpty a -> [a] -- the list without the first element+ last :: NonEmpty a -> a -- the last element of the list+ init :: NonEmpty a -> [a] -- the list without the last element+ ```++ You can also still work with lists for these functions. Using `viaNonEmpty`+ function you will get `Maybe a` from the list:++ ```haskell+ -- viaNonEmpty head :: [a] -> Maybe a+ ghci> viaNonEmpty head [1,2,3]+ Just 1+ ghci> viaNonEmpty head []+ Nothing+ ```++- `uncons` splits a list at the first element.+- `ordNub` and `sortNub` are _O(n log n)_ versions of `nub` (which is quadratic),+ also, `hashNub` and `unstableNub` are almost _O(n)_ versions of `nub`,+ and `intNub` for fast `Int`s nub.+- `whenM`, `unlessM`, `ifM`, `guardM` — monadic guard combinators, that work+ with any `Monad`, e.g. `whenM (doesFileExist "foo")`.+- General fold functions:+ ```haskell foldMapA :: (Monoid b, Applicative m, Foldable f) => (a -> m b) -> f a -> m b foldMapM :: (Monoid b, Monad m, Foldable f) => (a -> m b) -> f a -> m b ```-* `readMaybe` and `readEither` are like `read` but total and give either- `Maybe` or `Either` with parse error.-* `when(Just|Nothing|Left|Right|NotEmpty)[M][_]`- let you conditionally execute something. Before:+- `when(Just|Nothing|Left|Right|NotEmpty)[M][_]` functions that+ let you conditionally execute something. + **Before:**+ ```haskell case mbX of Nothing -> return () Just x -> f x ``` - After:+ **After:** ```haskell whenJust mbX $ \x -> f x ``` -* `for_` for loops. There's also `forM_` but `for_` looks a bit nicer.+- `for_` and `forM_` for loops. ```haskell- for_ [1..10] $ \i -> do+ for_ files $ \file -> do ... ``` -* `andM`, `allM`, `anyM`, `orM` are monadic version of corresponding functions from `base`.+- `andM`, `allM`, `anyM`, `orM` are monadic versions of the corresponding+ functions from `base`.+- Conversions between `Either` and `Maybe`, e.g. `rightToMaybe` and `maybeToLeft`+ with clear semantics.+- `using(Reader|State)[T]` functions as aliases for `flip run(Reader|State)[T]`.+- [`One` type class](http://hackage.haskell.org/package/relude/docs/Relude-Container-One.html)+ for creating singleton containers (even monomorphic ones like `Text`).+- `evaluateWHNF` and `evaluateNF` functions as clearer and lifted aliases for+ `evaluate` and `evaluate . force`.+- `MonadFail` instance for `Either`. -* Conversions between `Either` and `Maybe` like `rightToMaybe` and `maybeToLeft`- with clear semantic.-* `using(Reader|State)[T]` functions as aliases for `flip run(Reader|State)[T]`.-* [`One` type class](src/Relude/Container/One.hs)- for creating singleton containers. Even monomorhpic ones like `Text`.-* [`StaticMap` and `DynamicMap`type classes](src/Relude/Extra/Map.hs) as a+### Extra bonuses++`relude` has a number of `Extra` modules that are not exposed by default (they+are not a part of the `Relude` module). You need to import such modules+separately.++These extra modules include the following functionality:++- Convenient functions to work with `(Bounded a, Enum a)` types:++ 1. `universe :: (Bounded a, Enum a) => [a]`: get all values of the type.++ ```haskell+ ghci> universe @Bool+ [True,False]+ ```++ 2. `inverseMap :: (Bounded a, Enum a, Ord k) => (a -> k) -> k -> Maybe a`:+ convert functions like `show` to parsers:++ ```haskell+ readMyEnums :: Text -> Maybe MyEnum+ readMyEnums = inverseMap myShow+ ```++- Nice helpers to deal with `newtype`s in a more pleasant way:++ ```haskell+ ghci> newtype Foo = Foo Bool deriving Show+ ghci> under not (Foo True)+ Foo False+ ```++- Functions to operate with `CallStack`:++ ```haskell+ ghci> foo :: HasCallStack => String; foo = ownName+ ghci> foo+ "foo"+ ```++- `Foldable1` typeclass that contains generalized interface for folding+ non-empty structures like `NonEmpty`.+- [`StaticMap` and `DynamicMap` type classes](https://github.com/kowainik/relude/blob/main/src/Relude/Extra/Map.hs) as a general interface for `Map`-like data structures.-* `evaluateWHNF` and `evaluateNF` functions as clearer and lifted aliases for- `evaluate` and `evaluate . force`.-* `MonadFail` instance for `Either`.+- And much more! -Migration guide [↑](#structure-of-this-tutorial)-------------------------------------------------+Explore `Extra` modules: [`Relude.Extra`](http://hackage.haskell.org/package/relude/docs/Relude-Extra.html) -In order to replace default `Prelude` with `relude` you should start with instructions given in-[_get started_](#get-started-) section.+## Migration guide +[[Back to the Table of Contents] ↑](#structure-of-this-tutorial)++This section of the guide helps to migrate your project from `base`+to the `relude` library.++In order to replace the default `Prelude` with `relude` you should start with+instructions provided in the [_Get Started_](#get-started) section.++### Code changes+ This section describes what you need to change to make your code compile with `relude`. 1. Enable `-XOverloadedStrings` extension by default for your project. 2. Since `head`, `tail`, `last` and `init` work for `NonEmpty` you should- refactor your code in one of the multiple ways described below:+ refactor your code in one of the described below ways: 1. Change `[a]` to `NonEmpty a` where it makes sense. 2. Use functions which return `Maybe`. There is the `viaNonEmpty` function for this. And you can use it like `viaNonEmpty last l`.- + `viaNonEmpty head l` is `safeHead l`- + `tail` is `drop 1`. It's almost never a good idea to use `tail` from `Prelude`.- 3. Add `import qualified Relude.Unsafe as Unsafe` and replace function with qualified usage.-3. If you use `fromJust` or `!!` you should use them from `import qualified Relude.Unsafe as Unsafe`.+ - `tail` is `drop 1`. It's almost never a good idea to use `tail` from `Prelude`.+ 3. Add `import qualified Relude.Unsafe as Unsafe` and replace the function+ with its qualified usage: `Unsafe.head`.+3. If you use `fromJust` or `!!` you should import them from `import qualified Relude.Unsafe as Unsafe`. 4. If you use `foldr` or `forM_` or similar for something like `Maybe a` or- `Either a b` it's recommended to replace usages of such function with+ `Either a b` it is recommended to replace usages of such functions with the monomorhpic alternatives:- * `Maybe`- + `(?:) :: Maybe a -> a -> a`- + `fromMaybe :: a -> Maybe a -> a`- + `maybeToList :: Maybe a -> [a]`- + `maybeToMonoid :: Monoid m => Maybe m -> m`- + `maybe :: b -> (a -> b) -> Maybe a -> b`- + `whenJust :: Applicative f => Maybe a -> (a -> f ()) -> f ()`- + `whenJustM :: Monad m => m (Maybe a) -> (a -> m ()) -> m ()` - * `Either`- + `fromLeft :: a -> Either a b -> a`- + `fromRight :: b -> Either a b -> b`- + `either :: (a -> c) -> (b -> c) -> Either a b -> c`- + `whenRight_ :: Applicative f => Either l r -> (r -> f ()) -> f ()`- + `whenRightM_ :: Monad m => m (Either l r) -> (r -> m ()) -> m ()`+ - `Maybe` -5. Forget about `String` type.- + Replace `putStr` and `putStrLn` with `putText` and `putTextLn`.- + Replace `(++)` with `(<>)` for `String`-like types.- + Try to use [`fmt`](http://hackage.haskell.org/package/fmt) library if you need to construct messages.- + Use `toText/toLText/toString` functions to convert to `Text/LazyText/String` types.- + Use `encodeUtf8/decodeUtf8` to convert to/from `ByteString`.-6. Run `hlint` using `.hlint.yaml` file from `relude` package to cleanup code and imports.+ - `(?:) :: Maybe a -> a -> a`+ - `fromMaybe :: a -> Maybe a -> a`+ - `maybeToList :: Maybe a -> [a]`+ - `maybeToMonoid :: Monoid m => Maybe m -> m`+ - `maybe :: b -> (a -> b) -> Maybe a -> b`+ - `whenJust :: Applicative f => Maybe a -> (a -> f ()) -> f ()`+ - `whenJustM :: Monad m => m (Maybe a) -> (a -> m ()) -> m ()`++ - `Either`+ - `fromLeft :: a -> Either a b -> a`+ - `fromRight :: b -> Either a b -> b`+ - `either :: (a -> c) -> (b -> c) -> Either a b -> c`+ - `whenRight_ :: Applicative f => Either l r -> (r -> f ()) -> f ()`+ - `whenRightM_ :: Monad m => m (Either l r) -> (r -> m ()) -> m ()`++5. Replace the `String` type with more efficient and suitable ones (e.g. `Text`):++ - Replace `(++)` with `(<>)` for `String`-like types.+ - Use `toText/toLText/toString` functions to convert to `Text/LazyText/String` types.+ - Use `encodeUtf8/decodeUtf8` to convert to/from `ByteString`.+ - Use `(putStr[Ln]|readFile|writeFile|appendFile)[Text|LText|BS|LBS]` functions.++6. Since `show` doesn't come from `Show` anymore, you need to import the+ `Text.Show` module if you want to implement `Show` instance manually. This can be done in the following way:++```haskell+import qualified Text.Show++data MyType = ...++instance Show MyType where+ show :: MyType -> String+ show = ...+```++7. Run `hlint` using `.hlint.yaml` file from `relude` package to cleanup code and imports.++### Running HLint on CI++Instead of storing a `relude`-specific `.hlint.yaml` file inside your repository,+you can run HLint with this file automatically on any CI service such as+[Travis CI](https://travis-ci.org/) or [Circle CI](https://circleci.com/).+For this you need to:++1. Find the commit hash of the `relude` version you are using (can be found in [releases](https://github.com/kowainik/relude/releases)).+2. Run the command that downloads `.hlint.yaml` for that version.+3. Run `hlint` using this file.++For the latest `relude` version, this can be achieved by executing the following+two commands on your CI:++```yaml+curl https://raw.githubusercontent.com/kowainik/relude/v1.1.0.0/.hlint.yaml -o .hlint-relude.yaml+curl -sSL https://raw.github.com/ndmitchell/neil/master/misc/travis.sh | sh -s -- hlint -h .hlint-relude.yaml .+```++See an example of this feature described in the following blog post+about Travis CI settings:++- [Kodimensional: Dead simple Haskell Travis settings for cabal and stack](https://kodimensional.dev/posts/2019-02-25-haskell-travis#customization-hlint)++## Comparison with other alternative preludes++[[Back to the Table of Contents] ↑](#structure-of-this-tutorial)++There are quite a few libraries that can be used as alternative preludes in+Haskell, let's compare Relude with some of them.++### Relude vs Protolude++[[Back to the Table of Contents] ↑](#structure-of-this-tutorial)++[Protolude](https://github.com/sdiehl/protolude) is one of the most popular+alternative preludes. It's also relatively small, but:++1. `relude` has custom HLint rules specific to it: you can use them to remove+ redundant imports or find hints on how to use functions from `relude`. Moreover,+ the HLint rules are generated using Dhall and there is [a blog post about+ this technique](https://kowainik.github.io/posts/2018-09-09-dhall-to-hlint).+ This allows to maintain HLint rules with less effort, even though it is+ already not an easy task.+2. One significant difference: `head` in `protolude` returns `Maybe a` while in+ `relude` it works with `NonEmpty`.+3. `relude` uses type-level features to provide better error messages+ on the difference from `Prelude`, and also to forbid `elem` and `notElem`+ functions for `Set` and `HashSet` (because `elem` from `Foldable` runs in+ _O(n)_ time and you can accidentally use `elem` from `Foldable` but with+ `relude` you can't).+4. Protolude supports older GHC versions (from GHC 7.6.1) while `relude` only+ supports from GHC 8.4. So if you aim ancient GHC versions, `protolude`+ might be a better choice. But because of that it contains a lot of CPP, code+ is scary in some places as a consequence and it is more difficult to add,+ remove or change things there.+5. `relude` has much better documentation:+ - [High-level overview of internal module structure](http://hackage.haskell.org/package/relude/docs/Relude.html)+ - 100% Haddock coverage+ - Every function has usage examples and all examples are tested with+ `doctest` (which also sometimes hard to do due to the multiple GHC+ versions support, but we try really hard)+ - [Tutorial + migration guide](#structure-of-this-tutorial) from+ `Prelude` and just general description of the whole package and libraries+ it depends on.+6. `relude` has fewer dependencies and is slightly lighter because of that but still+ is very powerful and useful.+7. `relude` is opt-in oriented and has a notion of `Extra.*` modules that are+ not exported by default from the `Relude` module. That means that we do not+ spoil the global namespace but still have a lot of useful features, like+ polymorphic functions to work with every `newtype`, `Enum/Bounded`-related+ useful utilities, functions to take a name of any type as `Text` and much+ more. It is a straightforward process to make them accessible package-wide+ with the `base-noprelude` trick!++## For Developers++[[Back to the Table of Contents] ↑](#structure-of-this-tutorial)++### Generating .hlint.yaml++Note, that we are using custom `hlint` setting which are `Relude` specific. To+keep it up to date don't forget to reflect your changes in this file. We are+using `Dhall` to maintain the configurations (lang version 22.0.0). To+use it follow the steps below.++First time:++```shell+$ cabal v2-install dhall-yaml-1.2.10+```++To generate `hlint` file:++```shell+$ dhall-to-yaml-ng <<< './hlint/hlint.dhall' > .hlint.yaml+```++Check that you have generated valid `.hlint.yaml` file without parse errors:++```shell+$ hlint test/Spec.hs+```++See our blog post where we describe the details of the implementation for this solution:++- [Dhall To HLint](https://kowainik.github.io/posts/2018-09-09-dhall-to-hlint)++### Producing dependency graph++Install `cabal-plan` first:++```shell+$ cabal v2-install cabal-plan+$ cabal-plan --version+cabal-plan 0.6.2.0+```++Then draw the graph only for the library dependencies:++```shell+cabal-plan dot --root lib:relude | dot -Tpng -o relude-dependency-graph.png+```++### Updating license headers++Install the `headroom` tool and run it from this repository:++```shell+cabal v2-install headroom-0.4.3.0+headroom run+rg "SPDX-License-Identifier : MPL-2.0" --files-with-matches src/ | xargs sed -i 's/SPDX-License-Identifier : MPL-2.0/SPDX-License-Identifier : MIT/g'+```
benchmark/Main.hs view
@@ -1,17 +1,11 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE CPP #-} -module Main where+module Main (main) where import Relude hiding (show) import Data.List (nub)-import Gauge (Benchmark, bench, bgroup, nf)-import Gauge.Main (defaultMain)+import Test.Tasty.Bench (Benchmark, bench, bgroup, defaultMain, nf) import Prelude (show) import qualified Data.HashSet as HashSet (insert)@@ -24,6 +18,12 @@ , bgroupList listOfBig "big" , bgroupList (nStrings 'z') "small str" , bgroupList (nStrings 'c') "big str"++#if __GLASGOW_HASKELL__ > 804+ , bgroupIntList listOfSmall "small ints"+ , bgroupIntList listOfBig "big ints"+#endif+ , bgroupFold ] @@ -71,6 +71,33 @@ safeSort :: [a] -> [a] safeSort = map NonEmpty.head . NonEmpty.group . sort +#if __GLASGOW_HASKELL__ > 804+bgroupIntList+ :: (Int -> [Int])+ -> String+ -> Benchmark+bgroupIntList f name = bgroup name $ map ($ f)+ [ bgroupNub 100+ , bgroupNub 500+ , bgroupNub 1000+ , bgroupNub 5000+ , bgroupNub 500000+ , bgroupNub 1000000+ ]+ where+ bgroupNub :: Int -> (Int -> [Int]) -> Benchmark+ bgroupNub n listOf = bgroup (show n) nubBenchs+ where+ listN :: [Int]+ listN = listOf n++ nubBenchs :: [Benchmark]+ nubBenchs =+ [ bench "ordNub" $ nf ordNub listN+ , bench "intNub" $ nf intNub listN+ ]+#endif+ listOfSmall :: Int -> [Int] listOfSmall n = let part = n `div` 100 in concat $ replicate part [1..100] @@ -88,7 +115,8 @@ bgroupFold = do let testList = [1..100000] :: [Int] let flipFoldl' = flipfoldl' HashSet.insert mempty- let ghcFoldl' = foldl' (\hashSet element -> HashSet.insert element hashSet) mempty- bgroup "foldl'" [ bench "flipped" $ nf flipFoldl' testList- , bench "base" $ nf ghcFoldl' testList- ]+ let ghcFoldl' = foldl' (\hash el -> HashSet.insert el hash) mempty+ bgroup "foldl'"+ [ bench "flipped" $ nf flipFoldl' testList+ , bench "base" $ nf ghcFoldl' testList+ ]
relude.cabal view
@@ -1,71 +1,151 @@+cabal-version: 3.0 name: relude-version: 0.1.1-synopsis: Custom prelude from Kowainik+version: 1.2.2.2+synopsis: Safe, performant, user-friendly and lightweight Haskell Standard Library description:- == Goals- .- * __Avoid all [partial functions](https://www.reddit.com/r/haskell/comments/5n51u3/why_are_partial_functions_as_in_head_tail_bad/)__- (like @head :: [a] -> a@). The types of partial functions lie about their- behavior and usage of such functions can lead to the unexpected bugs. Though- you can still use some unsafe functions from @Relude.Unsafe@ module, but they- are not exported by default.- .- * __Type-safety__. We like to make invalid states unrepresantable. And if it's- possible to express this concept through the types then we will do it.- /Example:/ @ whenNotNull :: Applicative f => [a] -> (NonEmpty a -> f ()) -> f () @- .- * __Performance.__ Prefer @Text@ over @[String](https://www.reddit.com/r/haskell/comments/29jw0s/whats_wrong_with_string/)@,- use spaceleak-free functions (like our custom @sum@ and @product@).- .- * __Minimalism__ (low number of dependencies). We don't force users of @relude@ to- stick to some specific lens or text formatting or logging library.- .- * __Convenience__ (like lifted to @MonadIO@ functions, more reexports). But we- want to bring common types and functions (like @containers@ and @bytestrng@)- into scope because they are used in almost every application anyways.- .- * __Provide excellent documentation.__- .- 1. Tutorial- .- 2. Migration guide from @Prelude@- .- 3. Haddock with examples for (almost) every function- (all examples are tested with [`doctest`](http://hackage.haskell.org/package/doctest))- .- 4. Documentation regarding [internal module structure]((http://hackage.haskell.org/package/relude/docs/Relude.html))- .- 5. @relude@-specific [HLint](http://hackage.haskell.org/package/hlint) rules: @[.hlint.yaml](https://github.com/kowainik/relude/blob/master/.hlint.yaml)@- .- * __User-friendliness.__ Ability to quickly migrate to @relude@ if you're familiar- with the common libraries like @text@ and @containers@.- .- * __Exploration.__ Experiment with new ideas and proposals without introducing- breaking changes.+ @__relude__@ is an alternative prelude library. If you find the default+ @Prelude@ unsatisfying, despite its advantages, consider using @relude@+ instead. + == Relude goals and design principles+ * __Productivity.__ You can be more productive with a "non-standard" standard+ library, and @relude@ helps you with writing safer and more+ efficient code faster.++ * __Total programming__. Usage of [/partial functions/](https://www.reddit.com/r/haskell/comments/5n51u3/why_are_partial_functions_as_in_head_tail_bad/)+ can lead to unexpected bugs and runtime exceptions in pure+ code. The types of partial functions lie about their behaviour. And+ even if it is not always possible to rely only on total functions,+ @relude@ strives to encourage best-practices and reduce the+ chances of introducing a bug.++ +---------------------------------+--------------------------------------------++ | __Partial__ | __Total__ |+ +=================================+============================================++ | @head :: [a] -> a@ | @head :: NonEmpty a -> a@ |+ +---------------------------------+--------------------------------------------++ | @tail :: [a] -> [a]@ | @tail :: NonEmpty a -> [a]@ |+ +---------------------------------+--------------------------------------------++ | @read :: Read a => String -> a@ | @readMaybe :: Read a => String -> Maybe a@ |+ +---------------------------------+--------------------------------------------++ | @fromJust :: Maybe a -> a@ | @fromMaybe :: a -> Maybe a -> a@ |+ +---------------------------------+--------------------------------------------+++ * __Type-safety__. We use the /"make invalid states unrepresentable"/ motto as one+ of our guiding principles. If it is possible, we express this concept through the+ types.++ /Example:/ @ whenNotNull :: Applicative f => [a] -> (NonEmpty a -> f ()) -> f () @++ * __Performance.__ We prefer @Text@ over @[String](https://www.reddit.com/r/haskell/comments/29jw0s/whats_wrong_with_string/)@,+ use space-leaks-free functions (e.g. our custom performant @sum@ and @product@),+ introduce @\{\-\# INLINE \#\-\}@ and @\{\-\# SPECIALIZE \#\-\}@ pragmas where+ appropriate, and make efficient container types+ (e.g. @Map@, @HashMap@, @Set@) more accessible.++ * __Minimalism__ (low number of dependencies). We do not force users of+ @relude@ to stick to any specific lens or text formatting or logging+ library. Where possible, @relude@ depends only on boot libraries.+ The [Dependency graph](https://raw.githubusercontent.com/kowainik/relude/main/relude-dependency-graph.png)+ of @relude@ can give you a clearer picture.++ * __Convenience__. Despite minimalism, we want to bring commonly used+ types and functions into scope, and make available functions easier+ to use. Some examples of conveniences:++ 1. No need to add @containers@, @unordered-containers@, @text@+ and @bytestring@ to dependencies in your @.cabal@ file to+ use the main API of these libraries+ 2. No need to import types like @NonEmpty@, @Text@, @Set@, @Reader[T]@, @MVar@, @STM@+ 3. Functions like @liftIO@, @fromMaybe@, @sortWith@ are available by default as well+ 4. @IO@ actions are lifted to @MonadIO@++ * __Excellent documentation.__++ 1. Tutorial+ 2. Migration guide from @Prelude@+ 3. Haddock for every function with examples tested by+ [doctest](http://hackage.haskell.org/package/doctest).+ 4. Documentation regarding [internal module structure](http://hackage.haskell.org/package/relude/docs/Relude.html)+ 5. @relude@-specific [HLint](http://hackage.haskell.org/package/hlint) rules: @[.hlint.yaml](https://github.com/kowainik/relude/blob/main/.hlint.yaml)@++ * __User-friendliness.__ Anyone should be able to quickly migrate to @relude@. Only+ some basic familiarity with the common libraries like @text@ and @containers@+ should be enough (but not necessary).++ * __Exploration.__ We have space to experiment with new ideas and proposals+ without introducing breaking changes. @relude@ uses the approach with+ @Extra.*@ modules which are not exported by default. The chosen approach makes it quite+ easy for us to provide new functionality without breaking anything and let+ the users decide to use it or not.+ homepage: https://github.com/kowainik/relude bug-reports: https://github.com/kowainik/relude/issues license: MIT license-file: LICENSE-author: Stephen Diehl, @serokell, Kowainik+author: Dmitrii Kovanikov, Veronika Romashkina, Stephen Diehl, Serokell maintainer: Kowainik <xrom.xkov@gmail.com>-copyright: 2016 Stephen Diehl, 2016-2018 Serokell, 2018 Kowainik+copyright: 2016 Stephen Diehl, 2016-2018 Serokell, 2018-2023 Kowainik category: Prelude stability: stable build-type: Simple-cabal-version: 1.24-tested-with: GHC == 8.0.2- , GHC == 8.2.2- , GHC == 8.4.3 extra-doc-files: CHANGELOG.md- , CONTRIBUTING.md- , README.md+ README.md+tested-with: GHC == 8.4.4+ GHC == 8.6.5+ GHC == 8.8.4+ GHC == 8.10.7+ GHC == 9.0.2+ GHC == 9.2.8+ GHC == 9.4.7+ GHC == 9.6.6+ GHC == 9.8.2+ GHC == 9.10.1 + source-repository head type: git location: git@github.com:kowainik/relude.git +common common-options+ ghc-options: -Wall+ -Wcompat+ -Widentities+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ -fwarn-implicit-prelude+ -Wredundant-constraints+ -fhide-source-paths+ if impl(ghc >= 8.4)+ ghc-options: -Wmissing-export-lists+ -Wpartial-fields+ if impl(ghc >= 8.8)+ ghc-options: -Wmissing-deriving-strategies+ if impl(ghc >= 8.10)+ ghc-options: -Wunused-packages+ if impl(ghc >= 9.0)+ ghc-options: -Winvalid-haddock+ if impl(ghc >= 9.2)+ ghc-options: -Wredundant-bang-patterns+ -Woperator-whitespace+ if impl(ghc >= 9.4 && < 9.10)+ ghc-options: -Wforall-identifier+ if impl(ghc >= 9.4)+ ghc-options: -Wredundant-strictness-flags+ if impl(ghc >= 9.8)+ ghc-options: -Wterm-variable-capture+ -Winconsistent-flags+++ default-language: Haskell2010+ default-extensions: InstanceSigs+ NoImplicitPrelude+ OverloadedStrings+ ScopedTypeVariables+ TypeApplications+ library+ import: common-options hs-source-dirs: src exposed-modules: Relude@@ -79,7 +159,9 @@ Relude.Container.Reexport Relude.Debug Relude.DeepSeq+ Relude.Enum Relude.Exception+ Relude.File Relude.Foldable Relude.Foldable.Fold Relude.Foldable.Reexport@@ -92,9 +174,12 @@ Relude.Lifted.Exit Relude.Lifted.File Relude.Lifted.IORef+ Relude.Lifted.Terminal+ Relude.Lifted.Handle+ Relude.Lifted.Env Relude.List+ Relude.List.NonEmpty Relude.List.Reexport- Relude.List.Safe Relude.Monad Relude.Monad.Either Relude.Monad.Maybe@@ -102,83 +187,106 @@ Relude.Monad.Trans Relude.Monoid Relude.Nub+ Relude.Numeric Relude.Print Relude.String Relude.String.Conversion Relude.String.Reexport -- not exported by default- Relude.Extra.Bifunctor- Relude.Extra.Enum- Relude.Extra.Group- Relude.Extra.Map+ Relude.Extra+ Relude.Extra.Bifunctor+ Relude.Extra.CallStack+ Relude.Extra.Enum+ Relude.Extra.Foldable+ Relude.Extra.Foldable1+ Relude.Extra.Group+ Relude.Extra.Lens+ Relude.Extra.Map+ Relude.Extra.Newtype+ Relude.Extra.Tuple+ Relude.Extra.Type Relude.Unsafe + reexported-modules:+ -- containers+ , Data.IntMap.Lazy+ , Data.IntMap.Strict+ , Data.IntSet+ , Data.Map.Lazy+ , Data.Map.Strict+ , Data.Set+ , Data.Sequence+ , Data.Tree+ -- unordered-containers+ , Data.HashMap.Lazy+ , Data.HashMap.Strict+ , Data.HashSet+ -- text+ , Data.Text+ , Data.Text.IO+ , Data.Text.Lazy+ , Data.Text.Lazy.IO+ , Data.Text.Read+ -- bytestring+ , Data.ByteString+ , Data.ByteString.Builder+ , Data.ByteString.Lazy+ , Data.ByteString.Short - ghc-options: -Wall -fwarn-implicit-prelude - build-depends: base >= 4.8 && < 5- , bytestring- , containers- , deepseq- , ghc-prim >= 0.4.0.0- , hashable- , mtl- , stm- , text- , transformers- , unordered-containers- , utf8-string+ build-depends: base >= 4.11 && < 4.22+ , bytestring >= 0.10 && < 0.13+ , containers >= 0.5.10 && < 0.9+ , deepseq >= 1.4 && < 1.6+ , ghc-prim >= 0.5.0.0 && < 0.14+ , hashable >= 1.2 && < 1.6+ , mtl >= 2.2 && < 2.4+ , stm >= 2.4 && < 2.6+ , text >= 1.2 && < 2.2+ , transformers >= 0.5 && < 0.7+ , unordered-containers >= 0.2.7 && < 0.3 - default-language: Haskell2010- default-extensions: NoImplicitPrelude- OverloadedStrings test-suite relude-test+ import: common-options type: exitcode-stdio-1.0 hs-source-dirs: test main-is: Spec.hs - other-modules: Test.Relude.Property-- build-depends: base >= 4.8 && < 5+ other-modules: Test.Relude.Gen+ Test.Relude.Container.One+ Test.Relude.Property+ build-depends: base , relude , bytestring+ , containers , text- , utf8-string- , hedgehog- , tasty- , tasty-hedgehog+ , hedgehog >= 1.0 && < 1.6 - ghc-options: -Wall -threaded- default-language: Haskell2010- default-extensions: NoImplicitPrelude+ ghc-options: -threaded test-suite relude-doctest+ import: common-options type: exitcode-stdio-1.0 hs-source-dirs: test main-is: Doctest.hs - build-depends: base >= 4.8 && < 5- , doctest+ build-depends: relude+ , doctest >= 0.20 && < 0.25 , Glob ghc-options: -threaded- default-language: Haskell2010 benchmark relude-benchmark+ import: common-options type: exitcode-stdio-1.0- default-language: Haskell2010- ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N hs-source-dirs: benchmark main-is: Main.hs- build-depends: base < 5++ build-depends: base , relude- , containers- , gauge+ , tasty-bench , unordered-containers- if impl(ghc == 7.10.3)- build-depends: semigroups >= 0.18 - default-extensions: NoImplicitPrelude- ScopedTypeVariables+ ghc-options: -rtsopts
src/Relude.hs view
@@ -1,95 +1,142 @@-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable -{- | Main module that reexports all functionality allowed to use-without importing any other modules. Just add next lines to your-module to replace default 'Prelude' with better one.+@relude@ is a safe, performant, user-friendly and lightweight Haskell+standard library. +"Relude" is the main module that reexports all functionality provided by the library that will replace the default @Prelude@ in your project.++== Usage++To start using @relude@ in your project, you can set the library up for you by+one of the following ways.++=== mixins++One of the most convenient ways to use @relude@ is via the @mixins@ feature. This+feature is available since @Cabal >= 2.2@. In order to use the @mixins@ feature+one needs to specify supported @cabal-version@ in your package description.+And then the following lines should be added to the required stanza to replace+default "Prelude" with @relude@.+ @+cabal-version: 2.4+...+library+ ...+ mixins: base hiding (Prelude)+ , relude (Relude as Prelude)+ , relude+@++=== @base-noprelude@++Alternatively, you can replace @base@ package in your dependencies with+@[base-noprelude](http://hackage.haskell.org/package/base-noprelude)@ and add+the following "Prelude" module to your package to use @relude@ by default in+every module instead of @base@ "Prelude":++@+__module__ Prelude+ ( __module__ "Relude"+ ) __where__++__import__ "Relude"+@++=== @NoImplicitPrelude@++If you want to use @relude@ per-module basis then just add next lines to your+module to replace default "Prelude":++@ \{\-\# LANGUAGE NoImplicitPrelude \#\-\} __import__ "Relude" @ -This documentation section contains description of internal module structure to+== Structure++This documentation section contains the description of internal module structure to help navigate between modules, search for interesting functionalities and-understand where you need to put your new changes.+understand where you need to put your new changes (if you're a contributor). Functions and types are distributed across multiple modules and grouped by-meaning or __theme__. Name of the module should give you hints regarding what-this module contains. Some /themes/ contain a great amount of both reexported-functions and functions of our own. To make it easier to understand these huge-chunks of functions, all reexported stuff is moved into separate module with-name @Relude.SomeTheme.Reexport@ and our own functions and types are in-@Relude.SomeTheme.SomeName@. For example, see modules-"Relude.Container.Class" and "Relude.Container.Reexport".--Below is a short description of what you can find under different modules:--* __"Relude.Applicative"__: reexports from "Control.Applicative" and some- general-purpose applicative combinators.-* __"Relude.Base"__: different general types and type classes from @base@- package ('Int', 'Num', 'Generic', etc.) not exported by other modules.-* __"Relude.Bool"__: 'Bool' data type with different predicates and combinators.-* __"Relude.Debug"__: @trace@-like debugging functions with compile-time- warnings (so you don't forget to remove them)-* __"Relude.DeepSeq"__: reexports from "Control.DeepSeq" module and- functions to evaluate expressions to weak-head normal form or normal form.-* __"Relude.Exception"__: reexports "Control.Exception.Safe" from- @safe-exceptions@ package, 'bug' as better 'error', 'Exc' pattern synonym for- convenient pattern-matching on exceptions.-* __"Relude.Foldable"__: reexports functions for 'Foldable' and 'Traversable'.-* __"Relude.Function"__: almost everything from "Data.Function" module.-* __"Relude.Functor"__: reexports from "Data.Functor", "Data.Bifunctor",- other useful 'Functor' combinators.-* __"Relude.Lifted"__: lifted to 'MonadIO' functions to work with console,- files, 'IORef's, 'MVar's, etc.-* __"Relude.List"__: big chunk of "Data.List", 'NonEmpty' type and- functions for this type ('head', 'tail', 'last', 'init').-* __"Relude.Monad"__: monad transormers, combinators for 'Maybe' and 'Either'.-* __"Relude.Monoid"__: reexports from "Data.Monoid" and "Data.Semigroup".-* __"Relude.Nub"__: better versions of @nub@ function for list.-* __"Relude.Print"__: polymorphic 'putStrLn' function and functions to print 'Text'.-* __"Relude.String"__: reexports from @text@ and @bytestring@ packages with- conversion functions between different textual types.-* __"Relude.Unsafe"__: unsafe functions (produce 'error').- Not exported by "Relude" module by default.+meaning or __category__. Name of the module should give you hints regarding what+this module contains. Some /categories/ contain a significant amount of both+reexported+functions and functions of our own. To make it easier to understand these enormous+chunks of functions, all reexported stuff is moved into the separate module with+name @Relude.SomeCategory.Reexport@ and our own functions and types are in+@Relude.SomeCategory.SomeName@. For example, see modules+"Relude.Foldable.Fold" and "Relude.Foldable.Reexport". -} module Relude- ( -- * Reexports from base and from modules in this repo- module Relude.Applicative- , module Relude.Base- , module Relude.Bool- , module Relude.Container- , module Relude.Debug- , module Relude.DeepSeq- , module Relude.Exception- , module Relude.Foldable- , module Relude.Function- , module Relude.Functor- , module Relude.Lifted- , module Relude.List- , module Relude.Monad- , module Relude.Monoid- , module Relude.Nub- , module Relude.Print- , module Relude.String- ) where+ ( -- * Default Modules+ module Relude.Applicative+ -- $applicative+ , module Relude.Base+ -- $base+ , module Relude.Bool+ -- $bool+ , module Relude.Container+ -- $container+ , module Relude.Debug+ -- $debug+ , module Relude.DeepSeq+ -- $deepseq+ , module Relude.Enum+ -- $enum+ , module Relude.Exception+ -- $exception+ , module Relude.File+ -- $file+ , module Relude.Foldable+ -- $foldable+ , module Relude.Function+ -- $function+ , module Relude.Functor+ -- $functor+ , module Relude.Lifted+ -- $lifted+ , module Relude.List+ -- $list+ , module Relude.Monad+ -- $monad+ , module Relude.Monoid+ -- $monoid+ , module Relude.Nub+ -- $nub+ , module Relude.Numeric+ -- $numeric+ , module Relude.Print+ -- $print+ , module Relude.String+ -- $string + -- * Extra Modules+ -- $extra+ ) where+ import Relude.Applicative import Relude.Base import Relude.Bool import Relude.Container import Relude.Debug import Relude.DeepSeq+import Relude.Enum import Relude.Exception+import Relude.File import Relude.Foldable import Relude.Function import Relude.Functor@@ -98,5 +145,143 @@ import Relude.Monad import Relude.Monoid import Relude.Nub+import Relude.Numeric import Relude.Print import Relude.String++{- $applicative+__"Relude.Applicative"__ contains reexports from "Control.Applicative" and some+general-purpose applicative combinators.+-}++{- $base+__"Relude.Base"__ contains different general types and type classes from @base@+package ('Char', 'Eq', 'Generic', etc.) not exported by other modules.+-}++{- $bool+__"Relude.Bool"__ contains 'Bool' data type with different predicates and combinators.+-}++{- $container+__"Relude.Container"__ provides 'One' typeclass for creating data structures+from singleton element and reexports of types from packages @containers@ and+@unordered-containers@.+-}++{- $debug+__"Relude.Debug"__ contains @trace@-like debugging functions with compile-time+warnings (so you don't forget to remove them).+-}++{- $deepseq+__"Relude.DeepSeq"__ has reexports from "Control.DeepSeq" module and functions+to evaluate expressions to weak-head normal form or normal form.+-}++{- $enum+__"Relude.Enum"__ reexports 'Enum' related typeclasses and functions. Also+introduced a few useful helpers to work with Enums.+-}++{- $exception+__"Relude.Exception"__ contains reexports from "Control.Exception", introduces+'bug' function as better 'error' and 'Exc' pattern synonym for convenient+pattern-matching on exceptions.+-}++{- $file+__"Relude.File"__ implements functions to work with file content as 'Text' or+'ByteString'.+-}++{- $foldable+__"Relude.Foldable"__ reexports functions for 'Foldable' and 'Traversable' and+provide own better alternatives to some existing functions.+-}++{- $function+__"Relude.Function"__ contains almost everything from the "Data.Function" module.+-}++{- $functor+__"Relude.Functor"__ contains reexports from "Data.Functor", "Data.Bifunctor",+other useful 'Functor' combinators.+-}++{- $lifted+__"Relude.Lifted"__ implements lifted to 'MonadIO' functions to work with+console, files, 'IORef's, 'MVar's, etc.+-}++{- $list+__"Relude.List"__ provides big chunk of "Data.List", 'NonEmpty' type and+functions for this type ('head', 'tail', 'last', 'init').+-}++{- $monad+__"Relude.Monad"__ contains functions and data types from "Data.Maybe" and+"Data.Either" modules, monad transformers and other various combinators.+-}++{- $monoid+__"Relude.Monoid"__ reexports various types and functions from "Data.Monoid" and+"Data.Semigroup".+-}++{- $nub+__"Relude.Nub"__ implements better versions of @nub@ function for list.+-}++{- $numeric+__"Relude.Numeric"__ contains functions and types to work with numerical data.+-}++{- $print+__"Relude.Print"__ contains printing to terminal functions for 'Text' and 'ByteString'.+-}++{- $string+__"Relude.String"__ contains reexports from @text@ and @bytestring@ packages+with conversion functions between different textual types.+-}++{- $extra+The following modules are not exported by default, but you can easily bring them to+every module in your package by modifying your "Prelude" file:+++------------------------------+-----------------------------------------------------------++| __"Relude.Extra"__ | Reexports every @Relude.Extra.*@ module |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.Bifunctor"__ | Additional combinators for 'Bifunctor'. |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.CallStack"__ | Useful functions to extract information from 'CallStack'. |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.Enum"__ | Extra utilities for types that implement 'Bounded' |+| | and 'Enum' constraints. |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.Foldable"__ | Extra folds for instances of the 'Foldable' typeclass. |+| | Currently, just a short-circuitable left fold. |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.Foldable1"__ | t'Relude.Extra.Foldable1.Foldable1' typeclass |+| | like 'Foldable' but for non-empty structures. |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.Group"__ | Grouping functions, polymorphic on return @Map@ type. |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.Lens"__ | Minimal implementation of @lens@ package required |+| | for basic usage. |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.Map"__ | Typeclass for @Map@-like data structures. |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.Newtype"__ | Generic functions that automatically work for any |+| | @newtype@. |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.Tuple"__ | Functions for working with tuples. |++------------------------------+-----------------------------------------------------------++| __"Relude.Extra.Type"__ | Functions for inspecting and working with types. |++------------------------------+-----------------------------------------------------------++| __"Relude.Unsafe"__ | Unsafe partial functions (produce 'error') for lists and |+| | 'Maybe'. |++------------------------------+-----------------------------------------------------------+++-}
src/Relude/Applicative.hs view
@@ -1,50 +1,78 @@ {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Applicative+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Convenient utils to work with 'Applicative'. There were more functions in this module--- (see <https://www.stackage.org/haddock/lts-8.9/protolude-0.1.10/Applicative.html protolude version>)--- but only convenient ans most used are left.+This module contains reexports of 'Applicative' and related functional.+Additionally, it provides convenient combinators to work with 'Applicative'.+-} module Relude.Applicative- ( module Control.Applicative- , pass- ) where+ ( -- * Reexports+ -- $reexports+ Alternative (..)+ , Applicative (..)+ , Const (..)+ , ZipList (..)+ , liftA3+ , optional+ , (<**>) + -- * Combinators+ , pass+ , appliedTo+ ) where+ import Control.Applicative (Alternative (..), Applicative (..), Const (..), ZipList (..), liftA2, liftA3, optional, (<**>)) + -- $setup+-- >>> import Relude -- >>> import Relude.Monad (Maybe) --- | Shorter alias for @pure ()@.------ >>> pass :: Maybe ()--- Just ()-pass :: Applicative f => f ()-pass = pure ()+{- | Shorter alias for @pure ()@. -{--orAlt :: (Alternative f, Monoid a) => f a -> f a-orAlt f = f <|> pure mempty+>>> pass :: Maybe ()+Just () -orEmpty :: Alternative f => Bool -> a -> f a-orEmpty b a = if b then pure a else empty+Useful shortcut when need an empty action: -eitherA :: Alternative f => f a -> f b -> f (Either a b)-eitherA a b = (Left <$> a) <|> (Right <$> b)+@+printJust :: Maybe Int -> IO ()+printJust mInt = case mInt of+ Just i -> putStrLn $ "Number: " ++ show i+ Nothing -> pass+@+-}+pass :: Applicative f => f ()+pass = pure ()+{-# INLINE pass #-} -purer :: (Applicative f, Applicative g) => a -> f (g a)-purer = pure . pure+{- | Named version of the '<**>' operator, which is '<*>' but flipped. It is+helpful for chaining applicative operations in forward applications using+'Relude.Function.&'. -liftAA2 :: (Applicative f, Applicative g) => (a -> b -> c) -> f (g a) -> f (g b) -> f (g c)-liftAA2 = liftA2 . liftA2+>>> Just (+ 1) & appliedTo (Just 2)+Just 3+>>> Just (+) & appliedTo (Just 1) & appliedTo (Just 2)+Just 3+>>> Nothing & appliedTo (Just 2)+Nothing -(<<*>>) :: (Applicative f, Applicative g) => f (g (a -> b)) -> f (g a) -> f (g b)-(<<*>>) = liftA2 (<*>)+@since 0.5.0+-}+appliedTo :: Applicative f => f a -> f (a -> b) -> f b+appliedTo = (<**>)+{-# INLINE appliedTo #-}++{- $reexports+Main data types and functions reexported from "Control.Applicative". -}
src/Relude/Base.hs view
@@ -1,113 +1,81 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE Unsafe #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE Trustworthy #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Base+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Reexports from @GHC.*@ modules of <https://www.stackage.org/lts-8.9/package/base-4.9.1.0 base>--- package.+Reexports from @Data.*@ and @GHC.*@ modules of+<https://hackage.haskell.org/package/base base> package.+-} module Relude.Base- ( -- * Base types- module Data.Bits- , module Data.Char- , module Data.Int- , module Data.Word- , Natural-- -- * Base type classes- , module Data.Eq- , module Data.Ord-- -- * System IO- , module System.IO+ ( -- * Base types+ module Data.Char - -- * Types for type-level computation- , module Data.Coerce- , module Data.Kind- , module Data.Proxy- , module Data.Typeable- , module Data.Void+ -- * Base type classes+ , module Data.Eq+ , module Data.Ord - , module GHC.Base- , module GHC.Enum- , module GHC.Float- , module GHC.Generics- , module GHC.Num- , module GHC.Real- , module GHC.Show+ -- * System IO+ , module System.IO -#if MIN_VERSION_base(4,10,0)- , module GHC.TypeNats-#else- , module GHC.TypeLits+ -- * Types for type-level computation+ , module Data.Coerce+ , module Data.Kind+ , module Data.Proxy+#if __GLASGOW_HASKELL__ >= 904+ , module Data.Type.Equality #endif+ , module Data.Typeable+ , module Data.Void - , module GHC.OverloadedLabels- , module GHC.ExecutionStack- , module GHC.Stack+ -- * Basic type classes+ , module GHC.Base+ , module GHC.Generics+ , module GHC.Show - , ($!)- ) where+-- * GHC-specific functionality+ , module GHC.TypeNats+ , module GHC.OverloadedLabels+ , module GHC.ExecutionStack+ , module GHC.Stack+ ) where -- Base types-import Data.Bits (xor) import Data.Char (Char, chr)-import Data.Int (Int, Int16, Int32, Int64, Int8)-import Data.Word (Word, Word16, Word32, Word64, Word8, byteSwap16, byteSwap32, byteSwap64)-import Numeric.Natural (Natural) -- IO-import System.IO (FilePath, Handle, IO, IOMode (..), stderr, stdin, stdout, withFile)+import System.IO (FilePath, IO, IOMode (..)) -- Base typeclasses-import Data.Eq (Eq (..))+import Data.Eq (Eq, (/=), (==)) import Data.Ord (Down (..), Ord (..), Ordering (..), comparing) -- Types for type-level computation import Data.Coerce (Coercible, coerce) import Data.Kind (Constraint, Type) import Data.Proxy (Proxy (..))+#if __GLASGOW_HASKELL__ >= 904+import Data.Type.Equality (type (~))+#endif import Data.Typeable (Typeable) import Data.Void (Void, absurd, vacuous) -import GHC.Base (String, asTypeOf, maxInt, minInt, ord, seq)-import GHC.Enum (Bounded (..), Enum (..), boundedEnumFrom, boundedEnumFromThen)-import GHC.Float (Double (..), Float (..), Floating (acos, acosh, asin, asinh, atan, atanh, cos, cosh, exp, logBase, pi, sin, sinh, sqrt, tan, tanh, (**)))+import GHC.Base (asTypeOf, ord, seq, ($!)) import GHC.Generics (Generic)-import GHC.Num (Integer, Num (..), subtract)-import GHC.Real hiding (showSigned, (%)) import GHC.Show (Show) -#if MIN_VERSION_base(4,10,0) import GHC.TypeNats (CmpNat, KnownNat, Nat, SomeNat (..), natVal, someNatVal)-#else-import GHC.TypeLits (CmpNat, KnownNat, Nat, SomeNat (..), natVal, someNatVal)-#endif import GHC.ExecutionStack (getStackTrace, showStackTrace) import GHC.OverloadedLabels (IsLabel (..)) import GHC.Stack (CallStack, HasCallStack, callStack, currentCallStack, getCallStack, prettyCallStack, prettySrcLoc, withFrozenCallStack)---- $setup--- >>> import Relude.Function (const, ($))---- | Stricter version of 'Data.Function.$' operator.--- Default Prelude defines this at the toplevel module, so we do as well.------ >>> const 3 $ Prelude.undefined--- 3--- >>> const 3 $! Prelude.undefined--- *** Exception: Prelude.undefined--- CallStack (from HasCallStack):--- error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err--- ...-($!) :: (a -> b) -> a -> b-f $! x = let !vx = x in f vx-infixr 0 $!
src/Relude/Bool.hs view
@@ -1,12 +1,34 @@ {-# LANGUAGE Safe #-} --- | Convenient commonly used and very helpful functions to work with--- 'Bool' and also with monads.+{- |+Module : Relude.Bool+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +Convenient commonly used and very helpful functions to work with 'Bool' and also+with monads.+-}+ module Relude.Bool- ( module Relude.Bool.Guard- , module Relude.Bool.Reexport- ) where+ ( module Relude.Bool.Reexport+ -- $reexport+ , module Relude.Bool.Guard+ -- $guard+ ) where import Relude.Bool.Guard import Relude.Bool.Reexport++{- $reexport+Reexports from "Data.Bool" and "Control.Monad". Includes necessary types and+functions to work with 'Bool' type.+-}++{- $guard+Boolean combinators that work in monads. Like 'ifM' or 'guardM'.+-}
src/Relude/Bool/Guard.hs view
@@ -1,74 +1,179 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE Safe #-} --- | This module contains monadic predicates.+{- |+Module : Relude.Bool.Guard+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +Monadic boolean combinators.+-}+ module Relude.Bool.Guard- ( guardM- , ifM- , unlessM- , whenM- ) where+ ( guarded+ , guardM+ , ifM+ , unlessM+ , whenM+ , (&&^)+ , (||^)+ ) where -import Relude.Bool.Reexport (Bool, guard, unless, when)+import Relude.Applicative (Alternative, Applicative (..), empty)+import Relude.Bool.Reexport (Bool (..), guard, unless, when) import Relude.Function (flip) import Relude.Monad (Monad, MonadPlus, (>>=)) + -- $setup -- >>> import Relude.Applicative (pure) -- >>> import Relude.Bool.Reexport (Bool (..))+-- >>> import Relude.Debug (error) -- >>> import Relude.Function (($)) -- >>> import Relude.Monad (Maybe (..)) -- >>> import Relude.Print (putTextLn)+-- >>> import Relude (Int, String, even, const) --- | Monadic version of 'when'.------ >>> whenM (pure False) $ putTextLn "No text :("--- >>> whenM (pure True) $ putTextLn "Yes text :)"--- Yes text :)--- >>> whenM (Just True) (pure ())--- Just ()--- >>> whenM (Just False) (pure ())--- Just ()--- >>> whenM Nothing (pure ())--- Nothing+{- | Monadic version of 'when'.+Conditionally executes the provided action.++>>> whenM (pure False) $ putTextLn "No text :("+>>> whenM (pure True) $ putTextLn "Yes text :)"+Yes text :)+>>> whenM (Just True) (pure ())+Just ()+>>> whenM (Just False) (pure ())+Just ()+>>> whenM Nothing (pure ())+Nothing+-} whenM :: Monad m => m Bool -> m () -> m () whenM p m = p >>= flip when m {-# INLINE whenM #-} --- | Monadic version of 'unless'.------ >>> unlessM (pure False) $ putTextLn "No text :("--- No text :(--- >>> unlessM (pure True) $ putTextLn "Yes text :)"+{- | Monadic version of 'unless'. Reverse of 'whenM'.+Conditionally don't execute the provided action.++>>> unlessM (pure False) $ putTextLn "No text :("+No text :(+>>> unlessM (pure True) $ putTextLn "Yes text :)"+-} unlessM :: Monad m => m Bool -> m () -> m () unlessM p m = p >>= flip unless m {-# INLINE unlessM #-} --- | Monadic version of @if-then-else@.------ >>> ifM (pure True) (putTextLn "True text") (putTextLn "False text")--- True text+{- | Monadic version of @if-then-else@.++>>> ifM (pure True) (putTextLn "True text") (putTextLn "False text")+True text+>>> ifM (pure False) (putTextLn "True text") (putTextLn "False text")+False text+-} ifM :: Monad m => m Bool -> m a -> m a -> m a ifM p x y = p >>= \b -> if b then x else y {-# INLINE ifM #-} --- | Monadic version of 'guard'. Occasionally useful.--- Here some complex but real-life example:------ @--- findSomePath :: IO (Maybe FilePath)------ somePath :: MaybeT IO FilePath--- somePath = do--- path <- MaybeT findSomePath--- guardM $ liftIO $ doesDirectoryExist path--- return path--- @+{- | Monadic version of 'guard' that help to check that a condition ('Bool')+holds inside. Works with 'Monad's that are also 'Alternative'.++>>> guardM (Just True)+Just ()+>>> guardM (Just False)+Nothing+>>> guardM Nothing+Nothing++Here some complex but real-life example:++@+findSomePath :: IO (Maybe FilePath)++somePath :: MaybeT IO FilePath+somePath = do+ path <- MaybeT findSomePath+ guardM $ liftIO $ doesDirectoryExist path+ return path+@+-} guardM :: MonadPlus m => m Bool -> m () guardM f = f >>= guard {-# INLINE guardM #-}++{- | Either lifts a value into an alternative context or gives a+minimal value depending on a predicate. Works with 'Alternative's.++>>> guarded even 3 :: [Int]+[]+>>> guarded even 2 :: [Int]+[2]+>>> guarded (const True) "hello" :: Maybe String+Just "hello"+>>> guarded (const False) "world" :: Maybe String+Nothing++You can use this function to implement smart constructors simpler:++@+__newtype__ HttpHost = HttpHost+ { unHttpHost :: Text+ }++mkHttpHost :: Text -> Maybe HttpHost+mkHttpHost host = HttpHost \<$\> 'guarded' (not . Text.null) host+@++@since 0.6.0.0+-}+guarded :: Alternative f => (a -> Bool) -> a -> f a+guarded p a = if p a then pure a else empty+{-# INLINE guarded #-}++{- | Monadic version of '(Data.Bool.&&)' operator.++It is lazy by the second argument (similar to '(Data.Bool.||)'), meaning that if+the first argument is 'False', the function will return 'False' without evaluating+the second argument.++>>> Just False &&^ Just True+Just False+>>> Just True &&^ Just True+Just True+>>> Just True &&^ Nothing+Nothing+>>> Just False &&^ Nothing+Just False+>>> Just False &&^ error "Shouldn't be evaluated"+Just False++@since 0.4.0+-}+(&&^) :: Monad m => m Bool -> m Bool -> m Bool+(&&^) e1 e2 = ifM e1 e2 (pure False)+{-# INLINE (&&^) #-}++{- | Monadic version of '(Data.Bool.||)' operator.++It is lazy by the second argument (similar to '(Data.Bool.||)'), meaning that if+the first argument is 'True', the function will return 'True' without evaluating+the second argument.++>>> Just False ||^ Just True+Just True+>>> Just False ||^ Just False+Just False+>>> Just False ||^ Nothing+Nothing+>>> Just True ||^ Nothing+Just True+>>> Just True ||^ error "Shouldn't be evaluated"+Just True++@since 0.4.0+-}+(||^) :: Monad m => m Bool -> m Bool -> m Bool+e1 ||^ e2 = ifM e1 (pure True) e2+{-# INLINE (||^) #-}
src/Relude/Bool/Reexport.hs view
@@ -1,16 +1,31 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE Safe #-} --- | This module reexports functions to work with 'Bool' type.+{- |+Module : Relude.Bool.Reexport+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +Reexports functions to work with 'Bool' type.+-}+ module Relude.Bool.Reexport- ( module Control.Monad- , module Data.Bool- ) where+ ( -- * "Data.Bool" reexports+ Bool (..)+ , bool+ , not+ , otherwise+ , (&&)+ , (||)+ -- * "Control.Monad" reexports+ , guard+ , when+ , unless+ ) where import Control.Monad (guard, unless, when) import Data.Bool (Bool (..), bool, not, otherwise, (&&), (||))
src/Relude/Container.hs view
@@ -1,16 +1,39 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE Safe #-} --- | This module exports all container-related stuff.+{- |+Module : Relude.Container+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +This module exports all container-related stuff.+-}+ module Relude.Container- ( module Relude.Container.One- , module Relude.Container.Reexport- ) where+ ( module Relude.Container.One+ -- $one+ , module Relude.Container.Reexport+ -- $reexport+ ) where import Relude.Container.One import Relude.Container.Reexport++{- $one+'One' is a typeclass for creating structures from a singleton element.+This module provides many useful instances of 'One' for common containers+as well.+-}++{- $reexport+Reexports container-related functions, typeclasses and data types from+the following packages:++* [@base@](https://hackage.haskell.org/package/base)+* [@containers@](https://hackage.haskell.org/package/containers)+* [@unordered-containers@](https://hackage.haskell.org/package/unordered-containers)+-}
src/Relude/Container/One.hs view
@@ -1,23 +1,36 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE Safe #-} {-# LANGUAGE TypeFamilies #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Container.One+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable -{- | Typeclass for creating structures from singleton element.+'One' is a typeclass for creating structures from a singleton element.+It has three main goals:++1. Give a shorter name for the construction: uses 'one' instead of common @singleton@.+2. Work with monomorphic structures like 'T.Text' or 'IntSet'.+3. Give a clearer and less scary name for cases where you can use 'Relude.Applicative.pure'+ or @(:[])@.++@since 0.1.0 -} module Relude.Container.One- ( One (..)- ) where+ ( One (..)+ ) where -import Relude.Base (Char, Int, Word8)+import Relude.Base (Char) import Relude.Container.Reexport (HashMap, HashSet, Hashable, IntMap, IntSet, Map, Set, uncurry)+import Relude.Numeric (Int, Word8) import qualified Data.List.NonEmpty as NE @@ -25,6 +38,7 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BSL+import qualified Data.ByteString.Short as SBS import qualified Data.Text as T import qualified Data.Text.Lazy as TL@@ -37,14 +51,10 @@ import qualified Data.Set as Set -- $setup--- >>> import Relude.Base (Int, String)--- >>> import Relude.Bool (Bool (..))--- >>> import Relude.String (Text)--- >>> import qualified Data.HashMap.Strict as HashMap+-- >>> import Relude -{- | Type class for types that can be created from one element. @singleton@-is lone name for this function. Constructions of different type differ:-@:[]@ for lists, two arguments for Maps. Also some data types are monomorphic.+{- | Typeclass for data types that can be created from one element.+E.g. lists, non-empty containers, maps. >>> one True :: [Bool] [True]@@ -52,81 +62,251 @@ "a" >>> one (3, "hello") :: HashMap Int String fromList [(3,"hello")]++__Laws:__++* __@single-size@__: @∀ x . size (one x) ≡ 1@++(where @size@ is a specific function for each container that returns the size of+this container) -} class One x where+ -- | Type of a single element of the structure. type OneItem x- -- | Create a list, map, 'Text', etc from a single element.++ -- | Create a list, map, 'T.Text', etc from a single element. one :: OneItem x -> x -- Lists +{- | Allows to create a singleton list. You might prefer function with name 'one'+instead of 'Relude.Applicative.pure' or @(:[])@.++>>> one 42 :: [Int]+[42]++@+law> 'Relude.Foldable.Reexport.length' ('one' @[a] x) ≡ 1+@+-} instance One [a] where type OneItem [a] = a++ one :: a -> [a] one = (:[]) {-# INLINE one #-} +{- | Allows to create singleton 'NE.NonEmpty' list. You might prefer function with+name 'one' instead of 'Relude.Applicative.pure' or @(:|[])@.++>>> one 42 :: NonEmpty Int+42 :| []++@+law> 'Relude.Foldable.Reexport.length' ('one' @('NE.NonEmpty' a) x) ≡ 1+@+-} instance One (NE.NonEmpty a) where type OneItem (NE.NonEmpty a) = a++ one :: a -> NE.NonEmpty a one = (NE.:|[]) {-# INLINE one #-} +{- | Create singleton 'SEQ.Seq'.++>>> one 42 :: Seq Int+fromList [42]++@+law> 'Relude.Foldable.Reexport.length' ('one' @('SEQ.Seq' a) x) ≡ 1+@+-} instance One (SEQ.Seq a) where type OneItem (SEQ.Seq a) = a- one = (SEQ.empty SEQ.|>)++ one :: a -> SEQ.Seq a+ one = SEQ.singleton {-# INLINE one #-} -- Monomorphic sequences +{- | Create singleton strict 'T.Text'.++>>> one 'a' :: Text+"a"++@+law> 'Data.Text.length' ('one' x) ≡ 1+@+-} instance One T.Text where type OneItem T.Text = Char++ one :: Char -> T.Text one = T.singleton {-# INLINE one #-} +{- | Create singleton lazy 'TL.Text'.++>>> one 'a' :: LText+"a"++@+law> 'Data.Text.Lazy.length' ('one' x) ≡ 1+@+-} instance One TL.Text where type OneItem TL.Text = Char++ one :: Char -> TL.Text one = TL.singleton {-# INLINE one #-} +{- | Create singleton strict 'BS.ByteString'.++>>> one 97 :: ByteString+"a"++@+law> 'BS.length' ('one' x) ≡ 1+@+-} instance One BS.ByteString where type OneItem BS.ByteString = Word8++ one :: Word8 -> BS.ByteString one = BS.singleton {-# INLINE one #-} +{- | Create singleton lazy 'BSL.ByteString'.++>>> one 97 :: LByteString+"a"++@+law> 'BSL.length' ('one' x) ≡ 1+@+-} instance One BSL.ByteString where type OneItem BSL.ByteString = Word8++ one :: Word8 -> BSL.ByteString one = BSL.singleton {-# INLINE one #-} +{- | Create singleton 'SBS.ShortByteString'.++>>> one 97 :: ShortByteString+"a"++@+law> 'SBS.length' ('one' x) ≡ 1+@+-}+instance One SBS.ShortByteString where+ type OneItem SBS.ShortByteString = Word8++ one :: Word8 -> SBS.ShortByteString+ one x = SBS.pack [x]+ {-# INLINE one #-}+ -- Maps +{- | Create singleton 'Map' from key-value pair.++>>> one (3, "foo") :: Map Int Text+fromList [(3,"foo")]++@+law> 'Relude.Foldable.Reexport.length' ('one' @('Map' k v) (k, v)) ≡ 1+@+-} instance One (Map k v) where type OneItem (Map k v) = (k, v)++ one :: (k, v) -> Map k v one = uncurry M.singleton {-# INLINE one #-} +{- | Create singleton 'HashMap' from key-value pair.++>>> one (3, "foo") :: HashMap Int Text+fromList [(3,"foo")]++@+law> 'Relude.Foldable.Reexport.length' ('one' @('HashMap' k v) (k, v)) ≡ 1+@+-} instance Hashable k => One (HashMap k v) where type OneItem (HashMap k v) = (k, v)++ one :: (k, v) -> HashMap k v one = uncurry HM.singleton {-# INLINE one #-} +{- | Create singleton 'IntMap' from key-value pair.++>>> one (3, "foo") :: IntMap Text+fromList [(3,"foo")]++@+law> 'Relude.Foldable.Reexport.length' ('one' @('IntMap' a) x) ≡ 1+@+-} instance One (IntMap v) where type OneItem (IntMap v) = (Int, v)++ one :: (Int, v) -> IntMap v one = uncurry IM.singleton {-# INLINE one #-} -- Sets -instance One (Set v) where- type OneItem (Set v) = v+{- | Create singleton 'Set'.++>>> one 42 :: Set Int+fromList [42]++@+law> 'Relude.Foldable.Reexport.length' ('one' @('Set' a) x) ≡ 1+@+-}+instance One (Set a) where+ type OneItem (Set a) = a++ one :: a -> Set a one = Set.singleton {-# INLINE one #-} -instance Hashable v => One (HashSet v) where- type OneItem (HashSet v) = v+{- | Create singleton 'HashSet'.++>>> one 42 :: HashSet Int+fromList [42]++@+law> 'Relude.Foldable.Reexport.length' ('one' @('HashSet' a) x) ≡ 1+@+-}+instance Hashable a => One (HashSet a) where+ type OneItem (HashSet a) = a++ one :: a -> HashSet a one = HashSet.singleton {-# INLINE one #-} +{- | Create singleton 'IntSet'.++>>> one 42 :: IntSet+fromList [42]++@+law> 'Data.IntSet.size' ('one' x) ≡ 1+@+-} instance One IntSet where type OneItem IntSet = Int++ one :: Int -> IntSet one = IS.singleton {-# INLINE one #-}
src/Relude/Container/Reexport.hs view
@@ -1,24 +1,45 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE Trustworthy #-} --- | This module reexports all container related stuff from 'Prelude'.+{- |+Module : Relude.Container.Reexport+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +Reexports container-related data types, functions and typeclasses from @base@,+@containers@ and @unordered-containers@ packages.+-}+ module Relude.Container.Reexport- ( module Data.Hashable- , module Data.HashMap.Strict- , module Data.HashSet- , module Data.IntMap.Strict- , module Data.IntSet- , module Data.Map.Strict- , module Data.Sequence- , module Data.Set- , module Data.Tuple- , module GHC.Exts- ) where+ ( -- * "Data.Hashable" reexports+ Hashable (..)+ -- * "Data.HashMap.Strict" reexports+ , HashMap+ -- * "Data.HashSet" reexports+ , HashSet+ -- * "Data.IntMap.Strict" reexports+ , IntMap+ -- * "Data.IntSet" reexports+ , IntSet+ -- * "Data.Map.Strict" reexports+ , Map+ -- * "Data.Sequence" reexports+ , Seq+ -- * "Data.Set" reexports+ , Set+ -- * "Data.Tuple" reexports+ , curry+ , fst+ , snd+ , swap+ , uncurry+ -- * "GHC.Exts" reexports+ , IsList (..)+ ) where import Data.Hashable (Hashable (hashWithSalt)) import Data.HashMap.Strict (HashMap)
src/Relude/Debug.hs view
@@ -1,96 +1,312 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE CPP #-} -#if ( __GLASGOW_HASKELL__ >= 804 )-{-# LANGUAGE TypeInType #-}+#if __GLASGOW_HASKELL__ >= 902+{-# OPTIONS_GHC -Wno-operator-whitespace #-} #endif -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+#if ( __GLASGOW_HASKELL__ < 906 )+{-# LANGUAGE TypeInType #-}+#endif+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-} --- | Functions for debugging. If you left these functions in your code--- then warning is generated to remind you about left usages. Also some--- functions (and data types) are convenient for prototyping.+{- |+Module : Relude.Debug+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +This module contains functions for debugging __pure__ functions. You+can't use functions like 'System.IO.putStrLn' for this purpose because+they require changes to the type signature, but functions in this module+avoid this problem by being pure on their own.++Additionally, these functions produce compile-time warnings, if you leave them+in your code. Warnings help you to cleanup all debugging usages before+releasing.++@+__ghci>__ foo = trace "I forgot trace in code"++\<interactive\>:4:7: __warning__: [-Wdeprecations]+ In the use of ‘trace’ (imported from "Relude"):+ "'trace' remains in code"+@++The following table briefly shows names and types of all functions in+this module.+++-----------------+----------------------------------------++| __Name__ | __Type__ |++=================+========================================++| 'trace' | @String -> a -> a@ |++-----------------+----------------------------------------++| 'traceShow' | @Show a => a -> b -> b@ |++-----------------+----------------------------------------++| 'traceShowId' | @Show a => a -> a@ |++-----------------+----------------------------------------++| 'traceShowWith' | @Show b => (a -> b) -> a -> a@ |++-----------------+----------------------------------------++| 'traceId' | @String -> String@ |++-----------------+----------------------------------------++| 'traceM' | @(Applicative f) => String -> f ()@ |++-----------------+----------------------------------------++| 'traceShowM' | @(Show a, Applicative f) => a -> f ()@ |++-----------------+----------------------------------------+++__⚠ NOTE:__ Use these functions only for local debugging+purposes. They break referential transparency, they are only useful+when you want to observe intermediate values of your pure functions+and to understand the behaviour locally. If you want to log debug+messages in your application, consider using a logging library+instead.+-}+ module Relude.Debug- ( Undefined (..)- , error- , trace- , traceM- , traceId- , traceShow- , traceShowId- , traceShowM- , undefined- ) where+ ( -- * Tracing+ trace+ , traceShow+ , traceShowId+ , traceShowWith+ , traceId+ , traceM+ , traceShowM -import Control.Monad (Monad, return)+ -- * Imprecise error+ , error+ , Undefined (..)+ , undefined+ ) where+ import Data.Data (Data)-import Data.Text (Text, unpack)-import Data.Typeable (Typeable)+#if __GLASGOW_HASKELL__ >= 904+import Data.Type.Equality (type (~))+#endif import GHC.Exts (RuntimeRep, TYPE)-import GHC.Generics (Generic)-import System.IO.Unsafe (unsafePerformIO)+import GHC.TypeLits (ErrorMessage (..), TypeError) -import Relude.Base (HasCallStack)+import Relude.Applicative (Applicative)+import Relude.Base (Char, Constraint, Eq, Generic, HasCallStack, Ord, Show, Type, Typeable)+import Relude.Enum (Bounded, Enum)+import Relude.String (Read, String, Text, toString) -import Relude.Applicative (pass)-import Relude.Print (Print, putStrLn)+import qualified Debug.Trace as Debug+import qualified Prelude -import qualified Prelude as P --- | Generalized over string version of 'Debug.Trace.trace' that leaves warnings.+-- $setup+-- >>> import Relude+-- >>> :set -Wno-deprecations++----------------------------------------------------------------------------+-- trace+----------------------------------------------------------------------------++{- | Prints the given 'String' message and returns the passed value of+type @a@.++>>> increment l = map (+1) l+>>> increment [2, 3, 4]+[3,4,5]++>>> increment l = trace ("incrementing each value of: " ++ show l) (map (+1) l)+>>> increment [2, 3, 4]+incrementing each value of: [2,3,4]+[3,4,5]++* If you want to print a 'Show'able value instead of 'String', use 'traceShow'.+* If you want to print the value itself, use 'traceShowId'.+* If you want to print by specifying a custom formatting function, use 'traceShowWith'.++-}+trace :: String -> a -> a+trace = Debug.trace {-# WARNING trace "'trace' remains in code" #-}-trace :: Print b => b -> a -> a-trace string expr = unsafePerformIO (do- putStrLn string- return expr) --- | 'P.error' that takes 'Text' as an argument.-error :: forall (r :: RuntimeRep) . forall (a :: TYPE r) . HasCallStack- => Text -> a-error s = P.error (unpack s)+{- | Similar to 'trace' but prints a given value with the 'Show'+instance instead of a 'String'. --- | Version of 'Debug.Trace.traceShow' that leaves warning.+>>> increment l = map (+1) l+>>> increment [2, 3, 4]+[3,4,5]++>>> increment l = traceShow l (map (+1) l)+>>> increment [2, 3, 4]+[2,3,4]+[3,4,5]++* If you want to print a specific 'String' instead, use 'trace'+* If you want to print and return the same value, use 'traceShowId'+* If you want to specify a custom printing function, use 'traceShowWith'+-}+traceShow :: Show a => a -> b -> b+traceShow = Debug.traceShow {-# WARNING traceShow "'traceShow' remains in code" #-}-traceShow :: P.Show a => a -> b -> b-traceShow a b = trace (P.show a) b --- | Version of 'Debug.Trace.traceShow' that leaves warning.+{- | Similar to 'traceShow' but prints the given value itself instead+of a separate value.++>>> traceShowId (1+2+3, "hello" ++ "world")+(6,"helloworld")+(6,"helloworld")++* If you to specify a different value to print, use 'trace' or 'traceShow'+* If you want to have more control over printing, use 'traceShowWith'+-}+traceShowId :: Show a => a -> a+traceShowId = Debug.traceShowId {-# WARNING traceShowId "'traceShowId' remains in code" #-}-traceShowId :: P.Show a => a -> a-traceShowId a = trace (P.show a) a --- | Version of 'Debug.Trace.traceShowM' that leaves warning.-{-# WARNING traceShowM "'traceShowM' remains in code" #-}-traceShowM :: (P.Show a, Monad m) => a -> m ()-traceShowM a = trace (P.show a) pass+{- | Similar 'traceShowId', but uses a provided function to convert the+argument to a value with the 'Show' constraint. --- | Version of 'Debug.Trace.traceM' that leaves warning and takes 'Text'.+>>> traceShowWith fst (1, "ABC")+1+(1,"ABC")++In other words, @'traceShowId' ≡ 'traceShowWith' id@.++This function is useful for debugging values that do not have 'Show'+instance:++>>> fst $ traceShowWith fst (1, id)+1+1++* If you don't need such flexibility, use simpler 'trace', 'traceShow' or 'traceShowId'++@since 1.0.0.0+-}+traceShowWith :: Show b => (a -> b) -> a -> a+traceShowWith f v = Debug.traceShow (f v) v+{-# WARNING traceShowWith "'traceShowWith remains in code" #-}++{- | Trace function to print values while working a pure monad+(e.g. 'Maybe', 'State', etc.)++>>> :{+let action :: Maybe Int+ action = do+ x <- Just 3+ traceM ("x: " ++ show x)+ y <- pure 12+ traceM ("y: " ++ show y)+ pure (x*2 + y)+in action+:}+x: 3+y: 12+Just 18++* If you want to print a value with the 'Show' instance instead, use 'traceShowM'+-}+traceM :: (Applicative f) => String -> f ()+traceM = Debug.traceM {-# WARNING traceM "'traceM' remains in code" #-}-traceM :: (Monad m) => Text -> m ()-traceM s = trace (unpack s) pass --- | Version of 'Debug.Trace.traceId' that leaves warning and takes 'Text'.+{-|+Like 'traceM', but uses 'Prelude.show' on the argument to convert it to a+'String'.++>>> :{+let action :: Maybe Int+ action = do+ x <- Just 3+ traceShowM x+ y <- pure 12+ traceShowM y+ pure (x*2 + y)+in action+:}+3+12+Just 18+-}+traceShowM :: (Show a, Applicative f) => a -> f ()+traceShowM = Debug.traceShowM+{-# WARNING traceShowM "'traceShowM' remains in code" #-}++{- | Similar to 'traceShowId' but specialised for 'String'.++>>> traceId "hello"+"hello+hello"+-}+traceId :: String -> String+traceId = Debug.traceId {-# WARNING traceId "'traceId' remains in code" #-}-traceId :: Text -> Text-traceId s = trace s s +----------------------------------------------------------------------------+-- error+----------------------------------------------------------------------------++{- | Throw pure errors. Use this function only to when you are sure that this+branch of code execution is not possible. __DO NOT USE__ 'error' as a normal+error handling mechanism.++⚠️__CAUTION__⚠️ Unlike "Prelude" version, 'error' takes+ t'Relude.String.Reexport.Text' as an argument. In case it used by mistake,+the user will see the following:++>>> error ("oops" :: String)+...+... 'error' expects 'Text' but was given 'String'.+ Possible fixes:+ * Make sure OverloadedStrings extension is enabled+ * Use 'error (toText msg)' instead of 'error msg'+...+>>> error False+...+... 'error' works with 'Text'+ But given: Bool+...+-}+error+ :: forall (r :: RuntimeRep) (a :: TYPE r) (t :: Type) .+ (HasCallStack, IsText t)+ => t+ -> a+error e = Prelude.error (toString e)++type IsText (t :: Type) = (t ~ Text, CheckIsText t)++type family CheckIsText (t :: Type) :: Constraint where+ CheckIsText Text = ()+ CheckIsText [Char] = TypeError+ ( 'Text "'error' expects 'Text' but was given 'String'."+ ':$$: 'Text "Possible fixes:"+ ':$$: 'Text " * Make sure OverloadedStrings extension is enabled"+ ':$$: 'Text " * Use 'error (toText msg)' instead of 'error msg'"+ )+ CheckIsText a = TypeError+ ( 'Text "'error' works with 'Text'"+ ':$$: 'Text "But given: " ':<>: 'ShowType a+ )++----------------------------------------------------------------------------+-- Undefined and undefined+----------------------------------------------------------------------------+ -- | Similar to 'undefined' but data type.-{-# WARNING Undefined "'Undefined' type remains in code" #-} data Undefined = Undefined- deriving (P.Eq, P.Ord, P.Show, P.Read, P.Enum, P.Bounded, Data, Typeable, Generic)+ deriving stock (Eq, Ord, Show, Read, Enum, Bounded, Data, Typeable, Generic)+{-# WARNING Undefined "'Undefined' type remains in code" #-} --- | 'P.undefined' that leaves warning in code on every usage.-{-# WARNING undefined "'undefined' function remains in code (or use 'error')" #-}+-- | 'Prelude.undefined' that leaves warning in code on every usage. undefined :: forall (r :: RuntimeRep) . forall (a :: TYPE r) . HasCallStack => a-undefined = P.undefined+undefined = Prelude.undefined+{-# WARNING undefined "'undefined' function remains in code" #-}
src/Relude/DeepSeq.hs view
@@ -1,43 +1,99 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE Safe #-} --- | This module contains useful functions to evaluate expressions to weak-head--- normal form or just normal form. Useful to force traces or @error@ inside--- monadic computation or to remove space leaks.+{- |+Module : Relude.DeepSeq+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +This module contains useful functions to evaluate expressions to weak-head+normal form (WHNF) or just normal form (NF). Useful to force traces or @error@s+inside monadic computations or to remove space leaks.+-}+ module Relude.DeepSeq- ( module Control.DeepSeq- , evaluateNF- , evaluateNF_- , evaluateWHNF- , evaluateWHNF_- ) where+ ( -- * "Control.DeepSeq" reexports+ NFData (..)+ , deepseq+ , force+ , ($!!) + -- * Evaluation+ , evaluateNF+ , evaluateNF_+ , evaluateWHNF+ , evaluateWHNF_+ ) where+ import Control.DeepSeq (NFData (..), deepseq, force, ($!!)) -import Relude.Base (seq)+import Relude.Base (IO, seq) import Relude.Function ((.)) import Relude.Monad (MonadIO, liftIO, (<$!>)) import qualified Control.Exception.Base (evaluate) --- | Lifted alias for 'Control.Exception.Base.evaluate' with clearer name.++-- $setup+-- >>> import Relude++{- | Lifted alias for 'Control.Exception.Base.evaluate' with a clearer name.++>>> let list = [1..5] :: [Int]+>>> :sprint list+list = _+>>> () <$ evaluateWHNF list+>>> :sprint list+list = 1 : _+-} evaluateWHNF :: MonadIO m => a -> m a evaluateWHNF = liftIO . Control.Exception.Base.evaluate+{-# INLINE evaluateWHNF #-}+{-# SPECIALIZE evaluateWHNF :: a -> IO a #-} --- | Like 'evaluateWNHF' but discards value.+{- | Like 'evaluateWHNF' but discards value.++>>> let list = [1..5] :: [Int]+>>> :sprint list+list = _+>>> evaluateWHNF_ list+>>> :sprint list+list = 1 : _+-} evaluateWHNF_ :: MonadIO m => a -> m () evaluateWHNF_ what = (`seq` ()) <$!> evaluateWHNF what+{-# INLINE evaluateWHNF_ #-}+{-# SPECIALIZE evaluateWHNF_ :: a -> IO () #-} --- | Alias for @evaluateWHNF . force@ with clearer name.+{- | Alias for @evaluateWHNF . force@ with a clearer name.++>>> let list = [1..5] :: [Int]+>>> :sprint list+list = _+>>> () <$ evaluateNF list+>>> :sprint list+list = [1,2,3,4,5]+-} evaluateNF :: (NFData a, MonadIO m) => a -> m a evaluateNF = evaluateWHNF . force+{-# INLINE evaluateNF #-}+{-# SPECIALIZE evaluateNF :: NFData a => a -> IO a #-} --- | Alias for @evaluateWHNF . rnf@. Similar to 'evaluateNF'--- but discards resulting value.+{- | Alias for @evaluateWHNF . rnf@. Similar to 'evaluateNF'+but discards the resulting value.++>>> let list = [1..5] :: [Int]+>>> :sprint list+list = _+>>> evaluateNF_ list+>>> :sprint list+list = [1,2,3,4,5]+-} evaluateNF_ :: (NFData a, MonadIO m) => a -> m () evaluateNF_ = evaluateWHNF . rnf+{-# INLINE evaluateNF_ #-}+{-# SPECIALIZE evaluateNF_ :: NFData a => a -> IO () #-}
+ src/Relude/Enum.hs view
@@ -0,0 +1,149 @@+{-# LANGUAGE Safe #-}++{- |+Module : Relude.Enum+Copyright : (c) 2021-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Reexports 'Enum' related typeclasses and functions. Also introduces a few useful+helpers to work with Enums.++__Note:__ 'universe', 'universeNonEmpty' and 'inverseMap' were previously in the+extra modules, but due to their benefit in different use cases. If you imported+@Relude.Extra.Enum@ module, you can remove it now, as these functions are+reexported in the main "Relude" module.++@since 1.0.0.0+-}++module Relude.Enum+ ( -- * Useful combinators for Enums+ universe+ , universeNonEmpty+ , inverseMap+ -- * Base reexports+ , module GHC.Enum+ ) where++import GHC.Enum (Bounded (..), Enum (..), boundedEnumFrom, boundedEnumFromThen)+import Data.List.NonEmpty (NonEmpty (..))+import Data.List (drop)+import Data.Maybe (Maybe (..))++import Relude.Base (Ord (..))+import Relude.Extra.Tuple (fmapToFst)++import qualified Data.Map.Strict as M+++-- $setup+-- >>> import Relude++{- | Returns all values of some 'Bounded' 'Enum' in ascending order.++>>> universe :: [Bool]+[False,True]++>>> universe @Ordering+[LT,EQ,GT]++>>> data TrafficLight = Red | Blue | Green deriving (Show, Enum, Bounded)+>>> universe :: [TrafficLight]+[Red,Blue,Green]++>>> data Singleton = Singleton deriving (Show, Enum, Bounded)+>>> universe @Singleton+[Singleton]++@since 0.1.0+-}+universe :: (Bounded a, Enum a) => [a]+universe = [minBound .. maxBound]+{-# INLINE universe #-}++{- | Like 'universe', but returns 'NonEmpty' list of some enumeration++>>> universeNonEmpty :: NonEmpty Bool+False :| [True]++>>> universeNonEmpty @Ordering+LT :| [EQ,GT]++>>> data TrafficLight = Red | Blue | Green deriving (Show, Eq, Enum, Bounded)+>>> universeNonEmpty :: NonEmpty TrafficLight+Red :| [Blue,Green]++>>> data Singleton = Singleton deriving (Show, Eq, Enum, Bounded)+>>> universeNonEmpty @Singleton+Singleton :| []++@since 0.7.0.0+-}+universeNonEmpty :: forall a . (Bounded a, Enum a) => NonEmpty a+universeNonEmpty = minBound :| drop 1 universe+{-# INLINE universeNonEmpty #-}++{- | @inverseMap f@ creates a function that is the inverse of a given function+@f@. It does so by constructing 'M.Map' internally for each value @f a@. The+implementation makes sure that the 'M.Map' is constructed only once and then+shared for every call.++__Memory usage note:__ don't inverse functions that have types like 'Int'+as their input. In this case the created 'M.Map' will have huge size.++The complexity of reversed mapping is \(\mathcal{O}(\log n)\).++__Performance note:__ make sure to specialize monomorphic type of your functions+that use 'inverseMap' to avoid 'M.Map' reconstruction.++One of the common 'inverseMap' use-case is inverting the 'show' or a 'show'-like+function.++>>> data Color = Red | Green | Blue deriving (Show, Enum, Bounded)+>>> parse = inverseMap show :: String -> Maybe Color+>>> parse "Red"+Just Red+>>> parse "Black"+Nothing++__Correctness note:__ 'inverseMap' expects /injective function/ as its argument,+i.e. the function must map distinct arguments to distinct values.++Typical usage of this function looks like this:++@+__data__ GhcVer+ = Ghc802+ | Ghc822+ | Ghc844+ | Ghc865+ | Ghc881+ __deriving__ ('Eq', 'Ord', 'Show', 'Enum', 'Bounded')++showGhcVer :: GhcVer -> 'Text'+showGhcVer = \\__case__+ Ghc802 -> "8.0.2"+ Ghc822 -> "8.2.2"+ Ghc844 -> "8.4.4"+ Ghc865 -> "8.6.5"+ Ghc881 -> "8.8.1"++parseGhcVer :: 'Text' -> 'Maybe' GhcVer+parseGhcVer = 'inverseMap' showGhcVer+@++@since 0.1.1+-}+inverseMap+ :: forall a k .+ (Bounded a, Enum a, Ord k)+ => (a -> k)+ -> (k -> Maybe a)+inverseMap f = \k -> M.lookup k dict+ where+ dict :: M.Map k a+ dict = M.fromList (fmapToFst f (universe @a))+{-# INLINE inverseMap #-}
src/Relude/Exception.hs view
@@ -1,25 +1,33 @@+{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE Safe #-} {-# LANGUAGE ViewPatterns #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Exception+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Re-exports most useful functionality from 'safe-exceptions'. Also--- provides some functions to work with exceptions over 'MonadError'.+Re-exports most useful functionality from the "Control.Exception" module. Also+provides some convenient utilities to throw and handle exceptions.+-} module Relude.Exception- ( module Control.Exception+ ( -- * "Control.Exception" reexports+ Exception (..)+ , SomeException (..) - , Bug (..)- , bug- , pattern Exc- ) where+ -- * 'Bug's+ , Bug (..)+ , bug+ , pattern Exc+ ) where import Control.Exception (Exception (..), SomeException (..)) import Data.List ((++))@@ -31,28 +39,33 @@ import qualified Control.Exception as E (displayException, throw, toException) --- | Type that represents exceptions used in cases when a particular codepath--- is not meant to be ever executed, but happens to be executed anyway.++{- | Type that represents exceptions used in cases when a particular codepath is+not meant to be ever executed, but happens to be executed anyway.+-} data Bug = Bug SomeException CallStack- deriving (Show)+ deriving stock (Show) instance Exception Bug where- displayException (Bug e cStack) = E.displayException e ++ "\n"- ++ prettyCallStack cStack+ displayException (Bug e cStack) =+ E.displayException e ++ "\n"+ ++ prettyCallStack cStack -- | Generate a pure value which, when forced, will throw the given exception impureThrow :: Exception e => e -> a impureThrow = E.throw . E.toException --- | Generate a pure value which, when forced, will synchronously--- throw the exception wrapped into 'Bug' data type.+{- | Generate a pure value which, when forced, will synchronously+throw the exception wrapped into 'Bug' data type.+-} bug :: (HasCallStack, Exception e) => e -> a bug e = impureThrow (Bug (E.toException e) callStack) -{- | Pattern synonym to easy pattern matching on exceptions. So intead of+{- | Pattern synonym to easy pattern matching on exceptions. So instead of writing something like this: @+isNonCriticalExc :: SomeException -> Bool isNonCriticalExc e | Just (_ :: NodeAttackedError) <- fromException e = True | Just DialogUnexpected{} <- fromException e = True@@ -62,8 +75,9 @@ you can use 'Exc' pattern synonym: @+isNonCriticalExc :: SomeException -> Bool isNonCriticalExc = \case- Exc (_ :: NodeAttackedError) -> True -- matching all exceptions of type 'NodeAttackedError'+ Exc (_ :: NodeAttackedError) -> True -- matching all exceptions of type NodeAttackedError Exc DialogUnexpected{} -> True _ -> False @
+ src/Relude/Extra.hs view
@@ -0,0 +1,98 @@+{- |+Module : Relude.Extra+Copyright : (c) 2019-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++This module exports all extra-related stuff.+The extra modules are not exported by default, but you can easily bring them to+every module in your package by modifying your "Prelude" file.+-}++module Relude.Extra+ ( module Relude.Extra.Bifunctor+ -- $Bifunctor+ , module Relude.Extra.CallStack+ -- $CallStack+ , module Relude.Extra.Enum+ -- $Enum+ , module Relude.Extra.Foldable+ -- $Foldable+ , module Relude.Extra.Foldable1+ -- $Foldable1+ , module Relude.Extra.Group+ -- $Group+ , module Relude.Extra.Lens+ -- $Lens+ , module Relude.Extra.Map+ -- $Map+ , module Relude.Extra.Newtype+ -- $Newtype+ , module Relude.Extra.Tuple+ -- $Tuple+ , module Relude.Extra.Type+ -- $Type+ ) where++import Relude.Extra.Bifunctor+import Relude.Extra.CallStack+import Relude.Extra.Enum+import Relude.Extra.Foldable+import Relude.Extra.Foldable1+import Relude.Extra.Group+import Relude.Extra.Lens+import Relude.Extra.Map+import Relude.Extra.Newtype+import Relude.Extra.Tuple+import Relude.Extra.Type++{- $Bifunctor+Additional combinators for t'Relude.Functor.Reexport.Bifunctor'.+-}++{- $CallStack+Useful functions to extract information from t'Relude.Base.CallStack'.+-}+{- $Enum+Extra utilities for types that implement t'Relude.Enum.Bounded' and t'Relude.Enum.Enum'+constraints.+-}+{- $Foldable+Extra folds for instances of the t'Relude.Foldable.Reexport.Foldable' typeclass.+Currently, just a short-circuitable left fold 'foldlSC'.+-}++{- $Foldable1+'Foldable1' is a typeclass like t'Relude.Foldable.Reexport.Foldable' but for non-empty+structures. For example, t'Relude.List.NonEmpty.NonEmpty',+'Relude.Functor.Reexport.Identity'.++'Foldable1' has all type-safe and total methods like `head1`, `maximum1` in+contradiction with t'Relude.Foldable.Reexport.Foldable'.+-}++{- $Group+Grouping functions, polymorphic on return @Map@ type.+-}++{- $Lens+Minimal implementation of @lens@ package required for basic usage.+-}++{- $Map+The typeclass for @Map@-like data structures.+-}++{- $Newtype+Generic functions that automatically work for any @newtype@.+-}++{- $Tuple+Functions for working with tuples.+-}++{- $Type+Functions for inspecting and working with types.+-}
src/Relude/Extra/Bifunctor.hs view
@@ -1,16 +1,81 @@+{-# LANGUAGE Safe #-}++{- |+Module : Relude.Extra.Bifunctor+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Useful combinators for bifunctors inside functors. This set of functions is+useful when you want to work with types like these ones:++@+foo :: IO (Either a b)+bar :: IO (a, b)++baz :: Maybe (Either a b)+qux :: Maybe (a, b)++doo :: (a, a)+dee :: Either a a+@++@since 0.1.0+-}+ module Relude.Extra.Bifunctor- ( bimapF- , firstF- , secondF- ) where+ ( bimapBoth+ , bimapF+ , firstF+ , secondF+ ) where import Relude +{- | Maps a function over both elements of a bifunctor.++>>> bimapBoth length ([True], [False, True])+(1,2)+>>> map (bimapBoth not) [Left True, Right False]+[Left False,Right True]++@since 0.6.0.0+-}+bimapBoth :: Bifunctor f => (a -> b) -> f a a -> f b b+bimapBoth f = bimap f f+{-# INLINE bimapBoth #-}++{- | Fmaps functions for nested bifunctor. Short for @fmap (bimap f g)@.++>>> bimapF not length $ Just (False, ['a', 'b'])+Just (True,2)++@since 0.1.0+-} bimapF :: (Functor f, Bifunctor p) => (a -> c) -> (b -> d) -> f (p a b) -> f (p c d) bimapF f g = fmap (bimap f g)+{-# INLINE bimapF #-} +{- | Short for @fmap . first@.++>>> firstF not $ Just (False, ['a', 'b'])+Just (True,"ab")++@since 0.1.0+-} firstF :: (Functor f, Bifunctor p) => (a -> c) -> f (p a b) -> f (p c b) firstF = fmap . first+{-# INLINE firstF #-} +{- | Short for @fmap . second@.++>>> secondF length $ Just (False, ['a', 'b'])+Just (False,2)++@since 0.1.0+-} secondF :: (Functor f, Bifunctor p) => (b -> d) -> f (p a b) -> f (p a d) secondF = fmap . second+{-# INLINE secondF #-}
+ src/Relude/Extra/CallStack.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE Safe #-}++{- |+Module : Relude.Extra.CallStack+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Contains useful functions to work with GHC callstack.++@since 0.2.0+-}++module Relude.Extra.CallStack+ ( ownName+ , callerName+ ) where++import Relude++{- | This function returns the name of its caller function, but it requires+that the caller function has 'HasCallStack' constraint. Otherwise, it returns+@"<unknown>"@.++>>> foo :: HasCallStack => String; foo = ownName+>>> foo+"foo"+>>> bar :: HasCallStack => String; bar = foo+>>> bar+"foo"++@since 0.2.0+-}+ownName :: HasCallStack => String+ownName = case getCallStack callStack of+ _:caller:_ -> fst caller+ _ -> "<unknown>"++{- | This function returns the name of its caller of the caller function, but it+requires that the caller function and caller of the caller function have+'HasCallStack' constraint. Otherwise, it returns @"<unknown>"@. It's useful for+logging:++>>> log :: HasCallStack => String -> IO (); log s = putStrLn $ callerName ++ ":" ++ s+>>> greeting :: HasCallStack => IO (); greeting = log "Starting..." >> putStrLn "Hello!" >> log "Ending..."+>>> greeting+greeting:Starting...+Hello!+greeting:Ending...++@since 0.2.0+-}+callerName :: HasCallStack => String+callerName = case getCallStack callStack of+ _:_:caller:_ -> fst caller+ _ -> "<unknown>"
src/Relude/Extra/Enum.hs view
@@ -1,51 +1,26 @@-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}--module Relude.Extra.Enum- ( universe- , inverseMap- , next- , safeToEnum- ) where--import Relude--import qualified Data.Map.Strict as M+{-# LANGUAGE Safe #-} --- $setup--- >>> :set -XTypeApplications+{- |+Module : Relude.Extra.Enum+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable -{- | Returns all values of some 'Bounded' 'Enum' in ascending order.+Mini @bounded-enum@ framework inside @relude@. ->>> data TrafficLight = Red | Blue | Green deriving (Show, Enum, Bounded)->>> universe :: [TrafficLight]-[Red,Blue,Green]->>> universe :: [Bool]-[False,True]+@since 0.1.0 -}-universe :: (Bounded a, Enum a) => [a]-universe = [minBound .. maxBound] -{- | Creates a function that is the inverse of a given function @f@.+module Relude.Extra.Enum+ ( next+ , prev+ , safeToEnum+ ) where ->>> data Color = Red | Green | Blue deriving (Show, Enum, Bounded)->>> parse = inverseMap show :: String -> Maybe Color->>> parse "Red"-Just Red->>> parse "Black"-Nothing--}-inverseMap :: forall a k. (Bounded a, Enum a, Ord k)- => (a -> k)- -> k- -> Maybe a-inverseMap f = \x -> M.lookup x dict- where- dict :: M.Map k a- dict = M.fromList $ zip (map f univ) univ+import Relude - univ :: [a]- univ = universe {- | Like 'succ', but doesn't fail on 'maxBound'. Instead it returns 'minBound'. @@ -56,12 +31,31 @@ >>> succ True *** Exception: Prelude.Enum.Bool.succ: bad argument +@since 0.1.0 -}-next :: (Eq a, Bounded a, Enum a) => a -> a+next :: (Eq a, Bounded a, Enum a) => a -> a next e | e == maxBound = minBound | otherwise = succ e+{-# INLINE next #-} +{- | Like 'pred', but doesn't fail on 'minBound'. Instead it returns 'maxBound'.++>>> prev True+False+>>> prev False+True+>>> pred False+*** Exception: Prelude.Enum.Bool.pred: bad argument++@since 0.6.0.0+-}+prev :: (Eq a, Bounded a, Enum a) => a -> a+prev e+ | e == minBound = maxBound+ | otherwise = pred e+{-# INLINE prev #-}+ {- | Returns 'Nothing' if given 'Int' outside range. >>> safeToEnum @Bool 0@@ -72,6 +66,9 @@ Nothing >>> safeToEnum @Bool (-1) Nothing++@since 0.1.0 -} safeToEnum :: forall a . (Bounded a, Enum a) => Int -> Maybe a-safeToEnum i = guard (fromEnum @a minBound <= i && i <= fromEnum @a maxBound) *> Just (toEnum i)+safeToEnum i = guard (fromEnum @a minBound <= i && i <= fromEnum @a maxBound) $> toEnum i+{-# INLINE safeToEnum #-}
+ src/Relude/Extra/Foldable.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE Safe #-}+{-# LANGUAGE BangPatterns #-}++{- |+Module : Relude.Extra.Foldable+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Contains utility functions for working with tuples.++@since 0.6.0.0+-}++module Relude.Extra.Foldable+ ( foldlSC+ , average+ ) where++import Relude+++-- $setup++{- | A left-associative fold that's tail-recursive but can still short-circuit.+Returning a 'Left' short-circuits and immediately returns the value inside.+Returning a 'Right' continues the fold as usual with the value inside.++>>> foldlSC (\acc x -> if x == 0 then Left 0 else Right $! acc * x) 1 [1..6]+720+>>> foldlSC (\acc x -> if x == 0 then Left 0 else Right $! acc * x) 1 (0:error "Short-circuiting should keep this from happening")+0++@since 0.6.0.0+-}+foldlSC :: forall t b a. Foldable t => (b -> a -> Either b b) -> b -> t a -> b+foldlSC f = flip $ foldr go id+ where+ go :: a -> (b -> b) -> b -> b+ go x k z = case f z x of+ Left l -> l+ Right r -> k r+{-# INLINE foldlSC #-}++{- | Given a 'Foldable' of 'Fractional' elements, computes the average if+possible and returns 'Maybe' element.++>>> average [42]+Just 42.0+>>> average @Double [1, 2, 3, 4]+Just 2.5+>>> average @Float [1.5, 2.5, 3 ,4]+Just 2.75+>>> average []+Nothing++@since 1.0.0.0+-}+average :: forall a f . (Foldable f, Fractional a) => f a -> Maybe a+average xs+ | null xs = Nothing+ | otherwise = Just+ . uncurry (/)+ . foldl' (\(!total, !count) x -> (total + x, count + 1)) (0,0)+ $ xs+{-# INLINE average #-}
+ src/Relude/Extra/Foldable1.hs view
@@ -0,0 +1,445 @@+{-# OPTIONS_GHC -Wno-redundant-constraints #-}++{-# LANGUAGE CPP #-}++#if __GLASGOW_HASKELL__ >= 902+{-# OPTIONS_GHC -Wno-operator-whitespace #-}+#endif++{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++{- |+Module : Relude.Extra.Foldable1+Copyright : (c) 2011-2015 Edward Kmett+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++'Foldable1' is a typeclass like 'Data.Foldable.Foldable' but for non-empty+structures. For example, 'Relude.NonEmpty', 'Relude.Identity'.++'Foldable1' has all type-safe and total methods like `head1`, `maximum1` in+contradiction with 'Data.Foldable.Foldable'.++@since 0.3.0+-}++module Relude.Extra.Foldable1+ ( Foldable1 (..)+ , foldl1'+ , average1+ ) where++import Relude hiding (Product (..), Sum (..))+import Relude.Extra.Newtype ((#.))++import Data.Functor.Product (Product (..))+import Data.Functor.Sum (Sum (..))+import GHC.TypeLits (ErrorMessage (..), TypeError)++import qualified Data.Semigroup as SG+++-- $setup+-- >>> import Relude++{- | The class of foldable data structures that cannot be empty.++@since 0.3.0+-}+class Foldable f => Foldable1 f where+ {-# MINIMAL foldMap1 #-}++ {- | Map each element of the non-empty structure to a semigroup, and combine the results.++ >>> foldMap1 SG.Sum (1 :| [2, 3, 4])+ Sum {getSum = 10}+ >>> foldMap1 show (123 :| [456, 789, 0])+ "1234567890"+ -}+ foldMap1 :: Semigroup m => (a -> m) -> f a -> m++ {- | Combine the elements of a non-empty structure using a semigroup.++ >>> fold1 (1 :| [2, 3, 4 :: SG.Sum Int])+ Sum {getSum = 10}+ >>> fold1 (4 :| [5, 10 :: SG.Product Int])+ Product {getProduct = 200}+ -}+ fold1 :: Semigroup m => f m -> m+ fold1 = foldMap1 id++ {- | Combines the elements of a non-empty structure using a binary function @f@.++ >>> foldr1 (+) 0 (1 :| [2, 3])+ 6+ >>> foldr1 (+) 1 $ Identity 3+ 4++ @since 1.0.0.0+ -}+ foldr1 :: (a -> b -> b) -> b -> f a -> b+ foldr1 f accum as = appEndo (foldMap1 (Endo #. f) as) accum+ {-# INLINE foldr1 #-}++ {- | Convert a non-empty data structure to a NonEmpty list.++ >>> toNonEmpty (Identity 2)+ 2 :| []+ -}+ toNonEmpty :: f a -> NonEmpty a+ toNonEmpty = foldMap1 (:| [])++ {- | The first element of a non-empty data structure.++ >>> head1 (1 :| [2, 3, 4])+ 1+ -}+ head1 :: f a -> a+ head1 = SG.getFirst #. foldMap1 SG.First++ {- | The last element of a non-empty data structure.++ >>> last1 (1 :| [2, 3, 4])+ 4+ -}+ last1 :: f a -> a+ last1 = SG.getLast #. foldMap1 SG.Last++ {- | The largest element of a non-empty data structure.++ >>> maximum1 (32 :| [64, 8, 128, 16])+ 128+ -}+ maximum1 :: Ord a => f a -> a+ maximum1 = SG.getMax #. foldMap1 SG.Max++ {- | The smallest element of a non-empty data structure.++ >>> minimum1 (32 :| [64, 8, 128, 16])+ 8+ -}+ minimum1 :: Ord a => f a -> a+ minimum1 = SG.getMin #. foldMap1 SG.Min++ {- | The largest element of a non-empty data structure+ with respect to the given comparison function.++ >>> maximumOn1 abs (0 :| [2, 1, -3, -2])+ -3++ @since 1.0.0.0+ -}+ maximumOn1 :: Ord b => (a -> b) -> f a -> a+ maximumOn1 f = maximumOn1 f . toNonEmpty+ {-# INLINE maximumOn1 #-}++ {- | The smallest element of a non-empty data structure+ with respect to the given comparison function.++ >>> minimumOn1 abs (0 :| [2, 1, -3, -2])+ 0++ @since 1.0.0.0+ -}+ minimumOn1 :: Ord b => (a -> b) -> f a -> a+ minimumOn1 f = minimumOn1 f . toNonEmpty+ {-# INLINE minimumOn1 #-}++{- |++@since 0.3.0+-}+instance Foldable1 NonEmpty where+ fold1 :: Semigroup m => NonEmpty m -> m+ fold1 = sconcat+ {-# INLINE fold1 #-}++ foldMap1 :: forall m a . Semigroup m => (a -> m) -> NonEmpty a -> m+ foldMap1 f (a :| as) = foldr go f as a+ where+ go :: a -> (a -> m) -> a -> m+ go b g x = f x <> g b+ {-# INLINE foldMap1 #-}++ toNonEmpty :: NonEmpty a -> NonEmpty a+ toNonEmpty = id+ {-# INLINE toNonEmpty #-}++ head1, last1 :: NonEmpty a -> a+ head1 = head+ last1 = last+ {-# INLINE head1 #-}+ {-# INLINE last1 #-}++ maximum1, minimum1 :: Ord a => NonEmpty a -> a+ maximum1 = foldl1' max+ minimum1 = foldl1' min+ {-# INLINE maximum1 #-}+ {-# INLINE minimum1 #-}++ maximumOn1 :: forall a b. Ord b => (a -> b) -> NonEmpty a -> a+ maximumOn1 func = foldl1' $ cmpOn+ where+ cmpOn :: a -> a -> a+ cmpOn a b = case func a `compare` func b of+ GT -> a+ _ -> b+ {-# INLINE maximumOn1 #-}++ minimumOn1 :: forall a b. Ord b => (a -> b) -> NonEmpty a -> a+ minimumOn1 func = foldl1' $ cmpOn+ where+ cmpOn :: a -> a -> a+ cmpOn a b = case func a `compare` func b of+ LT -> a+ _ -> b+ {-# INLINE minimumOn1 #-}++{- |++@since 0.3.0+-}+instance Foldable1 Identity where+ foldMap1 :: Semigroup m => (a -> m) -> Identity a -> m+ foldMap1 = coerce+ {-# INLINE foldMap1 #-}++ fold1 :: Semigroup m => Identity m -> m+ fold1 = coerce+ {-# INLINE fold1 #-}++ toNonEmpty :: Identity a -> NonEmpty a+ toNonEmpty = (:| []) . coerce+ {-# INLINE toNonEmpty #-}++ head1 :: Identity a -> a+ head1 = coerce+ {-# INLINE head1 #-}++ last1 :: Identity a -> a+ last1 = coerce+ {-# INLINE last1 #-}++ maximum1 :: Ord a => Identity a -> a+ maximum1 = coerce+ {-# INLINE maximum1 #-}++ minimum1 :: Ord a => Identity a -> a+ minimum1 = coerce+ {-# INLINE minimum1 #-}++ maximumOn1 :: Ord b => (a -> b) -> Identity a -> a+ maximumOn1 = const coerce+ {-# INLINE maximumOn1 #-}++ minimumOn1 :: Ord b => (a -> b) -> Identity a -> a+ minimumOn1 = const coerce+ {-# INLINE minimumOn1 #-}++{- |++@since 0.3.0+-}+instance Foldable1 ((,) c) where+ foldMap1 :: Semigroup m => (a -> m) -> (c, a) -> m+ foldMap1 f = f . snd+ {-# INLINE foldMap1 #-}++ fold1 :: Semigroup m => (c, m) -> m+ fold1 = snd+ {-# INLINE fold1 #-}++ toNonEmpty :: (c, a) -> NonEmpty a+ toNonEmpty (_, y) = y :| []+ {-# INLINE toNonEmpty #-}++ head1, last1 :: (c, a) -> a+ head1 = snd+ last1 = snd+ {-# INLINE head1 #-}+ {-# INLINE last1 #-}++ maximum1, minimum1 :: Ord a => (c, a) -> a+ maximum1 = snd+ minimum1 = snd+ {-# INLINE maximum1 #-}+ {-# INLINE minimum1 #-}++ maximumOn1, minimumOn1 :: Ord b => (a -> b) -> (c, a) -> a+ maximumOn1 = const snd+ minimumOn1 = const snd+ {-# INLINE maximumOn1 #-}+ {-# INLINE minimumOn1 #-}++{- |++@since 0.3.0+-}+instance (Foldable1 f, Foldable1 g) => Foldable1 (Compose f g) where+ foldMap1 :: Semigroup m => (a -> m) -> Compose f g a -> m+ foldMap1 f = foldMap1 (foldMap1 f) . getCompose+ {-# INLINE foldMap1 #-}++ head1 :: Compose f g a -> a+ head1 = head1 . head1 . getCompose+ {-# INLINE head1 #-}++ last1 :: Compose f g a -> a+ last1 = last1 . last1 . getCompose+ {-# INLINE last1 #-}++{- |++@since 0.3.0+-}+instance (Foldable1 f, Foldable1 g) => Foldable1 (Product f g) where+ foldMap1 :: Semigroup m => (a -> m) -> Product f g a -> m+ foldMap1 f (Pair a b) = foldMap1 f a <> foldMap1 f b+ {-# INLINE foldMap1 #-}++{- |++@since 0.3.0+-}+instance (Foldable1 f, Foldable1 g) => Foldable1 (Sum f g) where+ foldMap1 :: Semigroup m => (a -> m) -> Sum f g a -> m+ foldMap1 f (InL x) = foldMap1 f x+ foldMap1 f (InR y) = foldMap1 f y+ {-# INLINE foldMap1 #-}+++----------------------------------------------------------------------------+-- List custom error+----------------------------------------------------------------------------++-- | For tracking usage of ordinary list with 'Foldable1' functions.+type family IsListError :: Constraint+ where+ IsListError = TypeError+ ( 'Text "The methods of the 'Foldable1' type class work with non-empty containers."+ ':$$: 'Text "However, one of the 'Foldable1' functions is applied to the List."+ ':$$: 'Text ""+ ':$$: 'Text "Possible fixes:"+ ':$$: 'Text " * Replace []"+ ':$$: 'Text " with one of the: 'NonEmpty', 'Identity', '(c,)', 'Compose f g', 'Product f g', 'Sum f g'"+ ':$$: 'Text " * Or use 'Foldable' class for your own risk."+ )+++{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.++'Foldable1' is not supposed to be used with the lists.++In case it is used by mistake, the user will see the following:++>>> head1 [1, 2, 3]+...+... The methods of the 'Foldable1' type class work with non-empty containers.+ However, one of the 'Foldable1' functions is applied to the List.+...+ Possible fixes:+ * Replace []+ with one of the: 'NonEmpty', 'Identity', '(c,)', 'Compose f g', 'Product f g', 'Sum f g'+ * Or use 'Foldable' class for your own risk.+...++@since 0.6.0.0+-}+instance IsListError => Foldable1 [] where+ foldMap1 :: Semigroup m => (a -> m) -> [a] -> m+ foldMap1 _ _ = error "Unreachable list instance of Foldable1"++ foldr1 :: (Foldable1 f) => (a -> b -> b) -> b -> f a -> b+ foldr1 _ _ = error "Unreachable list instance of Foldable1"++ fold1 :: Semigroup m => [m] -> m+ fold1 _ = error "Unreachable list instance of Foldable1"++ toNonEmpty :: [a] -> NonEmpty a+ toNonEmpty _ = error "Unreachable list instance of Foldable1"++ head1 :: [a] -> a+ head1 _ = error "Unreachable list instance of Foldable1"++ last1 :: [a] -> a+ last1 _ = error "Unreachable list instance of Foldable1"++ maximum1 :: Ord a => [a] -> a+ maximum1 _ = error "Unreachable list instance of Foldable1"++ minimum1 :: Ord a => [a] -> a+ minimum1 _ = error "Unreachable list instance of Foldable1"++ maximumOn1 :: (Ord b, Foldable1 f) => (a -> b) -> f a -> a+ maximumOn1 _ _ = error "Unreachable list instance of Foldable1"++ minimumOn1 :: (Ord b, Foldable1 f) => (a -> b) -> f a -> a+ minimumOn1 _ _ = error "Unreachable list instance of Foldable1"++{- | Strictly folds non-empty structure with given function @f@:++@+foldl1' f [x0, x1, x2 ...] = f (f x0 x1) x2 ...+@++>>> foldl1' (++) ([1,2] :| [[3,4], [5,6]])+[1,2,3,4,5,6]++@since 0.3.0+-}+foldl1' :: (a -> a -> a) -> NonEmpty a -> a+foldl1' _ (x :| []) = x+foldl1' f (x :| (y:ys)) = foldl' f (f x y) ys+{-# INLINE foldl1' #-}++{- | Given a 'Foldable1' of 'Fractional' elements, computes the average if+possible and returns the resulting element.+++>>> average1 (42 :| [])+42.0+>>> average1 (1 :| [2,3,4])+2.5++@since 1.0.0.0+-}+average1 :: forall a f . (Foldable1 f, Fractional a) => f a -> a+average1 = uncurry (/) . foldl' (\(!total, !count) x -> (total + x, count + 1)) (0, 0)+{-# INLINE average1 #-}++{-+Copyright 2011-2015 Edward Kmett++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++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.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.+-}
src/Relude/Extra/Group.hs view
@@ -1,26 +1,37 @@-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} -{--Copyright: (c) 2017-2018 Serokelll- (c) 2018 Kowainik-License: MIT+{- |+Module : Relude.Extra.Group+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Polymorphic grouping functions.++@since 0.1.0 -} module Relude.Extra.Group- ( groupBy- , groupOneBy- ) where+ ( groupBy+ , groupOneBy+ ) where import Relude import Relude.Extra.Map import Data.List.NonEmpty ((<|)) + {- | Groups elements using results of the given function as keys. >>> groupBy even [1..6] :: HashMap Bool (NonEmpty Int) fromList [(False,5 :| [3,1]),(True,6 :| [4,2])]++@since 0.1.0 -} groupBy :: forall f t a . (Foldable f, DynamicMap t, Val t ~ NonEmpty a, Monoid t) => (a -> Key t) -> f a -> t@@ -37,6 +48,8 @@ >>> groupOneBy even [1 .. 6] :: HashMap Bool Int fromList [(False,1),(True,2)]++@since 0.1.0 -} groupOneBy :: forall f t a . (Foldable f, DynamicMap t, Val t ~ a, Monoid t) => (a -> Key t) -> f a -> t
+ src/Relude/Extra/Lens.hs view
@@ -0,0 +1,276 @@+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE Safe #-}++{- |+Module : Relude.Extra.Lens+Copyright : (c) 2012-2016 Edward Kmett+ (c) 2019-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++@since 0.5.0++This module aims to provide a minimal implementation of @lens@ package required+for basic usage. All functions are compatible with the real @lens@ package+therefore if you need to expand to the full version the process should be+straightforward.++Main ideas implemented in this module are described in the following blog post:++* [Write yourself a lens (by Veronika Romashkina)](https://vrom911.github.io/blog/write-yourself-a-lens)++== Usage++To use lenses in your project, you don't need to add any other dependency rather+than @relude@. You should add the import of this module in the place of lenses+usage:++@+__import__ Relude.Extra.Lens+@++== Example++To understand better how to use this module lets look at some simple example.+Let's say we have the user data type in our system:++@+__data__ User = User+ { userName :: 'Text'+ , userAge :: 'Int'+ , userAddress :: Address+ } __deriving__ ('Show')++__data__ Address = Address+ { addressCountry :: 'Text'+ , addressCity :: 'Text'+ , addressIndex :: 'Text'+ } __deriving__ ('Show')+@++To create the lens for the @userName@ field we can use 'lens' function and manually writing getter and setter function:++@+nameL :: 'Lens'' User 'Text'+nameL = 'lens' getter setter+ __where__+ getter :: User -> 'Text'+ getter = userName++ setter :: User -> 'Text' -> User+ setter user newName = user {userName = newName}+@++In this manner, we can create other lenses for our User data type.++@+ageL :: 'Lens'' User 'Int'+addressL :: 'Lens'' User Address+countryL :: 'Lens'' User 'Text'+cityL :: 'Lens'' User 'Text'+indexL :: 'Lens'' User 'Text'+@++/Note:/ here we are using composition of the lenses for @userAddress@ field. If we have++@+addressCityL :: 'Lens'' Address 'Text'+@++then++@+cityL = addressL . addressCityL+@++Let's say we have some sample user++@+user :: User+user = User+ { userName = \"John\"+ , userAge = 42+ , userAddress = Address+ { addressCountry = \"UK\"+ , addressCity = \"London\"+ , addressIndex = \"XXX\"+ }+ }+@++To view the fields of the User data type we can use 'view' or '^.'++@+__>>>__ 'view' ageL user+42+__>>>__ user '^.' cityL+\"London\"+@++If we want to change any of the user's data, we should use 'set' or '.~'++@+__>>>__ 'set' nameL \"Johnny\" user+__>>>__ user '&' indexL '.~' \"YYY\"+@++'over' or '%~' operator could be useful when, for example, you want to increase the age by one on the user's birthday:++@+__>>>__ 'over' ageL 'succ' user+__>>>__ user '&' ageL '%~' 'succ'+@++== Migration++This module is not supposed to be the replacement for the @lens@ package. One of+the reasons why one would want to migrate to @lens@ or @microlens@ is that the+functional in @relude@ is limited to just vital lens functions.++To migrate to @lens@ or @microlens@ package add the required library to the+dependencies list in the @.cabal@ file and replace the import from @relude@+library++@+__import__ Relude.Extra.Lens+@++to the one of this correspondingly:++- @lens@:++ @+ __import__ Control.Lens+ @++- @microlens@:++ @+ __import__ Lens.Micro+ @++And that's all! No need to change the types or implementation of the functions+you used @Relude.Extra.Lens@ in.++== Links++- [lens](https://hackage.haskell.org/package/lens)+- [microlens](https://hackage.haskell.org/package/microlens)+- [lens tutorial](http://hackage.haskell.org/package/lens-tutorial-1.0.3/docs/Control-Lens-Tutorial.html)+-}++module Relude.Extra.Lens+ ( Lens'+ , lens+ , view+ , set+ , over+ , (^.)+ , (.~)+ , (%~)+ ) where++import Relude+++{- | The monomorphic lenses which don't change the type of the container (or of+the value inside). It has a 'Functor' constraint, and since both 'Const' and+'Identity' are functors, it can be used whenever a getter or a setter is needed.++ * @a@ is the type of the value inside of structure+ * @s@ is the type of the whole structure++@since 0.5.0+-}+type Lens' s a = forall f. Functor f => (a -> f a) -> s -> f s++{- | Creates 'Lens'' from the getter and setter.++@since 0.5.0+-}+lens :: (s -> a) -> (s -> a -> s) -> Lens' s a+lens getter setter = \f s -> setter s <$> f (getter s)+{-# INLINE lens #-}++{- | Gets a value out of a structure using a getter.++@since 0.5.0+-}+view :: Lens' s a -> s -> a+view l = getConst . l Const+{-# INLINE view #-}++{- | Sets the given value to the structure using a setter.++@since 0.5.0+-}+set :: Lens' s a -> a -> s -> s+set l a = runIdentity . l (const (Identity a))+{-# INLINE set #-}++{- | Applies the given function to the target.++@since 0.5.0+-}+over :: Lens' s a -> (a -> a) -> s -> s+over l fa = runIdentity . l (Identity . fa)+{-# INLINE over #-}++{- | The operator form of 'view' with the arguments flipped.++@since 0.5.0+-}+infixl 8 ^.+(^.) :: s -> Lens' s a -> a+s ^. l = view l s+{-# INLINE (^.) #-}++{- | The operator form of 'set'.++@since 0.5.0+-}+infixr 4 .~+(.~) :: Lens' s a -> a -> s -> s+(.~) = set+{-# INLINE (.~) #-}++{- | The operator form of 'over'.++@since 0.5.0+-}+infixr 4 %~+(%~) :: Lens' s a -> (a -> a) -> s -> s+(%~) = over+{-# INLINE (%~) #-}+++{-+Copyright 2012-2016 Edward Kmett++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++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.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.+-}
src/Relude/Extra/Map.hs view
@@ -1,31 +1,51 @@-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} -{- | Contains implememtation of polymorhic type classes for things like 'Set'-and 'Map'.+{- |+Module : Relude.Extra.Map+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Contains implementation of polymorphic type classes for data types 'Set' and+'Map'. -} module Relude.Extra.Map- ( StaticMap (..)- , DynamicMap (..)- , (!?)- , notMember- , lookupDefault+ ( StaticMap (..)+ , DynamicMap (..)+ , (!?)+ , notMember+ , lookupDefault - -- * To pairs- , toPairs- , keys- , elems- ) where+ -- * To pairs+ , toPairs+ , keys+ , elems+ ) where +#if __GLASGOW_HASKELL__ >= 904+import Data.Type.Equality (type (~))+#endif import GHC.Exts (IsList (Item, toList)) -import Relude.Applicative (pure, (*>))-import Relude.Base (Eq, Int, Ord, Type)+#if MIN_VERSION_hashable(1,4,0)+import Relude.Base (Ord, Type)+#else+import Relude.Base (Eq, Ord, Type)+#endif+ import Relude.Bool (Bool, guard, not) import Relude.Container.Reexport (HashMap, HashSet, Hashable, IntMap, IntSet, Map, Set, fst, snd) import Relude.Function ((.))+import Relude.Functor.Reexport (($>)) import Relude.List (map) import Relude.Monad.Reexport (Maybe (..), fromMaybe)+import Relude.Numeric (Int) import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS@@ -38,8 +58,10 @@ -- Static Map ---------------------------------------------------------------------------- -{- | Read-only map or set. Contains polymorhic functions which work for both+{- | Read-only map or set. Contains polymorphic functions which work for both sets and maps.++@since 0.1.0 -} class StaticMap t where type Key t :: Type@@ -49,85 +71,146 @@ lookup :: Key t -> t -> Maybe (Val t) member :: Key t -> t -> Bool +{- |++@since 0.1.0+-} instance Ord k => StaticMap (Map k v) where type Key (Map k v) = k type Val (Map k v) = v - size = M.size- lookup = M.lookup- member = M.member+ size = M.size {-# INLINE size #-}+ lookup = M.lookup {-# INLINE lookup #-}+ member = M.member {-# INLINE member #-} +{- |++@since 0.1.0+-}+#if MIN_VERSION_hashable(1,4,0)+instance (Hashable k) => StaticMap (HashMap k v) where+#else instance (Eq k, Hashable k) => StaticMap (HashMap k v) where+#endif type Key (HashMap k v) = k type Val (HashMap k v) = v - size = HM.size- lookup = HM.lookup- member = HM.member+ size = HM.size {-# INLINE size #-}+ lookup = HM.lookup {-# INLINE lookup #-}+ member = HM.member {-# INLINE member #-} +{- |++@since 0.1.0+-} instance StaticMap (IntMap v) where type Key (IntMap v) = Int type Val (IntMap v) = v - size = IM.size- lookup = IM.lookup- member = IM.member+ size = IM.size {-# INLINE size #-}+ lookup = IM.lookup {-# INLINE lookup #-}+ member = IM.member {-# INLINE member #-} +{- |++@since 0.1.0+-} instance Ord a => StaticMap (Set a) where type Key (Set a) = a type Val (Set a) = a - size = S.size- member = S.member- lookup k m = guard (member k m) *> pure k+ size = S.size {-# INLINE size #-}- {-# INLINE lookup #-}+ member = S.member {-# INLINE member #-}+ lookup k m = guard (member k m) $> k+ {-# INLINE lookup #-} +{- |++@since 0.1.0+-}+#if MIN_VERSION_hashable(1,4,0)+instance (Hashable a) => StaticMap (HashSet a) where+#else instance (Eq a, Hashable a) => StaticMap (HashSet a) where+#endif type Key (HashSet a) = a type Val (HashSet a) = a - size = HS.size- member = HS.member- lookup k m = guard (member k m) *> pure k+ size = HS.size {-# INLINE size #-}- {-# INLINE lookup #-}+ member = HS.member {-# INLINE member #-}+ lookup k m = guard (member k m) $> k+ {-# INLINE lookup #-} +{- |++@since 0.1.0+-} instance StaticMap IntSet where type Key IntSet = Int type Val IntSet = Int - size = IS.size- member = IS.member- lookup k m = guard (member k m) *> pure k+ size = IS.size {-# INLINE size #-}- {-# INLINE lookup #-}+ member = IS.member {-# INLINE member #-}+ lookup k m = guard (member k m) $> k+ {-# INLINE lookup #-} --- | Operator version of 'lookup' function.+{- | Operator version of 'lookup' function.++>>> let myHashMap = HashMap.fromList [('a', "xxx"), ('b', "yyy")]+>>> myHashMap !? 'b'+Just "yyy"++>>> myHashMap !? 'd'+Nothing++@since 0.1.0+-} infixl 9 !? (!?) :: StaticMap t => t -> Key t -> Maybe (Val t) (!?) m k = lookup k m {-# INLINE (!?) #-} --- | Inverse of 'member' function.+{- | Inverse of 'member' function.++>>> let myHashMap = HashMap.fromList [('a', "xxx"), ('b', "yyy")]+>>> notMember 'b' myHashMap+False++>>> notMember 'c' myHashMap+True++@since 0.1.0+-} notMember :: StaticMap t => Key t -> t -> Bool notMember k = not . member k {-# INLINE notMember #-} {- | Return the value to which the specified key is mapped, or the default value if this map contains no mapping for the key.++>>> let myHashMap = HashMap.fromList [('a', "xxx"), ('b', "yyy")]+>>> lookupDefault "zzz" 'b' myHashMap+"yyy"++>>> lookupDefault "zzz" 'c' myHashMap+"zzz"++@since 0.1.0 -} lookupDefault :: StaticMap t => Val t -- ^ Default value to return.@@ -142,6 +225,8 @@ ---------------------------------------------------------------------------- {- | Modifiable Map.++@since 0.1.0 -} class StaticMap t => DynamicMap t where -- insertions@@ -152,37 +237,50 @@ delete :: Key t -> t -> t alter :: (Maybe (Val t) -> Maybe (Val t)) -> Key t -> t -> t +{- |++@since 0.1.0+-} instance Ord k => DynamicMap (Map k v) where insert = M.insert- insertWith = M.insertWith- delete = M.delete- alter = M.alter- {-# INLINE insert #-}+ insertWith = M.insertWith {-# INLINE insertWith #-}+ delete = M.delete {-# INLINE delete #-}+ alter = M.alter {-# INLINE alter #-} +{- |++@since 0.1.0+-}+#if MIN_VERSION_hashable(1,4,0)+instance (Hashable k) => DynamicMap (HashMap k v) where+#else instance (Eq k, Hashable k) => DynamicMap (HashMap k v) where+#endif insert = HM.insert- insertWith = HM.insertWith- delete = HM.delete- alter = HM.alter- {-# INLINE insert #-}+ insertWith = HM.insertWith {-# INLINE insertWith #-}+ delete = HM.delete {-# INLINE delete #-}+ alter = HM.alter {-# INLINE alter #-} +{- |++@since 0.1.0+-} instance DynamicMap (IntMap v) where insert = IM.insert- insertWith = IM.insertWith- delete = IM.delete- alter = IM.alter- {-# INLINE insert #-}+ insertWith = IM.insertWith {-# INLINE insertWith #-}+ delete = IM.delete {-# INLINE delete #-}+ alter = IM.alter {-# INLINE alter #-} ----------------------------------------------------------------------------@@ -196,6 +294,8 @@ >>> toPairs (HashMap.fromList [('a', "xxx"), ('b', "yyy")]) [('a',"xxx"),('b',"yyy")]++@since 0.1.0 -} toPairs :: (IsList t, Item t ~ (a, b)) => t -> [(a, b)] toPairs = toList@@ -204,6 +304,8 @@ >>> keys (HashMap.fromList [('a', "xxx"), ('b', "yyy")]) "ab"++@since 0.1.0 -} keys :: (IsList t, Item t ~ (a, b)) => t -> [a] keys = map fst . toList@@ -212,6 +314,8 @@ >>> elems (HashMap.fromList [('a', "xxx"), ('b', "yyy")]) ["xxx","yyy"]++@since 0.1.0 -} elems :: (IsList t, Item t ~ (a, b)) => t -> [b] elems = map snd . toList
+ src/Relude/Extra/Newtype.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE Safe #-}++{- |+Module : Relude.Extra.Newtype+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Functions to ease work with @newtypes@.++@since 0.2.0+-}++module Relude.Extra.Newtype+ ( un+ , wrap+ , under+ , under2+ , underF2+ , (#.)+ ) where++import Relude+++-- $setup+-- >>> :set -XTypeApplications+-- >>> import Data.Semigroup (Max (..))++{- | Unwraps value from @newtype@.++>>> newtype Size = Size Int deriving Show+>>> un @Int (Size 5)+5+>>> un (Size 5) == length ['a', 'x', 'b']+False++@since 0.2.0+-}+un :: forall a n . Coercible a n => n -> a+un = coerce+{-# INLINE un #-}++{- | Wraps value to @newtype@. Behaves exactly as 'un' but has more meaningful+name in case you need to convert some value to @newtype@.++>>> newtype Flag = Flag Bool deriving (Show, Eq)+>>> wrap False == Flag True+False++@since 0.2.0+-}+wrap :: forall n a . Coercible a n => a -> n+wrap = coerce+{-# INLINE wrap #-}++{- | Applies function to the content of @newtype@. This function is not supposed+to be used on @newtype@s that are created with the help of smart constructors.++>>> newtype Foo = Foo Bool deriving Show+>>> under not (Foo True)+Foo False+>>> newtype Bar = Bar String deriving Show+>>> under (filter (== 'a')) (Bar "abacaba")+Bar "aaaa"++@since 0.2.0+-}+under :: forall n a . Coercible a n => (n -> n) -> (a -> a)+under = coerce+{-# INLINE under #-}++{- | Lift binary function for @newtype@s to work over underlying @newtype@+representation.++>>> under2 @(Sum Int) (<>) (3 :: Int) 4+7+>>> under2 @All (<>) True False+False++@since 0.3.0+-}+under2 :: forall n a . Coercible a n => (n -> n -> n) -> (a -> a -> a)+under2 = coerce+{-# INLINE under2 #-}++{- | Version of 'under2' that works on @newtype@s parametrized by their+representation. Provided for convenience.++>>> underF2 @Sum (<>) (3 :: Int) 4+7+>>> underF2 @Max (<>) 'p' 't'+'t'++@since 0.3.0+-}+underF2 :: forall n a . Coercible a (n a) => (n a -> n a -> n a) -> (a -> a -> a)+underF2 = coerce+{-# INLINE underF2 #-}++{- | Coercible composition. This function allows to write more efficient+implementations of function compositions over @newtypes@.++@since 0.3.0+-}+(#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c)+(#.) _f = coerce+{-# INLINE (#.) #-}
+ src/Relude/Extra/Tuple.hs view
@@ -0,0 +1,185 @@+{-# LANGUAGE Safe #-}+{-# LANGUAGE TupleSections #-}++{- |+Module : Relude.Extra.Tuple+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Contains utility functions for working with tuples.++@since 0.4.0+-}++module Relude.Extra.Tuple+ ( dup+ , toFst+ , toSnd+ , fmapToFst+ , fmapToSnd+ , mapToFst+ , mapToSnd+ , traverseToFst+ , traverseToSnd+ , traverseBoth+ ) where++import Relude.Function ((.))+import Relude.Functor (Functor (..), (<$>))+import Relude.Applicative (Applicative (..))+++-- $setup+-- >>> import Relude++{- | Creates a tuple by pairing something with itself.++>>> dup "foo"+("foo","foo")+>>> dup ()+((),())++@since 0.6.0.0+-}+dup :: a -> (a, a)+dup a = (a, a)+{-# INLINE dup #-}++{- | Apply a function, with the result in the fst slot,+and the value in the other.++A dual to 'toSnd'.++>>> toFst length [3, 1, 0, 2]+(4,[3,1,0,2])+>>> toFst (+5) 10+(15,10)++@since 0.7.0.0+-}+toFst :: (a -> b) -> a -> (b, a)+toFst f a = (f a, a)+{-# INLINE toFst #-}++{- | Apply a function, with the result in the snd slot,+and the value in the other.++A dual to 'toFst'.++>>> toSnd length [3, 1, 0, 2]+([3,1,0,2],4)+>>> toSnd (+5) 10+(10,15)++@since 0.7.0.0+-}+toSnd :: (a -> b) -> a -> (a, b)+toSnd f a = (a, f a)+{-# INLINE toSnd #-}++{- | Like 'fmap', but also keep the original value in the snd position.++A dual to 'fmapToSnd'.++>>> fmapToFst show [3, 10, 2]+[("3",3),("10",10),("2",2)]++@since 0.7.0.0+-}+fmapToFst :: Functor f => (a -> b) -> f a -> f (b, a)+fmapToFst = fmap . toFst+{-# INLINE fmapToFst #-}++{- | Like 'fmap', but also keep the original value in the fst position.++A dual to 'fmapToFst'.++>>> fmapToSnd show [3, 10, 2]+[(3,"3"),(10,"10"),(2,"2")]++@since 0.7.0.0+-}+fmapToSnd :: Functor f => (a -> b) -> f a -> f (a, b)+fmapToSnd = fmap . toSnd+{-# INLINE fmapToSnd #-}++{- | Apply a function, with the result in the fst slot,+and the value in the other.++A dual to 'mapToSnd'++>>> mapToFst (+1) 10+(11,10)++@since 0.4.0+-}+mapToFst :: (a -> b) -> a -> (b, a)+mapToFst = toFst+{-# INLINE mapToFst #-}+{-# DEPRECATED mapToFst "Use 'toFst' from [Tuple](\"Relude.Extra.Tuple\") instead" #-}++{- | Apply a function, with the result in the second slot,+and the value in the other.++A dual to 'mapToFst'.++>>> mapToSnd (+1) 10+(10,11)++@since 0.4.0+-}+mapToSnd :: (a -> b) -> a -> (a, b)+mapToSnd = toSnd+{-# INLINE mapToSnd #-}+{-# DEPRECATED mapToSnd "Use 'toSnd' from [Tuple](\"Relude.Extra.Tuple\") instead" #-}++{- | Apply a function that returns a value inside of a functor,+with the output in the first slot, the input in the second,+and the entire tuple inside the functor.+++A dual to 'traverseToSnd'++>>> traverseToFst (Just . (+1)) 10+Just (11,10)+>>> traverseToFst (const Nothing) 10+Nothing++@since 0.5.0+-}+traverseToFst :: Functor t => (a -> t b) -> a -> t (b, a)+traverseToFst f a = (,a) <$> f a+{-# INLINE traverseToFst #-}++{- | Apply a function that returns a value inside of a functor,+with the output in the second slot, the input in the first,+and the entire tuple inside the functor.++A dual to 'traverseToFst'.++>>> traverseToSnd (Just . (+1)) 10+Just (10,11)+>>> traverseToSnd (const Nothing) 10+Nothing++@since 0.5.0+-}+traverseToSnd :: Functor t => (a -> t b) -> a -> t (a, b)+traverseToSnd f a = (a,) <$> f a+{-# INLINE traverseToSnd #-}++{- | Maps a function that returns a value inside of+an applicative functor over both elements of a tuple,+with the entire tuple inside the applicative functor.++>>> traverseBoth (Just . ("Hello " <>)) ("Alice", "Bob")+Just ("Hello Alice","Hello Bob")+>>> traverseBoth (const Nothing) ("Alice", "Bob")+Nothing+-}+traverseBoth :: Applicative t => (a -> t b) -> (a, a) -> t (b, b)+traverseBoth f (a1, a2) = (,) <$> f a1 <*> f a2+{-# INLINE traverseBoth #-}
+ src/Relude/Extra/Type.hs view
@@ -0,0 +1,170 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++{- |+Module : Relude.Extra.Type+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Contains useful utilities to work with Types.++@since 0.4.0+-}++module Relude.Extra.Type+ ( typeName+ , type (++)+ , AllHave+ , Elem+ , Fst+ , Snd+ ) where++import Relude++import Type.Reflection (typeRep)+++#if ( __GLASGOW_HASKELL__ >= 906 )+-- $setup+-- >>> :set -XAllowAmbiguousTypes -XDataKinds -XExplicitNamespaces -XPolyKinds -XTypeFamilies -XTypeOperators -XUndecidableInstances+-- >>> :set -fprint-redundant-promotion-ticks+#else+-- $setup+-- >>> :set -XAllowAmbiguousTypes -XDataKinds -XExplicitNamespaces -XPolyKinds -XTypeFamilies -XTypeOperators -XUndecidableInstances+#endif++{- | Gets a string representation of a type.++__NOTE:__ This must be used with __TypeApplications__ language extension.++>>> typeName @()+"()"+>>> typeName @Int+"Int"+>>> typeName @String+"[Char]"+>>> typeName @(Maybe Int)+"Maybe Int"++@since 0.4.0+-}+typeName :: forall a. Typeable a => Text+typeName = show (typeRep @a)+{-# INLINE typeName #-}++{- | Concatenates type-level lists.++#if ( __GLASGOW_HASKELL__ >= 902 )++>>> :kind! '[ 'Just 5, 'Nothing] ++ '[ 'Just 3, 'Nothing, 'Just 1]+'[ 'Just 5, 'Nothing] ++ '[ 'Just 3, 'Nothing, 'Just 1] :: [Maybe+ Natural]+= '[ 'Just 5, 'Nothing, 'Just 3, 'Nothing, 'Just 1]++>>> :kind! '[] ++ '[ 'Just 3, 'Nothing, 'Just 1]+'[] ++ '[ 'Just 3, 'Nothing, 'Just 1] :: [Maybe Natural]+= '[ 'Just 3, 'Nothing, 'Just 1]++#elif ( __GLASGOW_HASKELL__ >= 806 )++>>> :kind! '[ 'Just 5, 'Nothing] ++ '[ 'Just 3, 'Nothing, 'Just 1]+'[ 'Just 5, 'Nothing] ++ '[ 'Just 3, 'Nothing, 'Just 1] :: [Maybe+ Nat]+= '[ 'Just 5, 'Nothing, 'Just 3, 'Nothing, 'Just 1]++>>> :kind! '[] ++ '[ 'Just 3, 'Nothing, 'Just 1]+'[] ++ '[ 'Just 3, 'Nothing, 'Just 1] :: [Maybe Nat]+= '[ 'Just 3, 'Nothing, 'Just 1]++#endif++@since 0.6.0.0+-}+infixr 5 +++type family (++) (xs :: [k]) (ys :: [k]) :: [k] where+ '[] ++ ys = ys+ (x ': xs) ++ ys = x ': xs ++ ys++{- | Builds combined 'Constraint' by applying Constraint constructor to all+elements of type-level list.++>>> :kind! AllHave Show '[Int, Text, Double]+AllHave Show '[Int, Text, Double] :: Constraint+= (Show Int, (Show Text, (Show Double, () :: Constraint)))++which is equivalent to:++@+(Show Int, Show Text, Show Double) :: Constraint+@++@since 0.6.0.0+-}+type family AllHave (f :: k -> Constraint) (xs :: [k]) :: Constraint where+ AllHave _ '[] = ()+ AllHave f (x ': xs) = (f x, AllHave f xs)++{- | Returns first element of tuple type (with kind @*@) or type-level tuple+(with kind @(k1, k2)@, marked by prefix quote).++>>> :kind! Maybe (Fst '(Int, Text))+Maybe (Fst '(Int, Text)) :: *+= Maybe Int++>>> :kind! Maybe (Fst (Int, Text))+Maybe (Fst (Int, Text)) :: *+= Maybe Int++@since 0.6.0.0+-}+type family Fst (t :: k) :: k' where+ Fst '(x, _) = x+ Fst (x, _) = x++{- | Returns second element of tuple type (with kind @*@) or type-level tuple+(with kind @(k1, k2)@, marked by prefix quote).++>>> :kind! Maybe (Snd '(Int, Text))+Maybe (Snd '(Int, Text)) :: *+= Maybe Text+>>> :kind! Maybe (Snd (Int, Text))+Maybe (Snd (Int, Text)) :: *+= Maybe Text++@since 0.6.0.0+-}+type family Snd (t :: k) :: k' where+ Snd '(_, y) = y+ Snd (_, y) = y++{- | Check that a type is an element of a list:++>>> :kind! Elem String '[]+Elem String '[] :: Bool+= 'False++>>> :kind! Elem String '[Int, String]+Elem String '[Int, String] :: Bool+= 'True++>>> :kind! Elem String '[Int, Bool]+Elem String '[Int, Bool] :: Bool+= 'False++@since 0.6.0.0+-}+type family Elem (e :: t) (es :: [t]) :: Bool where+ Elem _ '[] = 'False+ Elem x (x ': xs) = 'True+ Elem x (_ ': xs) = Elem x xs
+ src/Relude/File.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE Safe #-}++{- |+Module : Relude.File+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Lifted to 'MonadIO' families of file processing functions for the 'Text', 'LText',+'ByteString' and 'LByteString' types.++These functions are lifted which means that you can also use them inside+various Monad Transformers without adding 'liftIO' call explicitly.++__NOTE:__ These functions are for working with textual data. Functions that work+with 'Text' or 'LText' types are system and locale-sensitive (encoding,+line-endings). If you want binary data, use 'ByteString' functions (they are+also faster since they don't check encoding). However, you can then decode that+data with the help of functions from the "Relude.String.Conversion" module, e. g.+'Relude.String.Conversion.decodeUtf8'.++To be more precise, avoid the following functions:++* 'readFileText'+* 'readFileLText'++See the following blog post for more details:++* [Beware of readFile by Michael Snoyman](https://www.snoyman.com/blog/2016/12/beware-of-readfile/)++@since 0.3.0+-}++module Relude.File+ ( -- * Text+ readFileText+ , writeFileText+ , appendFileText++ -- * Lazy Text+ , readFileLText+ , writeFileLText+ , appendFileLText++ -- * ByteString+ , readFileBS+ , writeFileBS+ , appendFileBS++ -- * Lazy ByteString+ , readFileLBS+ , writeFileLBS+ , appendFileLBS+ ) where++import Relude.Base (FilePath, IO)+import Relude.Function ((.))+import Relude.Monad.Reexport (MonadIO (..))+import Relude.String (ByteString, LByteString, LText, Text)++import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.Text.IO as T+import qualified Data.Text.Lazy.IO as LT+++----------------------------------------------------------------------------+-- Text+----------------------------------------------------------------------------++{- | Lifted version of 'T.readFile'.++@since 0.3.0+-}+readFileText :: MonadIO m => FilePath -> m Text+readFileText = liftIO . T.readFile+{-# SPECIALIZE readFileText :: FilePath -> IO Text #-}+{-# INLINE readFileText #-}+{-# WARNING readFileText ["'readFileText' depends on the system's locale settings and can throw unexpected exceptions.", "Use 'readFileBS' instead."] #-}++{- | Lifted version of 'T.writeFile'.++@since 0.3.0+-}+writeFileText :: MonadIO m => FilePath -> Text -> m ()+writeFileText p = liftIO . T.writeFile p+{-# SPECIALIZE writeFileText :: FilePath -> Text -> IO () #-}+{-# INLINE writeFileText #-}++{- | Lifted version of 'T.appendFile'.++@since 0.3.0+-}+appendFileText :: MonadIO m => FilePath -> Text -> m ()+appendFileText p = liftIO . T.appendFile p+{-# SPECIALIZE appendFileText :: FilePath -> Text -> IO () #-}+{-# INLINE appendFileText #-}++----------------------------------------------------------------------------+-- Lazy Text+----------------------------------------------------------------------------++{- | Lifted version of 'LT.readFile'.++@since 0.3.0+-}+readFileLText :: MonadIO m => FilePath -> m LText+readFileLText = liftIO . LT.readFile+{-# SPECIALIZE readFileLText :: FilePath -> IO LText #-}+{-# INLINE readFileLText #-}+{-# WARNING readFileLText ["'readFileLText' depends on the system's locale settings and can throw unexpected exceptions.", "Use 'readFileLBS' instead."] #-}++{- | Lifted version of 'LT.writeFile'.++@since 0.3.0+-}+writeFileLText :: MonadIO m => FilePath -> LText -> m ()+writeFileLText p = liftIO . LT.writeFile p+{-# SPECIALIZE writeFileLText :: FilePath -> LText -> IO () #-}+{-# INLINE writeFileLText #-}++{- | Lifted version of 'LT.appendFile'.++@since 0.3.0+-}+appendFileLText :: MonadIO m => FilePath -> LText -> m ()+appendFileLText p = liftIO . LT.appendFile p+{-# SPECIALIZE appendFileLText :: FilePath -> LText -> IO () #-}+{-# INLINE appendFileLText #-}++----------------------------------------------------------------------------+-- ByteString+----------------------------------------------------------------------------++{- | Lifted version of 'BS.readFile'.++@since 0.3.0+-}+readFileBS :: MonadIO m => FilePath -> m ByteString+readFileBS = liftIO . BS.readFile+{-# SPECIALIZE readFileBS :: FilePath -> IO ByteString #-}+{-# INLINE readFileBS #-}++{- | Lifted version of 'BS.writeFile'.++@since 0.3.0+-}+writeFileBS :: MonadIO m => FilePath -> ByteString -> m ()+writeFileBS p = liftIO . BS.writeFile p+{-# SPECIALIZE writeFileBS :: FilePath -> ByteString -> IO () #-}+{-# INLINE writeFileBS #-}++{- | Lifted version of 'BS.appendFile'.++@since 0.3.0+-}+appendFileBS :: MonadIO m => FilePath -> ByteString -> m ()+appendFileBS p = liftIO . BS.appendFile p+{-# SPECIALIZE appendFileBS :: FilePath -> ByteString -> IO () #-}+{-# INLINE appendFileBS #-}++----------------------------------------------------------------------------+-- Lazy ByteString+----------------------------------------------------------------------------++{- | Lifted version of 'LBS.readFile'.++@since 0.3.0+-}+readFileLBS :: MonadIO m => FilePath -> m LByteString+readFileLBS = liftIO . LBS.readFile+{-# SPECIALIZE readFileLBS :: FilePath -> IO LByteString #-}+{-# INLINE readFileLBS #-}++{- | Lifted version of 'LBS.writeFile'.++@since 0.3.0+-}+writeFileLBS :: MonadIO m => FilePath -> LByteString -> m ()+writeFileLBS p = liftIO . LBS.writeFile p+{-# SPECIALIZE writeFileLBS :: FilePath -> LByteString -> IO () #-}+{-# INLINE writeFileLBS #-}++{- | Lifted version of 'LBS.appendFile'.++@since 0.3.0+-}+appendFileLBS :: MonadIO m => FilePath -> LByteString -> m ()+appendFileLBS p = liftIO . LBS.appendFile p+{-# SPECIALIZE appendFileLBS :: FilePath -> LByteString -> IO () #-}+{-# INLINE appendFileLBS #-}
src/Relude/Foldable.hs view
@@ -1,9 +1,30 @@--- | This module exports all 'Foldable' and 'Traversable' related stuff.+{-# LANGUAGE Safe #-} +{- |+Module : Relude.Foldable+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++This module provides 'Foldable' and 'Traversable' related types and functions.+-}+ module Relude.Foldable- ( module Relude.Foldable.Fold- , module Relude.Foldable.Reexport- ) where+ ( module Relude.Foldable.Reexport+ -- $reexport+ , module Relude.Foldable.Fold+ -- $fold+ ) where import Relude.Foldable.Fold import Relude.Foldable.Reexport++{- $reexport+Reexports types and functions from "Data.Foldable" and "Data.Traversable".+-}++{- $fold+Type safe versions and additional functions to 'Foldable'.+-}
src/Relude/Foldable/Fold.hs view
@@ -1,75 +1,118 @@+{-# OPTIONS_GHC -Wno-redundant-constraints #-}++{-# LANGUAGE CPP #-}++#if __GLASGOW_HASKELL__ >= 902+{-# OPTIONS_GHC -Wno-operator-whitespace #-}+#endif+ {-# LANGUAGE DataKinds #-}-{-# LANGUAGE ExplicitForAll #-} {-# LANGUAGE PolyKinds #-}+{-# LANGUAGE Safe #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}+{- |+Module : Relude.Foldable.Fold+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Fixes and additions to 'Foldable'.+Fixes and additions to 'Foldable'. Specifically: +* Space-leak free 'sum' and 'product'+* 'elem' and 'notElem' are forbidden for 'Set' and 'HashSet'+* Additional combinators for common idioms+-}+ module Relude.Foldable.Fold- ( flipfoldl'- , foldMapA- , foldMapM- , safeHead- , sum- , product+ ( flipfoldl'+ , asumMap+ , foldMapA+ , foldMapM - , elem- , notElem+ , sum+ , product - , allM- , anyM- , andM- , orM- ) where+ , elem+ , notElem -import GHC.TypeLits (ErrorMessage (..), TypeError)+ -- * Monadic functions+ , allM+ , anyM+ , andM+ , orM+ ) where -import Relude.Applicative (Applicative (..), pure)-import Relude.Base (Constraint, Eq, IO, Num (..), Type, ($!))-import Relude.Bool (Bool (..))+import GHC.TypeLits (ErrorMessage (..), Symbol, TypeError)++import Relude.Applicative (Alternative, Applicative (..), pure)+import Relude.Base (Constraint, Eq, IO, Type, coerce, ($!))+import Relude.Bool (Bool (..), (&&^), (||^)) import Relude.Container.Reexport (HashSet, Set) import Relude.Foldable.Reexport (Foldable (..)) import Relude.Function (flip, (.))-import Relude.Functor ((<$>))-import Relude.Monad.Reexport (Maybe (..), Monad (..))-import Relude.Monoid (Monoid (..))+import Relude.Monad.Reexport (Monad (..))+import Relude.Monoid (Alt (..), Ap (..), Monoid (..), Semigroup)+import Relude.Numeric (Num (..)) import qualified Data.Foldable as F + -- $setup--- >>> :set -XOverloadedStrings--- >>> import Relude.Base (String, Rational, even, (/))--- >>> import Relude.Bool (when)--- >>> import Relude.Monad (Maybe (..), (>=>))--- >>> import Relude.Print (print, putTextLn)--- >>> import Relude.String (Text, readMaybe)+-- >>> import Relude -- >>> import qualified Data.HashMap.Strict as HashMap -safeHead :: Foldable f => f a -> Maybe a-safeHead = foldr (\x _ -> Just x) Nothing-{-# INLINE safeHead #-}- {- | Similar to 'foldl'' but takes a function with its arguments flipped. >>> flipfoldl' (/) 5 [2,3] :: Rational 15 % 2 +This function can be useful for constructing containers from lists. -} flipfoldl' :: Foldable f => (a -> b -> b) -> b -> f a -> b flipfoldl' f = foldl' (flip f) {-# INLINE flipfoldl' #-} -foldMapA :: (Monoid b, Applicative m, Foldable f) => (a -> m b) -> f a -> m b-foldMapA f = foldr step (pure mempty)- where- step a mb = mappend <$> f a <*> mb+{- | Alternative version of 'Relude.Foldable.Reexport.asum' that takes a function to map over.++>>> asumMap (\x -> if x > 2 then Just x else Nothing) [1..4]+Just 3++@since 0.4.0+-}+asumMap :: forall b m f a . (Foldable f, Alternative m) => (a -> m b) -> f a -> m b+asumMap = coerce (foldMap :: (a -> Alt m b) -> f a -> Alt m b)+{-# INLINE asumMap #-}++{- | Polymorphic version of the 'concatMapA' function.++>>> foldMapA @[Int] (Just . replicate 3) [1..3]+Just [1,1,1,2,2,2,3,3,3]++@since 0.1.0+-}+foldMapA+ :: forall b m f a . (Semigroup b, Monoid b, Applicative m, Foldable f)+ => (a -> m b)+ -> f a+ -> m b+foldMapA = coerce (foldMap :: (a -> Ap m b) -> f a -> Ap m b) {-# INLINE foldMapA #-} -foldMapM :: (Monoid b, Monad m, Foldable f) => (a -> m b) -> f a -> m b+{- | Polymorphic version of the 'Control.Monad.Extra.concatMapM' function.++>>> foldMapM @[Int] (Just . replicate 3) [1..3]+Just [1,1,1,2,2,2,3,3,3]++@since 0.1.0+-}+foldMapM :: forall b m f a . (Monoid b, Monad m, Foldable f) => (a -> m b) -> f a -> m b foldMapM f xs = foldr step return xs mempty where step x r z = f x >>= \y -> r $! z `mappend` y@@ -99,17 +142,17 @@ False >>> elem False (one True :: Set Bool) ...- • Do not use 'elem' and 'notElem' methods from 'Foldable' on Set+... Do not use 'elem' and 'notElem' methods from 'Foldable' on Set Suggestions: Instead of elem :: (Foldable t, Eq a) => a -> t a -> Bool use- member :: ??? -- TODO+ member :: Ord a => a -> Set a -> Bool ... Instead of notElem :: (Foldable t, Eq a) => a -> t a -> Bool use- notMember :: ??? -- TODO+ not . member ... -} elem :: (Foldable f, DisallowElem f, Eq a) => a -> f a -> Bool@@ -122,17 +165,17 @@ True >>> notElem False (one True :: Set Bool) ...- • Do not use 'elem' and 'notElem' methods from 'Foldable' on Set+... Do not use 'elem' and 'notElem' methods from 'Foldable' on Set Suggestions: Instead of elem :: (Foldable t, Eq a) => a -> t a -> Bool use- member :: ??? -- TODO+ member :: Ord a => a -> Set a -> Bool ... Instead of notElem :: (Foldable t, Eq a) => a -> t a -> Bool use- notMember :: ??? -- TODO+ not . member ... -} notElem :: (Foldable f, DisallowElem f, Eq a) => a -> f a -> Bool@@ -155,12 +198,9 @@ False -} andM :: (Foldable f, Monad m) => f (m Bool) -> m Bool-andM = go . toList- where- go [] = pure True- go (p:ps) = do- q <- p- if q then go ps else pure False+andM = foldr (&&^) (pure True)+{-# INLINE andM #-}+{-# SPECIALIZE andM :: [IO Bool] -> IO Bool #-} {- | Monadic version of 'F.or'. @@ -172,12 +212,9 @@ Nothing -} orM :: (Foldable f, Monad m) => f (m Bool) -> m Bool-orM = go . toList- where- go [] = pure False- go (p:ps) = do- q <- p- if q then pure True else go ps+orM = foldr (||^) (pure False)+{-# INLINE orM #-}+{-# SPECIALIZE orM :: [IO Bool] -> IO Bool #-} {- | Monadic version of 'F.all'. @@ -189,12 +226,9 @@ Nothing -} allM :: (Foldable f, Monad m) => (a -> m Bool) -> f a -> m Bool-allM p = go . toList- where- go [] = pure True- go (x:xs) = do- q <- p x- if q then go xs else pure False+allM p = foldr ((&&^) . p) (pure True)+{-# INLINE allM #-}+{-# SPECIALIZE allM :: (a -> IO Bool) -> [a] -> IO Bool #-} {- | Monadic version of 'F.any'. @@ -206,38 +240,37 @@ Nothing -} anyM :: (Foldable f, Monad m) => (a -> m Bool) -> f a -> m Bool-anyM p = go . toList- where- go [] = pure False- go (x:xs) = do- q <- p x- if q then pure True else go xs--{-# SPECIALIZE andM :: [IO Bool] -> IO Bool #-}-{-# SPECIALIZE orM :: [IO Bool] -> IO Bool #-}+anyM p = foldr ((||^) . p) (pure False)+{-# INLINE anyM #-} {-# SPECIALIZE anyM :: (a -> IO Bool) -> [a] -> IO Bool #-}-{-# SPECIALIZE allM :: (a -> IO Bool) -> [a] -> IO Bool #-} ---------------------------------------------------------------------------- -- Type level tricks ---------------------------------------------------------------------------- +{- | Type family that produces compile-time errors when 'elem' and 'notElem'+functions are used with 'Set' and 'HashSet'.+-} type family DisallowElem (f :: Type -> Type) :: Constraint where- DisallowElem Set = TypeError (ElemErrorMessage Set)- DisallowElem HashSet = TypeError (ElemErrorMessage HashSet)+ DisallowElem Set = TypeError (ElemErrorMessage Set SetMemberType)+ DisallowElem HashSet = TypeError (ElemErrorMessage HashSet HashSetMemberType) DisallowElem f = () -type family ElemErrorMessage (t :: k) :: ErrorMessage where- ElemErrorMessage t =- Text "Do not use 'elem' and 'notElem' methods from 'Foldable' on " :<>: ShowType t- :$$: Text "Suggestions:"- :$$: Text " Instead of"- :$$: Text " elem :: (Foldable t, Eq a) => a -> t a -> Bool"- :$$: Text " use"- :$$: Text " member :: ??? -- TODO"- :$$: Text ""- :$$: Text " Instead of"- :$$: Text " notElem :: (Foldable t, Eq a) => a -> t a -> Bool"- :$$: Text " use"- :$$: Text " notMember :: ??? -- TODO"- :$$: Text ""++type family ElemErrorMessage (t :: k) (msg :: Symbol) :: ErrorMessage where+ ElemErrorMessage t msg =+ 'Text "Do not use 'elem' and 'notElem' methods from 'Foldable' on " ':<>: 'ShowType t+ ':$$: 'Text "Suggestions:"+ ':$$: 'Text " Instead of"+ ':$$: 'Text " elem :: (Foldable t, Eq a) => a -> t a -> Bool"+ ':$$: 'Text " use"+ ':$$: 'Text " member :: " ':<>: 'Text msg+ ':$$: 'Text ""+ ':$$: 'Text " Instead of"+ ':$$: 'Text " notElem :: (Foldable t, Eq a) => a -> t a -> Bool"+ ':$$: 'Text " use"+ ':$$: 'Text " not . member"+ ':$$: 'Text ""++type SetMemberType = "Ord a => a -> Set a -> Bool"+type HashSetMemberType = "(Eq a, Hashable a) => a -> HashSet a -> Bool"
src/Relude/Foldable/Reexport.hs view
@@ -1,9 +1,34 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE Safe #-}++{- |+Module : Relude.Foldable.Reexport+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Reexports "Data.Foldable" and "Data.Traversable".+-}+ module Relude.Foldable.Reexport- ( module Data.Foldable- , module Data.Traversable- ) where+ ( -- * @Foldable@ reexports+ module Data.Foldable+ , module Data.Traversable+ -- * @Bi@ reexports+ , module Data.Bifoldable+ , module Data.Bitraversable+ ) where import Data.Foldable (Foldable (fold, foldMap, foldl', foldr, length, null, toList), all, and, any, asum, concat, concatMap, find, foldlM, forM_, for_, mapM_, or, sequenceA_, sequence_, traverse_)+#if MIN_VERSION_base(4,13,0)+import Data.Foldable (foldMap')+#endif+import Data.Bifoldable (Bifoldable (..), biList, biall, biand, biany, biasum, bielem, bifind,+ bifoldl', bifoldlM, bifoldr', bifoldrM, bifor_, bilength, binull, bior,+ bisequence_, bitraverse_)+import Data.Bitraversable (Bitraversable (..), bifoldMapDefault, bifor, bimapDefault, bisequence) import Data.Traversable (Traversable (..), forM, mapAccumL, mapAccumR)
src/Relude/Function.hs view
@@ -1,20 +1,57 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE Safe #-} --- | This module reexports very basic and primitive functions and function combinators.+{- |+Module : Relude.Function+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +This module reexports very basic and primitive functions and function+combinators.+-}+ module Relude.Function- ( module Data.Function- , identity- ) where+ ( -- * Reexports+ -- ** "Data.Function" reexports+ (.)+ , ($)+ , (&)+ , id+ , const+ , flip+ , fix+ , on+ -- ** "Control.Category" reexports+ , (>>>)+ , (<<<)+ -- ** "Control.Arrow" reexports+ , (&&&) + -- * Combinators+ , identity+ ) where++import Control.Arrow ((&&&))+import Control.Category ((<<<), (>>>)) import Data.Function (const, fix, flip, id, on, ($), (&), (.)) --- | Renamed version of 'Prelude.id'.++-- $setup+-- >>> import Relude++{- | Renamed version of 'Prelude.id'.++>>> identity 10+10++>>> fmap identity [1,2,3]+[1,2,3]++-} identity :: a -> a identity = id {-# INLINE identity #-}
src/Relude/Functor.hs view
@@ -1,11 +1,33 @@ {-# LANGUAGE Safe #-} --- | Convenient functions to work with 'Functor'.+{- |+Module : Relude.Functor+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +Convenient functions to work with 'Functor'.+-}+ module Relude.Functor- ( module Relude.Functor.Fmap- , module Relude.Functor.Reexport- ) where+ ( module Relude.Functor.Reexport+ -- $reexport+ , module Relude.Functor.Fmap+ -- $fmap+ ) where import Relude.Functor.Fmap import Relude.Functor.Reexport++{- $reexport+Reexports functionality for 'Functor' and 'Bifunctor' typeclasses.+-}++{- $fmap+Additional useful combinators to work with 'Functor' type class.+For example, '<<$>>' and '??'.+-}
src/Relude/Functor/Fmap.hs view
@@ -1,29 +1,84 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Functor.Fmap+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | This module contains useful functions to work with 'Functor' type class.+This module contains useful functions to work with 'Functor' type class.+-} module Relude.Functor.Fmap- ( (<<$>>)- ) where+ ( (<<$>>)+ , (<&>)+ , flap+ , (??)+ ) where import Relude.Function ((.)) import Relude.Functor.Reexport (Functor (..)) +import Data.Functor ((<$>))+#if MIN_VERSION_base(4,11,0)+import Data.Functor ((<&>))+#else+++{- | Flipped version of '<$>'.++@+f <$> a = a <&> f+@+-}+infixl 1 <&>+(<&>) :: Functor f => f a -> (a -> b) -> f b+as <&> f = f <$> as+{-# INLINE (<&>) #-}+#endif+ -- $setup--- >>> import Relude.Base (negate) -- >>> import Relude.Monad (Maybe (..))+-- >>> import Relude.List ((++))+-- >>> import Relude.Numeric (negate, (*), (+)) --- | Alias for @fmap . fmap@. Convenient to work with two nested 'Functor's.------ >>> negate <<$>> Just [1,2,3]--- Just [-1,-2,-3]+{- | Alias for @fmap . fmap@. Convenient to work with two nested 'Functor's.++>>> negate <<$>> Just [1,2,3]+Just [-1,-2,-3]+-}+infixl 4 <<$>> (<<$>>) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b) (<<$>>) = fmap . fmap-infixl 4 <<$>>+{-# INLINE (<<$>>) #-}++{- | Takes a function in a 'Functor' context and applies it to a normal value.++>>> flap (++) "relude" "P"+"Prelude"++@since 0.3.0+-}+flap :: Functor f => f (a -> b) -> a -> f b+flap ff x = (\f -> f x) <$> ff+{-# INLINE flap #-}++{- | Operator version of the 'flap' function.++>>> [(+2), (*3)] ?? 5+[7,15]++>>> Just (+3) ?? 5+Just 8++@since 0.3.0+-}+infixl 4 ??+(??) :: Functor f => f (a -> b) -> a -> f b+(??) = flap+{-# INLINE (??) #-}
src/Relude/Functor/Reexport.hs view
@@ -1,24 +1,39 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Functor.Reexport+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | This module reexports functionality regarding 'Functor' type class.+Reexports functionality regarding 'Functor' and 'Bifunctor' typeclasses.+-} module Relude.Functor.Reexport- ( module Control.Arrow- , module Data.Bifunctor- , module Data.Functor- , module Data.Functor.Compose- , module Data.Functor.Identity- ) where+ ( -- * Reexport Functor+ module Data.Functor+ , module Data.Functor.Compose+ , module Data.Functor.Identity+#if MIN_VERSION_base(4,12,0)+ , module Data.Functor.Contravariant+#endif -import Control.Arrow ((&&&))+ -- * Reexport Bifunctor+ , module Data.Bifunctor+ ) where+ import Data.Bifunctor (Bifunctor (..)) import Data.Functor (Functor (..), void, ($>), (<$>)) import Data.Functor.Compose (Compose (..)) import Data.Functor.Identity (Identity (..))++#if MIN_VERSION_base(4,12,0)+import Data.Functor.Contravariant (Comparison (..), Contravariant (..), Equivalence (..), Op (..),+ Predicate (..), comparisonEquivalence, defaultComparison,+ defaultEquivalence, phantom, ($<), (>$$<), (>$<))+#endif
src/Relude/Lifted.hs view
@@ -1,22 +1,110 @@ {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Lifted+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Lifted versions of base functions.+Lifted versions of base functions. +These functions are lifted in a sense that you can use them inside various+monad transformers without adding 'Relude.Monad.Reexport.liftIO' calls+explicitly. However, you still can use all these functions inside plain+ t'Relude.Base.IO' monad as usual.++=== Example++==== @base@++With the @base@ function, you can easily work with these functions in the+ t'Relude.Base.IO' monad:++@+__main__ :: t'Relude.Base.IO' ()+__main__ = __do__+ x <- 'Data.Text.getLine'+ 'Prelude.print' x+@++However, to work in t'Relude.Monad.Reexport.MonadIO' you already need to "lift" them:++@+__main__ :: t'Relude.Monad.Reexport.MonadIO' m => m ()+__main__ = __do__+ x <- 'Relude.Monad.Reexport.liftIO' 'Data.Text.getLine'+ 'Relude.Monad.Reexport.liftIO' ('Prelude.print' x)+@++==== @relude@++In the meantime, @relude@ provides these function that can work in t'Relude.Base.IO'+the same way:++@+__main__ :: t'Relude.Base.IO' ()+__main__ = __do__+ x <- 'getLine'+ 'print' x+@++But also allows you to work in the t'Relude.Monad.Reexport.MonadIO' monads more easily:++@+__main__ :: t'Relude.Monad.Reexport.MonadIO' m => m ()+__main__ = __do__+ x <- 'getLine'+ 'print' x+@+-}+ module Relude.Lifted- ( module Relude.Lifted.Concurrent- , module Relude.Lifted.Exit- , module Relude.Lifted.File- , module Relude.Lifted.IORef- ) where+ ( module Relude.Lifted.Concurrent+ -- $concurrent+ , module Relude.Lifted.IORef+ -- $ioref+ , module Relude.Lifted.Exit+ -- $exit+ , module Relude.Lifted.File+ -- $file+ , module Relude.Lifted.Terminal+ -- $terminal+ , module Relude.Lifted.Handle+ -- $handle+ , module Relude.Lifted.Env+ -- $env+ ) where import Relude.Lifted.Concurrent import Relude.Lifted.Exit import Relude.Lifted.File import Relude.Lifted.IORef+import Relude.Lifted.Terminal+import Relude.Lifted.Handle+import Relude.Lifted.Env++{- $concurrent+Lifted 'MVar' and 'STM' functions.+-}+{- $exit+Lifted versions of functions that work with exit processes.+-}+{- $file+Lifted versions of functions working with files and common t'Relude.Base.IO'.+-}+{- $ioref+Lifted reexports from "Data.IORef" module.+-}+{- $terminal+Lifted functions to work with stdin and stdout.+-}+{- $handle+Lifted functions to work with t'Relude.Base.IO' 'Handle's.+-}+{- $env+Lifted functions to work with system environment.+-}
src/Relude/Lifted/Concurrent.hs view
@@ -1,53 +1,84 @@ {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Lifted.Concurrent+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Concurrency useful and common functions.+Lifted 'MVar' and 'STM' functions.+-} module Relude.Lifted.Concurrent- ( -- * MVar- MVar- , newEmptyMVar- , newMVar- , putMVar- , readMVar- , swapMVar- , takeMVar- , tryPutMVar- , tryReadMVar- , tryTakeMVar+ ( -- * MVar+ MVar+ , newEmptyMVar+ , newMVar+ , putMVar+ , readMVar+ , swapMVar+ , takeMVar+ , tryPutMVar+ , tryReadMVar+ , tryTakeMVar - -- * STM- , STM- , TVar- , atomically- , newTVarIO- , readTVarIO- , STM.modifyTVar'- , STM.newTVar- , STM.readTVar- , STM.writeTVar- ) where+ -- * STM+ , STM+ , atomically+ , STM.throwSTM+ , STM.catchSTM + -- * TVar+ , TVar+ , newTVarIO+ , readTVarIO+ , STM.modifyTVar'+ , STM.newTVar+ , STM.readTVar+ , STM.writeTVar++ -- * TMVar+ , TMVar+ , STM.newTMVar+ , STM.newEmptyTMVar+ , newTMVarIO+ , newEmptyTMVarIO+ , STM.takeTMVar+ , STM.putTMVar+ , STM.readTMVar+ , STM.tryReadTMVar+ , STM.swapTMVar+ , STM.tryTakeTMVar+ , STM.tryPutTMVar+ , STM.isEmptyTMVar+ , STM.mkWeakTMVar+ ) where+ import Control.Concurrent.MVar (MVar)+import Control.Concurrent.STM.TMVar (TMVar) import Control.Concurrent.STM.TVar (TVar) import Control.Monad.STM (STM)-import Control.Monad.Trans (MonadIO, liftIO)-import Data.Bool (Bool)-import Data.Function (($), (.))-import Data.Maybe (Maybe) +import Relude.Base (IO)+import Relude.Bool (Bool)+import Relude.Function (($), (.))+import Relude.Monad (Maybe, MonadIO (..))+ import qualified Control.Concurrent.MVar as CCM (newEmptyMVar, newMVar, putMVar, readMVar, swapMVar, takeMVar, tryPutMVar, tryReadMVar, tryTakeMVar)+import qualified Control.Concurrent.STM.TMVar as STM (isEmptyTMVar, mkWeakTMVar, newEmptyTMVar,+ newEmptyTMVarIO, newTMVar, newTMVarIO,+ putTMVar, readTMVar, swapTMVar, takeTMVar,+ tryPutTMVar, tryReadTMVar, tryTakeTMVar) import qualified Control.Concurrent.STM.TVar as STM (modifyTVar', newTVar, newTVarIO, readTVar, readTVarIO, writeTVar)-import qualified Control.Monad.STM as STM (atomically)+import qualified Control.Monad.STM as STM (atomically, catchSTM, throwSTM) + ---------------------------------------------------------------------------- -- Lifted Control.Concurrent.MVar ----------------------------------------------------------------------------@@ -56,46 +87,55 @@ newEmptyMVar :: MonadIO m => m (MVar a) newEmptyMVar = liftIO CCM.newEmptyMVar {-# INLINE newEmptyMVar #-}+{-# SPECIALIZE newEmptyMVar :: IO (MVar a) #-} -- | Lifted to 'MonadIO' version of 'CCM.newMVar'. newMVar :: MonadIO m => a -> m (MVar a) newMVar = liftIO . CCM.newMVar {-# INLINE newMVar #-}+{-# SPECIALIZE newMVar :: a -> IO (MVar a) #-} -- | Lifted to 'MonadIO' version of 'CCM.putMVar'. putMVar :: MonadIO m => MVar a -> a -> m () putMVar m a = liftIO $ CCM.putMVar m a {-# INLINE putMVar #-}+{-# SPECIALIZE putMVar :: MVar a -> a -> IO () #-} -- | Lifted to 'MonadIO' version of 'CCM.readMVar'. readMVar :: MonadIO m => MVar a -> m a readMVar = liftIO . CCM.readMVar {-# INLINE readMVar #-}+{-# SPECIALIZE readMVar :: MVar a -> IO a #-} -- | Lifted to 'MonadIO' version of 'CCM.swapMVar'. swapMVar :: MonadIO m => MVar a -> a -> m a swapMVar m v = liftIO $ CCM.swapMVar m v {-# INLINE swapMVar #-}+{-# SPECIALIZE swapMVar :: MVar a -> a -> IO a #-} -- | Lifted to 'MonadIO' version of 'CCM.takeMVar'. takeMVar :: MonadIO m => MVar a -> m a takeMVar = liftIO . CCM.takeMVar {-# INLINE takeMVar #-}+{-# SPECIALIZE takeMVar :: MVar a -> IO a #-} -- | Lifted to 'MonadIO' version of 'CCM.tryPutMVar'. tryPutMVar :: MonadIO m => MVar a -> a -> m Bool tryPutMVar m v = liftIO $ CCM.tryPutMVar m v {-# INLINE tryPutMVar #-}+{-# SPECIALIZE tryPutMVar :: MVar a -> a -> IO Bool #-} -- | Lifted to 'MonadIO' version of 'CCM.tryReadMVar'. tryReadMVar :: MonadIO m => MVar a -> m (Maybe a) tryReadMVar = liftIO . CCM.tryReadMVar {-# INLINE tryReadMVar #-}+{-# SPECIALIZE tryReadMVar :: MVar a -> IO (Maybe a) #-} -- | Lifted to 'MonadIO' version of 'CCM.tryTakeMVar'. tryTakeMVar :: MonadIO m => MVar a -> m (Maybe a) tryTakeMVar = liftIO . CCM.tryTakeMVar {-# INLINE tryTakeMVar #-}+{-# SPECIALIZE tryTakeMVar :: MVar a -> IO (Maybe a) #-} ---------------------------------------------------------------------------- -- Lifted STM@@ -105,13 +145,28 @@ atomically :: MonadIO m => STM a -> m a atomically = liftIO . STM.atomically {-# INLINE atomically #-}+{-# SPECIALIZE atomically :: STM a -> IO a #-} -- | Lifted to 'MonadIO' version of 'STM.newTVarIO'. newTVarIO :: MonadIO m => a -> m (TVar a) newTVarIO = liftIO . STM.newTVarIO {-# INLINE newTVarIO #-}+{-# SPECIALIZE newTVarIO :: a -> IO (TVar a) #-} -- | Lifted to 'MonadIO' version of 'STM.readTVarIO'. readTVarIO :: MonadIO m => TVar a -> m a readTVarIO = liftIO . STM.readTVarIO {-# INLINE readTVarIO #-}+{-# SPECIALIZE readTVarIO :: TVar a -> IO a #-}++-- | Lifted to 'MonadIO' version of 'STM.newTMVarIO'.+newTMVarIO :: MonadIO m => a -> m (TMVar a)+newTMVarIO = liftIO . STM.newTMVarIO+{-# INLINE newTMVarIO #-}+{-# SPECIALIZE newTMVarIO :: a -> IO (TMVar a) #-}++-- | Lifted to 'MonadIO' version of 'STM.newEmptyTMVarIO'.+newEmptyTMVarIO :: MonadIO m => m (TMVar a)+newEmptyTMVarIO = liftIO STM.newEmptyTMVarIO+{-# INLINE newEmptyTMVarIO #-}+{-# SPECIALIZE newEmptyTMVarIO :: IO (TMVar a) #-}
+ src/Relude/Lifted/Env.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE Safe #-}++{- |+Module : Relude.Lifted.Env+Copyright : (c) 2020-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Lifted functions to work with system environment.++@since 1.0.0.0+-}++module Relude.Lifted.Env+ ( getArgs+ , lookupEnv+ ) where++import Relude.Base (IO)+import Relude.Function ((.))+import Relude.Monad.Reexport (MonadIO (..), Maybe)+import Relude.String.Reexport (String)++import qualified System.Environment as ENV (getArgs, lookupEnv)++{- | Lifted version of 'System.Environment.getArgs'.++@since 1.0.0.0+-}+getArgs :: MonadIO m => m [String]+getArgs = liftIO ENV.getArgs+{-# SPECIALIZE getArgs :: IO [String] #-}+{-# INLINE getArgs #-}++{- | Lifted version of 'System.Environment.lookupEnv'.++@since 1.0.0.0+-}+lookupEnv :: MonadIO m => String -> m (Maybe String)+lookupEnv = liftIO . ENV.lookupEnv+{-# SPECIALIZE lookupEnv :: String -> IO (Maybe String) #-}+{-# INLINE lookupEnv #-}
src/Relude/Lifted/Exit.hs view
@@ -1,48 +1,69 @@ {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Lifted.Exit+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Lifted versions of functions that work with exit processes.+Lifted versions of functions that work with exit processes.+-} module Relude.Lifted.Exit- ( exitWith- , exitFailure- , exitSuccess- , die- ) where+ ( exitWith+ , exitFailure+ , exitSuccess+ , die+ ) where import Control.Monad.Trans (MonadIO, liftIO) import Data.String (String)-import Prelude ((>>)) import System.Exit (ExitCode)-import System.IO (stderr) import qualified System.Exit as XIO-import qualified System.IO (hPutStrLn) --- | Lifted version of 'System.Exit.exitWith'.++-- $setup+-- >>> import Relude+-- >>> import System.Exit (ExitCode (..))+++{- | Lifted version of 'System.Exit.exitWith'.++>>> exitWith (ExitFailure 3)+*** Exception: ExitFailure 3++>>> exitWith ExitSuccess+*** Exception: ExitSuccess+-} exitWith :: MonadIO m => ExitCode -> m a exitWith a = liftIO (XIO.exitWith a)-{-# INLINE exitWith #-} --- | Lifted version of 'System.Exit.exitFailure'.+{- | Lifted version of 'System.Exit.exitFailure'.++>>> exitFailure+*** Exception: ExitFailure 1+-} exitFailure :: MonadIO m => m a exitFailure = liftIO XIO.exitFailure-{-# INLINE exitFailure #-} --- | Lifted version of 'System.Exit.exitSuccess'.+{- | Lifted version of 'System.Exit.exitSuccess'.++>>> exitSuccess+*** Exception: ExitSuccess+-} exitSuccess :: MonadIO m => m a exitSuccess = liftIO XIO.exitSuccess-{-# INLINE exitSuccess #-} --- | Lifted version of 'System.Exit.die'.--- 'XIO.die' is available since base-4.8, but it's more convenient to--- redefine it instead of using CPP.-die :: MonadIO m => String -> m ()-die err = liftIO (System.IO.hPutStrLn stderr err) >> exitFailure-{-# INLINE die #-}+{- | Lifted version of 'System.Exit.die'.++>>> die "Goodbye!"+Goodbye!+*** Exception: ExitFailure 1+-}+die :: MonadIO m => String -> m a+die err = liftIO (XIO.die err)
src/Relude/Lifted/File.hs view
@@ -1,58 +1,63 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Lifted.File+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Lifted versions of functions working with files and common IO.--- All functions are specialized to 'Data.Text.Text'.+Lifted versions of functions working with files and common IO.+-} module Relude.Lifted.File- ( appendFile- , getLine- , openFile- , readFile- , writeFile- ) where+ ( readFile+#if ( __GLASGOW_HASKELL__ >= 900 )+ , readFile'+#endif+ , writeFile+ , appendFile+ ) where -import Control.Monad.Trans (MonadIO, liftIO)-import Data.Text (Text)-import Prelude (FilePath)-import System.IO (Handle, IOMode)+import Relude.Base (FilePath, IO)+import Relude.Function ((.))+import Relude.Monad.Reexport (MonadIO (..))+import Relude.String (String) -import qualified Data.Text.IO as XIO-import qualified System.IO as XIO (openFile)+import qualified System.IO as IO -------------------------------------------------------------------------------- Text----------------------------------------------------------------------------- --- | Lifted version of 'Data.Text.appendFile'.-appendFile :: MonadIO m => FilePath -> Text -> m ()-appendFile a b = liftIO (XIO.appendFile a b)-{-# INLINE appendFile #-}+-- | Lifted version of 'IO.readFile'.+readFile :: MonadIO m => FilePath -> m String+readFile = liftIO . IO.readFile+{-# SPECIALIZE readFile :: FilePath -> IO String #-}+{-# INLINE readFile #-}+{-# WARNING readFile ["'readFile' depends on the system's locale settings and can throw unexpected exceptions.", "Use 'readFileBS' or 'readFileLBS' instead."] #-} --- | Lifted version of 'Data.Text.getLine'.-getLine :: MonadIO m => m Text-getLine = liftIO XIO.getLine-{-# INLINE getLine #-}+#if ( __GLASGOW_HASKELL__ >= 900 )+{- | Lifted version of 'IO.readFile''. Strict version of 'readFile'. --- | Lifted version of 'Data.Text.readFile'.-readFile :: MonadIO m => FilePath -> m Text-readFile a = liftIO (XIO.readFile a)-{-# INLINE readFile #-}+@since 1.0.0.0+-}+readFile' :: MonadIO m => FilePath -> m String+readFile' = liftIO . IO.readFile'+{-# SPECIALIZE readFile' :: FilePath -> IO String #-}+{-# INLINE readFile' #-}+{-# WARNING readFile' ["readFile' depends on the system's locale settings and can throw unexpected exceptions.", "Use 'readFileBS' or 'readFileLBS' instead."] #-}+#endif --- | Lifted version of 'Data.Text.writeFile'.-writeFile :: MonadIO m => FilePath -> Text -> m ()-writeFile a b = liftIO (XIO.writeFile a b)+-- | Lifted version of 'IO.writeFile'.+writeFile :: MonadIO m => FilePath -> String -> m ()+writeFile p= liftIO . IO.writeFile p+{-# SPECIALIZE writeFile :: FilePath -> String -> IO () #-} {-# INLINE writeFile #-} --- | Lifted version of 'System.IO.openFile'.-openFile :: MonadIO m => FilePath -> IOMode -> m Handle-openFile a b = liftIO (XIO.openFile a b)-{-# INLINE openFile #-}---- 'withFile' can't be lifted into 'MonadIO', as it uses 'bracket'+-- | Lifted version of 'IO.appendFile'.+appendFile :: MonadIO m => FilePath -> String -> m ()+appendFile p = liftIO . IO.appendFile p+{-# SPECIALIZE appendFile :: FilePath -> String -> IO () #-}+{-# INLINE appendFile #-}
+ src/Relude/Lifted/Handle.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE Safe #-}++{- |+Module : Relude.Lifted.Handle+Copyright : (c) 2020-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Lifted functions to work with 'IO' 'Handle's.++@since 1.0.0.0+-}++module Relude.Lifted.Handle+ ( hFlush+ , hIsEOF+ , hSetBuffering+ , hGetBuffering+ , Handle+ , stdin+ , stdout+ , stderr+ , withFile+ , BufferMode (..)+ ) where++import Relude.Base (IO)+import Relude.Function ((.))+import Relude.Bool (Bool (..))+import Relude.Monad.Reexport (MonadIO (..))+import qualified System.IO as IO (Handle, hFlush, hIsEOF, hSetBuffering, hGetBuffering, BufferMode)+import System.IO (Handle, stdin, stdout, stderr, withFile, BufferMode (..))++{- | Lifted version of 'IO.hFlush'.++@since 1.0.0.0+-}+hFlush :: MonadIO m => IO.Handle -> m ()+hFlush = liftIO . IO.hFlush+{-# SPECIALIZE hFlush :: IO.Handle -> IO () #-}+{-# INLINE hFlush #-}++{- | Lifted version of 'IO.hIsEOF'.++@since 1.0.0.0+-}+hIsEOF :: MonadIO m => IO.Handle -> m Bool+hIsEOF = liftIO . IO.hIsEOF+{-# SPECIALIZE hIsEOF :: IO.Handle -> IO Bool #-}+{-# INLINE hIsEOF #-}++{- | Lifted version of 'IO.hSetBuffering'.++@since 1.0.0.0+-}+hSetBuffering :: MonadIO m => IO.Handle -> IO.BufferMode -> m ()+hSetBuffering h = liftIO . IO.hSetBuffering h+{-# SPECIALIZE hSetBuffering :: IO.Handle -> IO.BufferMode -> IO () #-}+{-# INLINE hSetBuffering #-}++{- | Lifted version of 'IO.hGetBuffering'.++@since 1.0.0.0+-}+hGetBuffering :: MonadIO m => IO.Handle -> m IO.BufferMode+hGetBuffering = liftIO . IO.hGetBuffering+{-# SPECIALIZE hGetBuffering :: IO.Handle -> IO IO.BufferMode #-}+{-# INLINE hGetBuffering #-}
src/Relude/Lifted/IORef.hs view
@@ -1,69 +1,185 @@ {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Lifted.IORef+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Lifted reexports from 'Data.IORef' module.+Lifted reexports from "Data.IORef" module.+-} module Relude.Lifted.IORef- ( IORef- , atomicModifyIORef- , atomicModifyIORef'- , atomicWriteIORef- , modifyIORef- , modifyIORef'- , newIORef- , readIORef- , writeIORef- ) where+ ( IORef+ , atomicModifyIORef+ , atomicModifyIORef'+ , atomicModifyIORef_+ , atomicModifyIORef'_+ , atomicWriteIORef+ , modifyIORef+ , modifyIORef'+ , newIORef+ , readIORef+ , writeIORef+ ) where import Control.Monad.Trans (MonadIO, liftIO) import Data.Function (($), (.)) import Data.IORef (IORef) +import Relude.Base (IO)+ import qualified Data.IORef as Ref (atomicModifyIORef, atomicModifyIORef', atomicWriteIORef, modifyIORef, modifyIORef', newIORef, readIORef, writeIORef) --- | Lifted version of 'Ref.newIORef'.++-- $setup+-- >>> import Relude++{- | Lifted version of 'Ref.newIORef'.++>>> ref <- newIORef False+>>> :t ref+ref :: IORef Bool++-} newIORef :: MonadIO m => a -> m (IORef a) newIORef = liftIO . Ref.newIORef {-# INLINE newIORef #-}+{-# SPECIALIZE newIORef :: a -> IO (IORef a) #-} --- | Lifted version of 'Ref.readIORef'.+{- | Lifted version of 'Ref.readIORef'.++>>> ref <- newIORef 42+>>> readIORef ref+42++-} readIORef :: MonadIO m => IORef a -> m a readIORef = liftIO . Ref.readIORef {-# INLINE readIORef #-}+{-# SPECIALIZE readIORef :: IORef a -> IO a #-} --- | Lifted version of 'Ref.writeIORef'.+{- | Lifted version of 'Ref.writeIORef'.++>>> ref <- newIORef 42+>>> writeIORef ref 43+>>> readIORef ref+43++-} writeIORef :: MonadIO m => IORef a -> a -> m () writeIORef ref what = liftIO $ Ref.writeIORef ref what {-# INLINE writeIORef #-}+{-# SPECIALIZE writeIORef :: IORef a -> a -> IO () #-} --- | Lifted version of 'Ref.modifyIORef'.+{- | Lifted version of 'Ref.modifyIORef'.++>>> ref <- newIORef 42+>>> modifyIORef ref (\a -> a + 6)+>>> readIORef ref+48++* To avoid space-leaks, see 'modifyIORef'' for stricter updates+* For atomic updates, see 'atomicModifyIORef'+-} modifyIORef :: MonadIO m => IORef a -> (a -> a) -> m () modifyIORef ref how = liftIO $ Ref.modifyIORef ref how {-# INLINE modifyIORef #-}+{-# SPECIALIZE modifyIORef :: IORef a -> (a -> a) -> IO () #-} --- | Lifted version of 'Ref.modifyIORef''.+{- | Lifted version of 'Ref.modifyIORef''.++>>> ref <- newIORef 42+>>> modifyIORef' ref (\a -> a + 3)+>>> readIORef ref+45++* For lazier updates, see 'modifyIORef'+* For atomic updates, see 'atomicModifyIORef''+-} modifyIORef' :: MonadIO m => IORef a -> (a -> a) -> m () modifyIORef' ref how = liftIO $ Ref.modifyIORef' ref how {-# INLINE modifyIORef' #-}+{-# SPECIALIZE modifyIORef' :: IORef a -> (a -> a) -> IO () #-} --- | Lifted version of 'Ref.atomicModifyIORef'.+{- | Lifted version of 'Ref.atomicModifyIORef'.++>>> ref <- newIORef 42+>>> atomicModifyIORef ref (\a -> (a, a + 3))+45+>>> readIORef ref+42++* To avoid space-leaks, see 'atomicModifyIORef'' for stricter updates+* If you are not interested in the return value, see 'atomicModifyIORef_'+-} atomicModifyIORef :: MonadIO m => IORef a -> (a -> (a, b)) -> m b atomicModifyIORef ref how = liftIO $ Ref.atomicModifyIORef ref how {-# INLINE atomicModifyIORef #-}+{-# SPECIALIZE atomicModifyIORef :: IORef a -> (a -> (a, b)) -> IO b #-} --- | Lifted version of 'Ref.atomicModifyIORef''.+{- | Lifted version of 'Ref.atomicModifyIORef''.++>>> ref <- newIORef 42+>>> atomicModifyIORef' ref (\a -> (a, a + 3))+45+>>> readIORef ref+42++* For lazier updates, see 'atomicModifyIORef'+* If you are not interested in the return value, see 'atomicModifyIORef'_'+-} atomicModifyIORef' :: MonadIO m => IORef a -> (a -> (a, b)) -> m b atomicModifyIORef' ref how = liftIO $ Ref.atomicModifyIORef' ref how {-# INLINE atomicModifyIORef' #-}+{-# SPECIALIZE atomicModifyIORef' :: IORef a -> (a -> (a, b)) -> IO b #-} --- | Lifted version of 'Ref.atomicWriteIORef'.+{- | Version of 'atomicModifyIORef' that discards return value. Useful+when you want to update 'IORef' but not interested in the returning+result.++>>> ref <- newIORef 42+>>> atomicModifyIORef_ ref (`div` 2)+>>> readIORef ref+21++@since 0.7.0.0+-}+atomicModifyIORef_ :: MonadIO m => IORef a -> (a -> a) -> m ()+atomicModifyIORef_ ref f = atomicModifyIORef ref $ \a -> (f a, ())+{-# INLINE atomicModifyIORef_ #-}+{-# SPECIALIZE atomicModifyIORef_ :: IORef a -> (a -> a) -> IO () #-}++{- | Version of 'atomicModifyIORef'' that discards return value. Useful+when you want to update 'IORef' but not interested in the returning+result.++>>> ref <- newIORef 42+>>> atomicModifyIORef'_ ref (`div` 2)+>>> readIORef ref+21++@since 0.7.0.0+-}+atomicModifyIORef'_ :: MonadIO m => IORef a -> (a -> a) -> m ()+atomicModifyIORef'_ ref f = atomicModifyIORef' ref $ \a -> (f a, ())+{-# INLINE atomicModifyIORef'_ #-}+{-# SPECIALIZE atomicModifyIORef'_ :: IORef a -> (a -> a) -> IO () #-}++{- | Lifted version of 'Ref.atomicWriteIORef'.++>>> ref <- newIORef 42+>>> atomicWriteIORef ref 45+>>> readIORef ref+45++-} atomicWriteIORef :: MonadIO m => IORef a -> a -> m () atomicWriteIORef ref what = liftIO $ Ref.atomicWriteIORef ref what {-# INLINE atomicWriteIORef #-}+{-# SPECIALIZE atomicWriteIORef :: IORef a -> a -> IO () #-}
+ src/Relude/Lifted/Terminal.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE Safe #-}++{- |+Module : Relude.Lifted.Terminal+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Lifted functions to work with stdin and stdout.++@since 0.3.0+-}++module Relude.Lifted.Terminal+ ( getLine+ , print+ , putStr+ , putStrLn+ ) where++import Relude.Base (IO, Show)+import Relude.Function ((.))+import Relude.Monad.Reexport (MonadIO (..))+import Relude.String.Reexport (String, Text)++import qualified Data.Text.IO as TIO+import qualified System.IO as IO (print, putStr, putStrLn)+++-- | Lifted version of 'Data.Text.getLine'.+getLine :: MonadIO m => m Text+getLine = liftIO TIO.getLine+{-# SPECIALIZE getLine :: IO Text #-}+{-# INLINE getLine #-}++-- | Lifted version of 'Prelude.print'.+print :: forall a m . (MonadIO m, Show a) => a -> m ()+print = liftIO . IO.print+{-# SPECIALIZE print :: Show a => a -> IO () #-}+{-# INLINE print #-}++-- | Lifted version of 'IO.putStr'.+putStr :: MonadIO m => String -> m ()+putStr = liftIO . IO.putStr+{-# SPECIALIZE putStr :: String -> IO () #-}+{-# INLINE putStr #-}++-- | Lifted version of 'IO.putStrLn'.+putStrLn :: MonadIO m => String -> m ()+putStrLn = liftIO . IO.putStrLn+{-# SPECIALIZE putStrLn :: String -> IO () #-}+{-# INLINE putStrLn #-}
src/Relude/List.hs view
@@ -1,18 +1,125 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}- {-# LANGUAGE Safe #-} --- | Utility functions to work with lists.+{- |+Module : Relude.List+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +Utility functions to work with lists and 'NonEmpty' lists.+-}+ module Relude.List- ( module Relude.List.Reexport- , module Relude.List.Safe- ) where+ ( module Relude.List.Reexport+ -- $reexport+ , module Relude.List.NonEmpty+ -- $nonempty+ , (!!?)+ , maybeAt+ , partitionWith+ ) where ++import Relude.Base ((<))+import Relude.Bool (otherwise)+import Relude.Function (flip, (.))+import Relude.List.NonEmpty import Relude.List.Reexport-import Relude.List.Safe+import Relude.Monad (Either, Maybe (..), partitionEithers)+import Relude.Numeric (Int, (-))+++-- $setup+-- >>> import Relude++{- | Safer version of 'Relude.Unsafe.!!', returns a Maybe.++Get element from list using index value starting from `0`.++>>> [] !!? 0+Nothing++>>> ["a", "b", "c"] !!? 3+Nothing++>>> [1, 2, 3] !!? (-1)+Nothing++>>> ["a", "b", "c"] !!? 2+Just "c"++@since 0.6.0.0+-}+infix 9 !!?+(!!?) :: [a] -> Int -> Maybe a+(!!?) xs i+ | i < 0 = Nothing+ | otherwise = go i xs+ where+ go :: Int -> [a] -> Maybe a+ go 0 (x:_) = Just x+ go j (_:ys) = go (j - 1) ys+ go _ [] = Nothing+{-# INLINE (!!?) #-}++{- | '!!?' with its arguments flipped.++Get element from list using index value starting from `0`.++>>> maybeAt 0 []+Nothing++>>> maybeAt 3 ["a", "b", "c"]+Nothing++>>> maybeAt (-1) [1, 2, 3]+Nothing++>>> maybeAt 2 ["a", "b", "c"]+Just "c"++@since 1.0.0.0+-}+maybeAt :: Int -> [a] -> Maybe a+maybeAt = flip (!!?)+{-# INLINE maybeAt #-}++{- | Partitions a list based on the result of function which produces an Either+value. List of all elements producing Left are extracted, in order, to the first+element of the output tuple. Similarly, a list of all elements producing Right+are extracted to the second element of output.++>>> :{+ divideEvenOrShow :: Int -> Either Int String+ divideEvenOrShow n+ | even n = Left $ n `div` 2+ | otherwise = Right $ "Odd: " <> show n+ :}++>>> partitionWith divideEvenOrShow [1 .. 6]+([1,2,3],["Odd: 1","Odd: 3","Odd: 5"])++@since 1.0.0.0+-}+partitionWith :: (a -> Either b c) -> [a] -> ([b], [c])+partitionWith f = partitionEithers . map f+{-# INLINE partitionWith #-}++{- $reexport+Most of the "Data.List" types and function.++Note, that list partial functions (e.g. 'Data.List.head') are not exported from+"Data.List".+Instead @relude@ provides safe functions that work with+'Data.List.NonEmpty.NonEmpty'. You can find them in the+"Relude.List.NonEmpty" module instead.+-}++{- $nonempty+Reexports from "Data.List.NonEmpty" and additional safe functions to work with+list type in terms of 'NonEmpty'.+-}
+ src/Relude/List/NonEmpty.hs view
@@ -0,0 +1,325 @@+{-# LANGUAGE CPP #-}++#if __GLASGOW_HASKELL__ >= 902+{-# OPTIONS_GHC -Wno-operator-whitespace #-}+#endif++{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++{- |+Module : Relude.List.NonEmpty+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++This module contains reexports from "Data.List.NonEmpty" and safe functions to+work with list type in terms of 'NonEmpty'.++Note, that "Relude" reexports 'head', 'tail', 'init', 'last' from+"Data.List.NonEmpty" instead of the "Data.List", so these functions are safe to+use.+++------------+--------------+-----------------------++| | @base@ | @relude@ |++============+==============+=======================++| __'head'__ | @[a] -> a@ | @'NonEmpty' a -> a@ |++------------+--------------+-----------------------++| __'tail'__ | @[a] -> [a]@ | @'NonEmpty' a -> [a]@ |++------------+--------------+-----------------------++| __'last'__ | @[a] -> a@ | @'NonEmpty' a -> a@ |++------------+--------------+-----------------------++| __'init'__ | @[a] -> [a]@ | @'NonEmpty' a -> [a]@ |++------------+--------------+-----------------------+++@relude@ also provides custom type error for better experience with transition+from lists to 'NonEmpty' with those functions.+++Let's examine the behaviour of the @relude@ list functions comparing to the+corresponding @base@ one on the example of the 'head' function:+++-----------------+-----------------------------------------++| | 'head' |++=================+=========================================++| __@base@__ | @[a] -> a@ |++-----------------+-----------------------------------------++| __@relude@__ | @'NonEmpty' a -> a@ |++-----------------+-----------------------------------------++| Example | @> 'Data.List.head' [1..5]@ |+| with list +-----------------------------------------++| __@base@__ | @1@ |++-----------------+-----------------------------------------++| Example | @> 'Data.List.head' []@ |+| with empty list +-----------------------------------------++| __@base@__ |@*** Exception: Prelude.head: empty list@|++-----------------+-----------------------------------------++| Example |@> 'head' $ 1 :| [2..5]@ |+| with 'NonEmpty' +-----------------------------------------++| __@relude@__ | @1@ |++-----------------+-----------------------------------------++| Example | @> 'viaNonEmpty' 'head' [1..5]@ |+| with list +-----------------------------------------++| __@relude@__ | @'Just' 1@ |++-----------------+-----------------------------------------++| Example |@> 'viaNonEmpty' 'head' []@ |+| with empty list +-----------------------------------------++| __@relude@__ | @'Nothing'@ |++-----------------+-----------------------------------------+++@since 0.2.0+-}++module Relude.List.NonEmpty+ ( -- * Reexports from "DataList.NonEmpty"+ NonEmpty (..)+ , nonEmpty+ , head+ , tail+ , last+ , init++ -- * Combinators+ , viaNonEmpty+ , whenNotNull+ , whenNotNullM+ ) where++import Data.List.NonEmpty (NonEmpty (..), nonEmpty)+#if __GLASGOW_HASKELL__ >= 904+import Data.Type.Equality (type (~))+#endif+import GHC.TypeLits (ErrorMessage (..), Symbol, TypeError)++import Relude.Applicative (Applicative, pass)+import Relude.Base (Constraint, Type)+import Relude.Function ((.))+import Relude.Functor (fmap)+import Relude.Monad (Maybe (..), Monad (..))++import qualified Data.List.NonEmpty as NE (head, init, last, tail)+++-- $setup+-- >>> import Relude++{- | For safe work with lists using functions for 'NonEmpty'.++>>> viaNonEmpty head [1]+Just 1+>>> viaNonEmpty head []+Nothing++@since 0.1.0+-}+viaNonEmpty :: (NonEmpty a -> b) -> [a] -> Maybe b+viaNonEmpty f = fmap f . nonEmpty+{-# INLINE viaNonEmpty #-}++{- | Performs given action over 'NonEmpty' list if given list is non empty.++>>> whenNotNull [] $ \(b :| _) -> print (not b)+>>> whenNotNull [False,True] $ \(b :| _) -> print (not b)+True+-}+whenNotNull :: Applicative f => [a] -> (NonEmpty a -> f ()) -> f ()+whenNotNull [] _ = pass+whenNotNull (x:xs) f = f (x :| xs)+{-# INLINE whenNotNull #-}++-- | Monadic version of 'whenNotNull'.+whenNotNullM :: Monad m => m [a] -> (NonEmpty a -> m ()) -> m ()+whenNotNullM ml f = ml >>= \l -> whenNotNull l f+{-# INLINE whenNotNullM #-}+++-- | For tracking usage of ordinary list with @head@-like functions.+type IsNonEmpty+ (f :: Type -> Type) -- Container, e.g. NonEmpty or []+ (a :: Type) -- Type of the element+ (res :: Type) -- Type of the result of the work of the function+ (fun :: Symbol) -- Function name+ = (f ~ NonEmpty, CheckNonEmpty f a res fun)++type family CheckNonEmpty+ (f :: Type -> Type)+ (a :: Type)+ (res :: Type)+ (fun :: Symbol)+ :: Constraint+ where+ CheckNonEmpty NonEmpty _ _ _ = ()+ CheckNonEmpty [] a res fun = TypeError+ ( 'Text "'" ':<>: 'Text fun ':<>: 'Text "' works with 'NonEmpty', not ordinary lists."+ ':$$: 'Text "Possible fix:"+ ':$$: 'Text " Replace: [" ':<>: 'ShowType a ':<>: 'Text "]"+ ':$$: 'Text " With: NonEmpty " ':<>: 'ShowType a+ ':$$: 'Text ""+ ':$$: 'Text "However, you can use '" ':<>: 'Text fun ':<>: 'Text "' with the ordinary lists."+ ':$$: 'Text "Apply 'viaNonEmpty' function from relude:"+ ':$$: 'Text " viaNonEmpty " ':<>: 'Text fun ':<>: 'Text " (yourList)"+ ':$$: 'Text "Note, that this will return 'Maybe " ':<>: 'ShowType res ':<>: 'Text "'"+ ':$$: 'Text "therefore it is a safe function unlike '" ':<>: 'Text fun ':<>: 'Text "' from the standard Prelude"+ )+ CheckNonEmpty t a _ fun = TypeError+ ( 'Text "'"+ ':<>: 'Text fun+ ':<>: 'Text "' works with 'NonEmpty "+ ':<>: 'ShowType a+ ':<>: 'Text "' lists"+ ':$$: 'Text "But given: "+ ':<>: 'ShowType t+ ':<>: 'Text " "+ ':<>: 'ShowType a+ )+++{- | @O(1)@. Extracts the first element of a 'NonEmpty' list.++Actual type of this function is the following:++@+head :: 'NonEmpty' a -> a+@++but it was given a more complex type to provide friendlier compile time errors.++>>> head ('a' :| "bcde")+'a'+>>> head [0..5 :: Int]+...+... 'head' works with 'NonEmpty', not ordinary lists.+ Possible fix:+ Replace: [Int]+ With: NonEmpty Int+...+ However, you can use 'head' with the ordinary lists.+ Apply 'viaNonEmpty' function from relude:+ viaNonEmpty head (yourList)+ Note, that this will return 'Maybe Int'+ therefore it is a safe function unlike 'head' from the standard Prelude+...+>>> head (Just 'a')+...+... 'head' works with 'NonEmpty Char' lists+ But given: Maybe Char+...+-}+head :: IsNonEmpty f a a "head" => f a -> a+head = NE.head+{-# INLINE head #-}++{- | @O(n)@. Return all the elements of a 'NonEmpty' list except the last one+element.++Actual type of this function is the following:++@+init :: 'NonEmpty' a -> [a]+@++but it was given a more complex type to provide friendlier compile time errors.++>>> init ('a' :| "bcde")+"abcd"+>>> init [0..5 :: Int]+...+... 'init' works with 'NonEmpty', not ordinary lists.+ Possible fix:+ Replace: [Int]+ With: NonEmpty Int+...+ However, you can use 'init' with the ordinary lists.+ Apply 'viaNonEmpty' function from relude:+ viaNonEmpty init (yourList)+ Note, that this will return 'Maybe [Int]'+ therefore it is a safe function unlike 'init' from the standard Prelude+...+>>> init (Just 'a')+...+... 'init' works with 'NonEmpty Char' lists+ But given: Maybe Char+...+-}+init :: IsNonEmpty f a [a] "init" => f a -> [a]+init = NE.init+{-# INLINE init #-}++{- | @O(n)@. Extracts the last element of a 'NonEmpty' list.++Actual type of this function is the following:++@+last :: 'NonEmpty' a -> a+@++but it was given a more complex type to provide friendlier compile time errors.++>>> last ('a' :| "bcde")+'e'+>>> last [0..5 :: Int]+...+... 'last' works with 'NonEmpty', not ordinary lists.+ Possible fix:+ Replace: [Int]+ With: NonEmpty Int+...+ However, you can use 'last' with the ordinary lists.+ Apply 'viaNonEmpty' function from relude:+ viaNonEmpty last (yourList)+ Note, that this will return 'Maybe Int'+ therefore it is a safe function unlike 'last' from the standard Prelude+...+>>> last (Just 'a')+...+... 'last' works with 'NonEmpty Char' lists+ But given: Maybe Char+...+-}+last :: IsNonEmpty f a a "last" => f a -> a+last = NE.last+{-# INLINE last #-}++{- | @O(1)@. Return all the elements of a 'NonEmpty' list after the head+element.++Actual type of this function is the following:++@+tail :: 'NonEmpty' a -> [a]+@++but it was given a more complex type to provide friendlier compile time errors.++>>> tail ('a' :| "bcde")+"bcde"+>>> tail [0..5 :: Int]+...+... 'tail' works with 'NonEmpty', not ordinary lists.+ Possible fix:+ Replace: [Int]+ With: NonEmpty Int+...+ However, you can use 'tail' with the ordinary lists.+ Apply 'viaNonEmpty' function from relude:+ viaNonEmpty tail (yourList)+ Note, that this will return 'Maybe [Int]'+ therefore it is a safe function unlike 'tail' from the standard Prelude+...+>>> tail (Just 'a')+...+... 'tail' works with 'NonEmpty Char' lists+ But given: Maybe Char+...+-}+tail :: IsNonEmpty f a [a] "tail" => f a -> [a]+tail = NE.tail+{-# INLINE tail #-}
src/Relude/List/Reexport.hs view
@@ -1,25 +1,48 @@ {-# LANGUAGE Trustworthy #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.List.Reexport+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | This module reexports functinons to work with list, 'NonEmpty' and String types.+Reexports most of the "Data.List".+-} module Relude.List.Reexport- ( module Data.List- , module Data.List.NonEmpty- , module GHC.Exts- ) where--import Data.List (break, cycle, drop, dropWhile, filter, genericDrop, genericLength,- genericReplicate, genericSplitAt, genericTake, group, inits, intercalate,- intersperse, isPrefixOf, iterate, map, permutations, repeat, replicate, reverse,- scanl, scanr, sort, sortBy, sortOn, splitAt, subsequences, tails, take, takeWhile,- transpose, unfoldr, unzip, unzip3, zip, zip3, zipWith, (++))-import Data.List.NonEmpty (NonEmpty (..), head, init, last, nonEmpty, tail)+ ( -- * List+ module Data.List+ , cycle+ , sortWith+ ) where +import Data.List (break, drop, dropWhile, filter, genericDrop, genericLength, genericReplicate,+ genericSplitAt, genericTake, group, inits, intercalate, intersperse, isPrefixOf,+ iterate, map, permutations, repeat, replicate, reverse, scanl, scanl', scanl1,+ scanr, scanr1, sort, sortBy, sortOn, span, splitAt, subsequences, tails, take,+ takeWhile, transpose, uncons, unfoldr, unzip, unzip3, zip, zip3, zipWith, (++)) import GHC.Exts (sortWith)+++-- $setup+-- >>> import Relude++{- | Creates an infinite list from a finite list by appending the list+to itself infinite times (i.e. by cycling the list). Unlike @cycle@+from "Data.List", this implementation doesn't throw error on empty+lists, but returns an empty list instead.++>>> cycle []+[]+>>> take 10 $ cycle [1,2,3]+[1,2,3,1,2,3,1,2,3,1]+-}+cycle :: [a] -> [a]+cycle [] = []+cycle xs = cycledList+ where+ cycledList = xs ++ cycledList
− src/Relude/List/Safe.hs
@@ -1,75 +0,0 @@-{-# LANGUAGE Safe #-}--{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}---- | This module contains safe functions to work with list type (mostly with 'NonEmpty').--module Relude.List.Safe- ( viaNonEmpty- , uncons- , whenNotNull- , whenNotNullM- ) where--import Relude.Applicative (Applicative, pass)-import Relude.Function ((.))-import Relude.Functor (fmap)-import Relude.List.Reexport (NonEmpty (..), nonEmpty)-import Relude.Monad (Maybe (..), Monad (..))---- $setup--- >>> import Relude.Applicative (pure)--- >>> import Relude.Base ((==))--- >>> import Relude.Bool (Bool (..), not)--- >>> import Relude.Foldable (length)--- >>> import Relude.Function (($))--- >>> import Relude.List.Reexport (head)--- >>> import Relude.Print (print)--{- | For safe work with lists using functinons for 'NonEmpty'.-->>> viaNonEmpty head [1]-Just 1->>> viaNonEmpty head []-Nothing---}-viaNonEmpty :: (NonEmpty a -> b) -> [a] -> Maybe b-viaNonEmpty f = fmap f . nonEmpty-{-# INLINE viaNonEmpty #-}--{- | Destructuring list into its head and tail if possible. This function is total.-->>> uncons []-Nothing->>> uncons [1..5]-Just (1,[2,3,4,5])->>> uncons (5 : [1..5]) >>= \(f, l) -> pure $ f == length l-Just True---}-uncons :: [a] -> Maybe (a, [a])-uncons [] = Nothing-uncons (x:xs) = Just (x, xs)--{- | Performs given action over 'NonEmpty' list if given list is non empty.-->>> whenNotNull [] $ \(b :| _) -> print (not b)->>> whenNotNull [False,True] $ \(b :| _) -> print (not b)-True---}-whenNotNull :: Applicative f => [a] -> (NonEmpty a -> f ()) -> f ()-whenNotNull [] _ = pass-whenNotNull (x:xs) f = f (x :| xs)-{-# INLINE whenNotNull #-}---- | Monadic version of 'whenNotNull'.-whenNotNullM :: Monad m => m [a] -> (NonEmpty a -> m ()) -> m ()-whenNotNullM ml f = ml >>= \l -> whenNotNull l f-{-# INLINE whenNotNullM #-}
src/Relude/Monad.hs view
@@ -1,22 +1,90 @@ {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Monad+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Reexporting useful monadic stuff.+Reexporting useful monadic stuff.+-} module Relude.Monad- ( module Relude.Monad.Either- , module Relude.Monad.Maybe- , module Relude.Monad.Reexport- , module Relude.Monad.Trans- ) where+ ( module Relude.Monad.Reexport+ -- $reexport+ , module Relude.Monad.Maybe+ -- $maybe+ , module Relude.Monad.Either+ -- $either+ , module Relude.Monad.Trans+ -- $trans+ , chainedTo+ , infinitely+ ) where import Relude.Monad.Either import Relude.Monad.Maybe import Relude.Monad.Reexport import Relude.Monad.Trans+import Control.Applicative (Applicative)+import Relude.Base (Void)+++-- $setup+-- >>> import Relude++{- | For chaining monadic operations in forward applications using '(&)'+Named version of '=<<'.++>>> Just [ 1 :: Int ] & chainedTo (viaNonEmpty head)+Just 1+>>> Nothing & chainedTo (viaNonEmpty head)+Nothing++@since 0.5.0+-}+chainedTo :: Monad m => (a -> m b) -> m a -> m b+chainedTo = (=<<)+{-# INLINE chainedTo #-}++{- | Repeat a monadic action indefinitely.++This is a more type safe version of 'forever', which has a convenient+but unsafe type.++Consider the following two examples. In the @getIntForever@ functions, it+falsely expects 'Int' as the result of the 'forever' function. But it would need+to wait *forever* to get that, and this mistake won't be caught by the type+system and compiler:++@+getIntForever :: IO Int+getIntForever = do+ i <- forever $ do ...+ pure i+@++In contrast, using 'infinitely' instead of 'forever' in 'foo' is a type error.++@since 1.0.0.0+-}+infinitely :: Applicative f => f a -> f Void+infinitely = forever+{-# INLINE infinitely #-}++{- $reexport+Reexports functions to work with different monads.+-}+{- $maybe+Provided new combinators to work with 'Relude.Monad.Reexport.Maybe' data type.+-}+{- $either+Provided new combinators to work with 'Relude.Monad.Reexport.Either' data type.+-}+{- $trans+Monad transformers functions and combinators.+-}
src/Relude/Monad/Either.hs view
@@ -1,145 +1,200 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+ {-# LANGUAGE Safe #-} -{-# OPTIONS_GHC -fno-warn-orphans #-}+{- |+Module : Relude.Monad.Either+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT+Utilities to work with 'Relude.Monad.Reexport.Either' data type. -} --- | Utilites to work with @Either@ data type.- module Relude.Monad.Either- ( fromLeft- , fromRight- , maybeToLeft- , maybeToRight- , leftToMaybe- , rightToMaybe- , whenLeft- , whenLeft_- , whenLeftM- , whenLeftM_- , whenRight- , whenRight_- , whenRightM- , whenRightM_- ) where+ ( -- * Combinators+ fromLeft+ , fromRight+ , maybeToLeft+ , maybeToRight+ , leftToMaybe+ , rightToMaybe+ , whenLeft+ , whenLeft_ + -- * Monadic combinators+ , whenLeftM+ , whenLeftM_+ , whenRight+ , whenRight_+ , whenRightM+ , whenRightM_+ ) where+ import Control.Applicative (Applicative) import Control.Monad (Monad (..))+import Data.Either (fromLeft, fromRight) import Data.Function (const) import Data.Maybe (Maybe (..), maybe) import Relude.Applicative (pure) import Relude.Function ((.)) import Relude.Monad.Reexport (Either (..), MonadFail (..), either)-import Relude.String (IsString, fromString)+import Relude.String.Reexport (IsString (..), String) + -- $setup--- >>> import Relude.Bool (Bool (..))+-- >>> import Relude +{- | For convenient work with 'MonadFail'.++@since 0.1.0+-} instance IsString str => MonadFail (Either str) where+ fail :: String -> Either str a fail = Left . fromString---- | Extracts value from 'Left' or return given default value.------ >>> fromLeft 0 (Left 3)--- 3--- >>> fromLeft 0 (Right 5)--- 0-fromLeft :: a -> Either a b -> a-fromLeft _ (Left a) = a-fromLeft a (Right _) = a+ {-# INLINE fail #-} --- | Extracts value from 'Right' or return given default value.------ >>> fromRight 0 (Left 3)--- 0--- >>> fromRight 0 (Right 5)--- 5-fromRight :: b -> Either a b -> b-fromRight b (Left _) = b-fromRight _ (Right b) = b+{- | Maps left part of 'Either' to 'Maybe'. --- | Maps left part of 'Either' to 'Maybe'.------ >>> leftToMaybe (Left True)--- Just True--- >>> leftToMaybe (Right "aba")--- Nothing+>>> leftToMaybe (Left True)+Just True+>>> leftToMaybe (Right "aba")+Nothing+-} leftToMaybe :: Either l r -> Maybe l leftToMaybe = either Just (const Nothing)+{-# INLINE leftToMaybe #-} --- | Maps right part of 'Either' to 'Maybe'.------ >>> rightToMaybe (Left True)--- Nothing--- >>> rightToMaybe (Right "aba")--- Just "aba"+{- | Maps right part of 'Either' to 'Maybe'.++>>> rightToMaybe (Left True)+Nothing+>>> rightToMaybe (Right "aba")+Just "aba"+-} rightToMaybe :: Either l r -> Maybe r rightToMaybe = either (const Nothing) Just+{-# INLINE rightToMaybe #-} --- | Maps 'Maybe' to 'Either' wrapping default value into 'Left'.------ >>> maybeToRight True (Just "aba")--- Right "aba"--- >>> maybeToRight True Nothing--- Left True+{- | Maps 'Maybe' to 'Either' wrapping default value into 'Left'.++>>> maybeToRight True (Just "aba")+Right "aba"+>>> maybeToRight True Nothing+Left True+-} maybeToRight :: l -> Maybe r -> Either l r maybeToRight l = maybe (Left l) Right+{-# INLINE maybeToRight #-} --- | Maps 'Maybe' to 'Either' wrapping default value into 'Right'.------ >>> maybeToLeft True (Just "aba")--- Left "aba"--- >>> maybeToLeft True Nothing--- Right True+{- | Maps 'Maybe' to 'Either' wrapping default value into 'Right'.++>>> maybeToLeft True (Just "aba")+Left "aba"+>>> maybeToLeft True Nothing+Right True+-} maybeToLeft :: r -> Maybe l -> Either l r maybeToLeft r = maybe (Right r) Left+{-# INLINE maybeToLeft #-} --- | Applies given action to 'Either' content if 'Left' is given and returns--- the result. In case of 'Right' the default value will be returned.+{- | Applies given action to 'Either' content if 'Left' is given and returns+the result. In case of 'Right' the default value will be returned.++>>> whenLeft "bar" (Left 42) (\a -> "success!" <$ print a)+42+"success!"++>>> whenLeft "bar" (Right 42) (\a -> "success!" <$ print a)+"bar"+-} whenLeft :: Applicative f => a -> Either l r -> (l -> f a) -> f a whenLeft _ (Left l) f = f l whenLeft a (Right _) _ = pure a {-# INLINE whenLeft #-} --- | Applies given action to 'Either' content if 'Left' is given.+{- | Applies given action to 'Either' content if 'Left' is given.++>>> whenLeft_ (Right 42) putTextLn+>>> whenLeft_ (Left "foo") putTextLn+foo+-} whenLeft_ :: Applicative f => Either l r -> (l -> f ()) -> f () whenLeft_ = whenLeft () {-# INLINE whenLeft_ #-} --- | Monadic version of 'whenLeft'.+{- | Monadic version of 'whenLeft'.++>>> whenLeftM "bar" (pure $ Left 42) (\a -> "success!" <$ print a)+42+"success!"++>>> whenLeftM "bar" (pure $ Right 42) (\a -> "success!" <$ print a)+"bar"+-} whenLeftM :: Monad m => a -> m (Either l r) -> (l -> m a) -> m a whenLeftM a me f = me >>= \e -> whenLeft a e f {-# INLINE whenLeftM #-} --- | Monadic version of 'whenLeft_'.+{- | Monadic version of 'whenLeft_'.++>>> whenLeftM_ (pure $ Right 42) putTextLn+>>> whenLeftM_ (pure $ Left "foo") putTextLn+foo+-} whenLeftM_ :: Monad m => m (Either l r) -> (l -> m ()) -> m () whenLeftM_ me f = me >>= \e -> whenLeft_ e f {-# INLINE whenLeftM_ #-} --- | Applies given action to 'Either' content if 'Right' is given and returns--- the result. In case of 'Left' the default value will be returned.+{- | Applies given action to 'Either' content if 'Right' is given and returns+the result. In case of 'Left' the default value will be returned.++>>> whenRight "bar" (Left "foo") (\a -> "success!" <$ print a)+"bar"++>>> whenRight "bar" (Right 42) (\a -> "success!" <$ print a)+42+"success!"+-} whenRight :: Applicative f => a -> Either l r -> (r -> f a) -> f a whenRight a (Left _) _ = pure a whenRight _ (Right r) f = f r {-# INLINE whenRight #-} --- | Applies given action to 'Either' content if 'Right' is given.+{- | Applies given action to 'Either' content if 'Right' is given.++>>> whenRight_ (Left "foo") print+>>> whenRight_ (Right 42) print+42+-} whenRight_ :: Applicative f => Either l r -> (r -> f ()) -> f () whenRight_ = whenRight () {-# INLINE whenRight_ #-} --- | Monadic version of 'whenRight'.+{- | Monadic version of 'whenRight'.++>>> whenRightM "bar" (pure $ Left "foo") (\a -> "success!" <$ print a)+"bar"++>>> whenRightM "bar" (pure $ Right 42) (\a -> "success!" <$ print a)+42+"success!"+-} whenRightM :: Monad m => a -> m (Either l r) -> (r -> m a) -> m a whenRightM a me f = me >>= \e -> whenRight a e f {-# INLINE whenRightM #-} --- | Monadic version of 'whenRight_'.+{- | Monadic version of 'whenRight_'.++>>> whenRightM_ (pure $ Left "foo") print+>>> whenRightM_ (pure $ Right 42) print+42+-} whenRightM_ :: Monad m => m (Either l r) -> (r -> m ()) -> m () whenRightM_ me f = me >>= \e -> whenRight_ e f {-# INLINE whenRightM_ #-}
src/Relude/Monad/Maybe.hs view
@@ -1,33 +1,40 @@ {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Monad.Maybe+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Utility functions to work with 'Data.Maybe' data type as monad.+Utility functions to work with 'Relude.Monad.Reexport.Maybe' data type as monad.+-} module Relude.Monad.Maybe- ( (?:)- , whenJust- , whenJustM- , whenNothing- , whenNothing_- , whenNothingM- , whenNothingM_- ) where+ ( -- * Combinators+ (?:)+ , whenJust+ , whenJustM+ , whenNothing+ , whenNothing_+ -- * Monadic combinators+ , whenNothingM+ , whenNothingM_+ , mapMaybeM+ ) where import Relude.Applicative (Applicative, pass, pure)-import Relude.Monad.Reexport (fromMaybe)-import Relude.Monad.Reexport (Maybe (..), Monad (..))+import Relude.Foldable.Reexport (mapM)+import Relude.Function ((.))+import Relude.Functor.Reexport (fmap)+import Relude.Monad.Reexport (Maybe (..), Monad (..), catMaybes, fromMaybe) + -- $setup--- >>> import Relude.Bool (Bool (..), not)--- >>> import Relude.Function (($))--- >>> import Relude.Print (putTextLn, print)--- >>> import Relude.String (readMaybe)+-- >>> import Relude {- | Similar to 'fromMaybe' but with flipped arguments. @@ -36,61 +43,98 @@ >>> readMaybe "Tru" ?: False False- -} infixr 0 ?: (?:) :: Maybe a -> a -> a mA ?: b = fromMaybe b mA {-# INLINE (?:) #-} -{- | Specialized version of 'for_' for 'Maybe'. It's used for code readability.+{- | Specialized version of 'Relude.Foldable.Reexport.for_' for 'Maybe'. It's used for code readability.+ Also helps to avoid space leaks: <http://www.snoyman.com/blog/2017/01/foldable-mapm-maybe-and-recursive-functions Foldable.mapM_ space leak>. >>> whenJust Nothing $ \b -> print (not b) >>> whenJust (Just True) $ \b -> print (not b) False- -} whenJust :: Applicative f => Maybe a -> (a -> f ()) -> f () whenJust (Just x) f = f x whenJust Nothing _ = pass {-# INLINE whenJust #-} --- | Monadic version of 'whenJust'.+{- | Monadic version of 'whenJust'.++>>> whenJustM (pure Nothing) $ \b -> print (not b)+>>> whenJustM (pure $ Just True) $ \b -> print (not b)+False+-} whenJustM :: Monad m => m (Maybe a) -> (a -> m ()) -> m () whenJustM mm f = mm >>= \m -> whenJust m f {-# INLINE whenJustM #-} --- | Performs default 'Applicative' action if 'Nothing' is given.--- Otherwise returns content of 'Just' pured to 'Applicative'.------ >>> whenNothing Nothing [True, False]--- [True,False]--- >>> whenNothing (Just True) [True, False]--- [True]+{- | Performs default 'Applicative' action if 'Nothing' is given.+Otherwise returns content of 'Just' pured to 'Applicative'.++>>> whenNothing Nothing [True, False]+[True,False]+>>> whenNothing (Just True) [True, False]+[True]+-} whenNothing :: Applicative f => Maybe a -> f a -> f a whenNothing (Just x) _ = pure x whenNothing Nothing m = m {-# INLINE whenNothing #-} --- | Performs default 'Applicative' action if 'Nothing' is given.--- Do nothing for 'Just'. Convenient for discarding 'Just' content.------ >>> whenNothing_ Nothing $ putTextLn "Nothing!"--- Nothing!--- >>> whenNothing_ (Just True) $ putTextLn "Nothing!"+{- | Performs default 'Applicative' action if 'Nothing' is given.+Do nothing for 'Just'. Convenient for discarding 'Just' content.++>>> whenNothing_ Nothing $ putTextLn "Nothing!"+Nothing!+>>> whenNothing_ (Just True) $ putTextLn "Nothing!"+-} whenNothing_ :: Applicative f => Maybe a -> f () -> f () whenNothing_ Nothing m = m whenNothing_ _ _ = pass {-# INLINE whenNothing_ #-} --- | Monadic version of 'whenNothing'.+{- | Monadic version of 'whenNothing'.++>>> whenNothingM (pure $ Just True) $ True <$ putTextLn "Is Just!"+True+>>> whenNothingM (pure Nothing) $ False <$ putTextLn "Is Nothing!"+Is Nothing!+False+-} whenNothingM :: Monad m => m (Maybe a) -> m a -> m a whenNothingM mm action = mm >>= \m -> whenNothing m action {-# INLINE whenNothingM #-} --- | Monadic version of 'whenNothingM_'.+{- | Monadic version of 'whenNothing_'.++>>> whenNothingM_ (pure $ Just True) $ putTextLn "Is Just!"+>>> whenNothingM_ (pure Nothing) $ putTextLn "Is Nothing!"+Is Nothing!+-} whenNothingM_ :: Monad m => m (Maybe a) -> m () -> m () whenNothingM_ mm action = mm >>= \m -> whenNothing_ m action {-# INLINE whenNothingM_ #-}+++{- | The monadic version of the 'Data.Maybe.mapMaybe' function.++>>> :{+evenInHalf :: Int -> IO (Maybe Int)+evenInHalf n+ | even n = pure $ Just $ n `div` 2+ | otherwise = pure Nothing+:}++>>> mapMaybeM evenInHalf [1..10]+[1,2,3,4,5]++@since 0.6.0.0+-}+mapMaybeM :: (Monad m) => (a -> m (Maybe b)) -> [a] -> m [b]+mapMaybeM f = fmap catMaybes . mapM f+{-# INLINE mapMaybeM #-}
src/Relude/Monad/Reexport.hs view
@@ -1,41 +1,50 @@ {-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Monad.Reexport+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | This module reexports functions to work with monads.+Reexports functions to work with monads.+-} module Relude.Monad.Reexport- ( -- * Reexport transformers- module Control.Monad.Except- , module Control.Monad.Reader- , module Control.Monad.State.Strict- , module Control.Monad.Trans- , module Control.Monad.Trans.Identity- , module Control.Monad.Trans.Maybe+ ( -- * Reexport MonadIO+ MonadIO (liftIO) - -- * Reexport monadic functions- , module Control.Monad- , module Control.Monad.Fail+ -- * Reexport transformers+ , module Control.Monad.Except+ , module Control.Monad.Reader+ , module Control.Monad.State.Strict+ , module Control.Monad.Trans+ , module Control.Monad.Trans.Identity+ , module Control.Monad.Trans.Maybe - -- * Reexport 'Maybe'- , module Data.Maybe+ -- * Reexport monadic functions+ , module Control.Monad+ , module Control.Monad.Fail - -- * Reexport 'Either'- , module Data.Either- ) where+ -- * Reexport 'Maybe'+ , module Data.Maybe + -- * Reexport 'Either'+ , module Data.Either+ ) where+ -- Monad transformers+import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Except (ExceptT (..), runExceptT)-import Control.Monad.Reader (MonadReader, Reader, ReaderT (..), ask, asks, local, reader, runReader)+import Control.Monad.Reader (MonadReader, Reader, ReaderT (..), ask, asks, local, reader, runReader,+ withReader, withReaderT) import Control.Monad.State.Strict (MonadState, State, StateT (..), evalState, evalStateT, execState, execStateT, get, gets, modify, modify', put, runState, state, withState)-import Control.Monad.Trans (MonadIO, MonadTrans, lift, liftIO)+import Control.Monad.Trans (MonadTrans, lift) import Control.Monad.Trans.Identity (IdentityT (runIdentityT)) import Control.Monad.Trans.Maybe (MaybeT (..), exceptToMaybeT, maybeToExceptT)
src/Relude/Monad/Trans.hs view
@@ -1,45 +1,104 @@-{-# LANGUAGE Safe #-}+{-# LANGUAGE Trustworthy #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Monad.Trans+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Monad transformers utilities.+Monad transformers utilities.+-} module Relude.Monad.Trans- ( -- * Convenient functions to work with 'Reader' monad- usingReader- , usingReaderT+ ( -- * Convenient functions to work with 'Reader' monad+ usingReader+ , usingReaderT+ , etaReaderT - -- * Convenient functions to work with 'State' monad- , evaluatingState- , evaluatingStateT- , executingState- , executingStateT- , usingState- , usingStateT- ) where+ -- * Convenient functions to work with 'State' monad+ , evaluatingState+ , evaluatingStateT+ , executingState+ , executingStateT+ , usingState+ , usingStateT -import Prelude (flip, fst, snd)+ -- * Lifted to Transformers+ , hoistMaybe+ , hoistEither+ ) where +import GHC.Exts (oneShot)++import Relude.Applicative (Applicative (pure))+import Relude.Container.Reexport (fst, snd)+import Relude.Function (flip, (.)) import Relude.Functor (Functor, (<$>))-import Relude.Monad.Reexport (Reader, ReaderT, State, StateT, runReader, runReaderT, runState,- runStateT)+import Relude.Monad.Reexport (Either, ExceptT (..), Maybe, MaybeT (..), Reader, ReaderT (..), State,+ StateT, runReader, runReaderT, runState, runStateT) --- | Shorter and more readable alias for @flip runReaderT@.++-- $setup+-- >>> import Relude++{- | Shorter and more readable alias for @flip runReaderT@.++>>> usingReaderT 42 $ asks (+5)+47+-} usingReaderT :: r -> ReaderT r m a -> m a usingReaderT = flip runReaderT {-# INLINE usingReaderT #-} --- | Shorter and more readable alias for @flip runReader@.+{- | Shorter and more readable alias for @flip runReader@.++>>> usingReader 42 $ asks (+5)+47+-} usingReader :: r -> Reader r a -> a usingReader = flip runReader {-# INLINE usingReader #-} --- | Shorter and more readable alias for @flip runStateT@.+{- | This function helps with optimizing performance when working with+the 'ReaderT' transformer. If you have code like below, that is+called in a loop++@+step :: Instruction -> 'ReaderT' Config IO Result+step instruction = __case__ instruction __of__+ Add -> __do__ stuff ...+ Del -> __do__ stuff ...+@++you can improve performance of your Haskell applications by using+'etaReaderT' in the following way:++@+step :: Instruction -> 'ReaderT' Config IO Result+step instruction = 'etaReaderT' $ __case__ instruction __of__+ Add -> __do__ stuff ...+ Del -> __do__ stuff ...+@++For a detailed explanation, refer to the following blog post:++* <https://www.joachim-breitner.de/blog/763-Faster_Winter_5__Eta-Expanding_ReaderT Faster Winter 5: Eta-Expanding ReaderT (by Joachim Breitner)>++@since 0.7.0.0+-}+etaReaderT :: ReaderT r m a -> ReaderT r m a+etaReaderT = ReaderT . oneShot . runReaderT+{-# INLINE etaReaderT #-}++{- | Shorter and more readable alias for @flip runStateT@.++>>> usingStateT 0 $ put 42 >> pure False+(False,42)+-} usingStateT :: s -> StateT s m a -> m (a, s) usingStateT = flip runStateT {-# INLINE usingStateT #-}@@ -49,26 +108,46 @@ usingState = flip runState {-# INLINE usingState #-} --- | Alias for @flip evalStateT@. It's not shorter but sometimes--- more readable. Done by analogy with @using*@ functions family.+{- | Alias for @flip evalStateT@. It's not shorter but sometimes+more readable. Done by analogy with @using*@ functions family.+-} evaluatingStateT :: Functor f => s -> StateT s f a -> f a evaluatingStateT s st = fst <$> usingStateT s st {-# INLINE evaluatingStateT #-} --- | Alias for @flip evalState@. It's not shorter but sometimes--- more readable. Done by analogy with @using*@ functions family.+{- | Alias for @flip evalState@. It's not shorter but sometimes+more readable. Done by analogy with @using*@ functions family.+-} evaluatingState :: s -> State s a -> a evaluatingState s st = fst (usingState s st) {-# INLINE evaluatingState #-} --- | Alias for @flip execStateT@. It's not shorter but sometimes--- more readable. Done by analogy with @using*@ functions family.+{- | Alias for @flip execStateT@. It's not shorter but sometimes+more readable. Done by analogy with @using*@ functions family.+-} executingStateT :: Functor f => s -> StateT s f a -> f s executingStateT s st = snd <$> usingStateT s st {-# INLINE executingStateT #-} --- | Alias for @flip execState@. It's not shorter but sometimes--- more readable. Done by analogy with @using*@ functions family.+{- | Alias for @flip execState@. It's not shorter but sometimes+more readable. Done by analogy with @using*@ functions family.+-} executingState :: s -> State s a -> s executingState s st = snd (usingState s st) {-# INLINE executingState #-}++{- | Lift a 'Maybe' to the 'MaybeT' monad++@since 0.3.0+-}+hoistMaybe :: Applicative m => Maybe a -> MaybeT m a+hoistMaybe m = MaybeT (pure m)+{-# INLINE hoistMaybe #-}++{- | Lift a 'Either' to the 'ExceptT' monad++@since 0.3.0+-}+hoistEither :: Applicative m => Either e a -> ExceptT e m a+hoistEither e = ExceptT (pure e)+{-# INLINE hoistEither #-}
src/Relude/Monoid.hs view
@@ -1,34 +1,148 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveGeneric #-}+#if !MIN_VERSION_base(4,12,0)+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE Trustworthy #-}+#else+{-# LANGUAGE Safe #-} --- | This module reexports functions to work with monoids plus adds extra useful functions.+{- |+Module : Relude.Monoid+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +Reexports functions to work with monoids plus adds extra useful functions.+-}++#endif module Relude.Monoid- ( module Data.Monoid- , module Data.Semigroup- , maybeToMonoid- ) where+ ( -- * Reexports+ module Data.Monoid+ , module Data.Semigroup + , Ap (..)++ -- * Combinators+ , maybeToMonoid+ , memptyIfFalse+ , memptyIfTrue+ ) where++#if MIN_VERSION_base(4,12,0)+import Data.Monoid (Ap (..))+#endif import Data.Monoid (All (..), Alt (..), Any (..), Dual (..), Endo (..), First (..), Last (..), Monoid (..), Product (..), Sum (..))-import Data.Semigroup (Option (..), Semigroup (sconcat, stimes, (<>)), WrappedMonoid, cycle1,- mtimesDefault, stimesIdempotent, stimesIdempotentMonoid, stimesMonoid)+import Data.Semigroup (Semigroup (sconcat, stimes, (<>)), WrappedMonoid, cycle1, mtimesDefault,+ stimesIdempotent, stimesIdempotentMonoid, stimesMonoid) +import Relude.Bool.Reexport (Bool (..)) import Relude.Monad.Reexport (Maybe, fromMaybe) +#if !MIN_VERSION_base(4,12,0)+import GHC.Generics (Generic1)++import Relude.Applicative (Alternative, Applicative (..), liftA2)+import Relude.Base (Eq, Generic, Ord, Show)+import Relude.Enum (Bounded (..), Enum)+import Relude.Function (($), (.))+import Relude.Functor.Reexport (Functor (..))+import Relude.Monad.Reexport (Monad, MonadFail, MonadPlus)+import Relude.Numeric (Num (..))+import Relude.String.Reexport (Read)+#endif++ -- $setup--- >>> import Relude.Base (Int) -- >>> import Relude.Monad (Maybe (..))+-- >>> import Relude.Numeric (Int) --- | Extracts 'Monoid' value from 'Maybe' returning 'mempty' if 'Nothing'.------ >>> maybeToMonoid (Just [1,2,3] :: Maybe [Int])--- [1,2,3]--- >>> maybeToMonoid (Nothing :: Maybe [Int])--- []+{- | Extracts 'Monoid' value from 'Maybe' returning 'mempty' if 'Relude.Monad.Reexport.Nothing'.++>>> maybeToMonoid (Just [1,2,3] :: Maybe [Int])+[1,2,3]+>>> maybeToMonoid (Nothing :: Maybe [Int])+[]+-} maybeToMonoid :: Monoid m => Maybe m -> m maybeToMonoid = fromMaybe mempty+{-# INLINE maybeToMonoid #-}++{- | Returns the given value in case of the given predicate is satisfied+(is 'True'). Otherwise, it returns 'mempty'.++>>> memptyIfFalse True (Just "Hello")+Just "Hello"+>>> memptyIfFalse False "Doesn't matter"+""++@since 0.7.0.0+-}+memptyIfFalse :: Monoid m => Bool -> m -> m+memptyIfFalse p val = if p then val else mempty+{-# INLINE memptyIfFalse #-}+++{- | Returns the given value in case of the given predicate is unsatisfied+(is 'False'). Otherwise, it returns 'mempty'.++>>> memptyIfTrue True (Just "Hello")+Nothing+>>> memptyIfTrue False "Does matter"+"Does matter"++@since 0.7.0.0+-}+memptyIfTrue :: Monoid m => Bool -> m -> m+memptyIfTrue p val = if p then mempty else val+{-# INLINE memptyIfTrue #-}++#if !MIN_VERSION_base(4,12,0)+-- | This data type witnesses the lifting of a 'Monoid' into an+-- 'Applicative' pointwise.+--+-- @since 0.5.0+newtype Ap f a = Ap { getAp :: f a }+ deriving ( Alternative+ , Applicative+ , Enum+ , Eq+ , Functor+ , Generic+ , Generic1+ , Monad+ , MonadFail+ , MonadPlus+ , Ord+ , Read+ , Show+ )++-- | @since 0.5.0+instance (Applicative f, Semigroup a) => Semigroup (Ap f a) where+ (Ap x) <> (Ap y) = Ap $ liftA2 (<>) x y++-- | @since 0.5.0+instance (Applicative f, Semigroup a, Monoid a) => Monoid (Ap f a) where+ mempty = Ap $ pure mempty+ mappend = (<>)++-- | @since 0.5.0+instance (Applicative f, Bounded a) => Bounded (Ap f a) where+ minBound = pure minBound+ maxBound = pure maxBound++-- | @since 0.5.0+instance (Applicative f, Num a) => Num (Ap f a) where+ (+) = liftA2 (+)+ (*) = liftA2 (*)+ negate = fmap negate+ fromInteger = pure . fromInteger+ abs = fmap abs+ signum = fmap signum+#endif
src/Relude/Nub.hs view
@@ -1,12 +1,18 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE CPP #-}+{-# LANGUAGE Safe #-} -{-| Functions to remove duplicates from a list.+{- |+Module : Relude.Nub+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +Functions to remove duplicates from a list.+ = Performance To check the performance there was done a bunch of benchmarks. Benchmarks were made on lists of 'Prelude.Int's and 'Data.Text.Text's.@@ -17,63 +23,114 @@ * Lists which consist of many same elements - Here are some recomendations for usage of particular functions based on benchmarking resutls.+ Here are some recommendations for usage of particular functions based on benchmarking results. * 'hashNub' is faster than 'ordNub' when there're not so many different values in the list. * 'hashNub' is the fastest with 'Data.Text.Text'. + * 'intNub' is faster when you work with lists of 'Int's.++ * 'intNubOn' is fast with the lists of type that can have fixed number representations.+ * 'sortNub' has better performance than 'ordNub' but should be used when sorting is also needed. * 'unstableNub' has better performance than 'hashNub' but doesn't save the original order. -} module Relude.Nub- ( hashNub- , ordNub- , sortNub- , unstableNub- ) where+ ( hashNub+ , ordNub +#if MIN_VERSION_containers(0,6,0)+ , ordNubOn+ , intNub+ , intNubOn+#endif++ , sortNub+ , unstableNub+ ) where++#if !MIN_VERSION_hashable(1,4,0) import Data.Eq (Eq)+#endif import Data.Hashable (Hashable) import Data.HashSet as HashSet import Data.Ord (Ord)-import Prelude ((.))+import Prelude (Int, (.)) import qualified Data.Set as Set+#if MIN_VERSION_containers(0,6,0)+import qualified Data.Containers.ListUtils as Containers+#endif -{- | Like 'Prelude.nub' but runs in @O(n * log n)@ time and requires 'Ord'. +-- $setup+-- >>> import Prelude (div, fromEnum)++{- | Removes duplicate elements from a list, keeping only the first occurrence of+the element.++Like 'Prelude.nub' but runs in \( O(n \log n) \) time and requires 'Ord'.+ >>> ordNub [3, 3, 3, 2, 2, -1, 1] [3,2,-1,1] -}-ordNub :: (Ord a) => [a] -> [a]+ordNub :: forall a . (Ord a) => [a] -> [a]+#if MIN_VERSION_containers(0,6,0)+ordNub = Containers.nubOrd+{-# INLINE ordNub #-}+#else ordNub = go Set.empty where+ go :: Set.Set a -> [a] -> [a] go _ [] = [] go s (x:xs) = if x `Set.member` s then go s xs else x : go (Set.insert x s) xs+{-# INLINEABLE ordNub #-}+#endif -{- | Like 'Prelude.nub' but runs in @O(n * log_16(n))@ time and requires 'Hashable'. +#if MIN_VERSION_containers(0,6,0)+{- | Similar to 'ordNub' but performs nub through the mapped list on the given+function.++>>> ordNubOn (`div` 10) [3, 3, 3, 13, 2, 22, -1, 1, 66]+[3,13,22,-1,66]++@since 1.0.0.0+-}+ordNubOn :: forall b a . (Ord b) => (a -> b) -> [a] -> [a]+ordNubOn = Containers.nubOrdOn+{-# INLINE ordNubOn #-}+#endif++{- | Like 'Prelude.nub' but runs in \( O(n \log_{16} n) \) time and requires 'Hashable'.+ >>> hashNub [3, 3, 3, 2, 2, -1, 1] [3,2,-1,1] -}-hashNub :: (Eq a, Hashable a) => [a] -> [a]+#if MIN_VERSION_hashable(1,4,0)+hashNub :: forall a . (Hashable a) => [a] -> [a]+#else+hashNub :: forall a . (Eq a, Hashable a) => [a] -> [a]+#endif hashNub = go HashSet.empty where+ go :: HashSet.HashSet a -> [a] -> [a] go _ [] = [] go s (x:xs) = if x `HashSet.member` s then go s xs else x : go (HashSet.insert x s) xs+{-# INLINEABLE hashNub #-} -{- | Like 'ordNub' but also sorts a list.+{- | Like 'ordNub' runs in \( O(n \log n) \) but also sorts a list. >>> sortNub [3, 3, 3, 2, 2, -1, 1] [-1,1,2,3]@@ -81,12 +138,48 @@ -} sortNub :: (Ord a) => [a] -> [a] sortNub = Set.toList . Set.fromList+{-# INLINE sortNub #-} -{- | Like 'hashNub' but has better performance and also doesn't save the order.+{- | Like 'hashNub' runs in \( O(n \log_{16} n) \) but has better performance; it doesn't save the order. >>> unstableNub [3, 3, 3, 2, 2, -1, 1] [1,2,3,-1] -}+#if MIN_VERSION_hashable(1,4,0)+unstableNub :: (Hashable a) => [a] -> [a]+#else unstableNub :: (Eq a, Hashable a) => [a] -> [a]+#endif unstableNub = HashSet.toList . HashSet.fromList+{-# INLINE unstableNub #-}+++#if MIN_VERSION_containers(0,6,0)+{- | Removes duplicate elements from a list, keeping only the first occurance of+the element.++Like 'Prelude.nub' but runs in \( O (n \min (n, int\_bits )) \) time and requires 'Ord'.++>>> intNub [3, 3, 3, 2, 2, -1, 1]+[3,2,-1,1]++@since 1.0.0.0+-}+intNub :: [Int] -> [Int]+intNub = Containers.nubInt++{-# INLINE intNub #-}++{- | Similar to 'intNub' but works on lists of any types by performing "nubbing" through 'Int's.++>>> intNubOn fromEnum "ababbbcdaffee"+"abcdfe"++@since 1.0.0.0+-}+intNubOn :: (a -> Int) -> [a] -> [a]+intNubOn = Containers.nubIntOn+{-# INLINE intNubOn #-}++#endif
+ src/Relude/Numeric.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE Trustworthy #-}++{- |+Module : Relude.Numeric+Copyright : (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Provides numerical data types and functions.++@since 0.5.0+-}++module Relude.Numeric+ ( -- * Reexports+ module Data.Bits+ , module Data.Int+ , module Data.Word+ , module GHC.Base+ , module GHC.Float+ , module GHC.Num+ , module GHC.Real+ , module Numeric.Natural+ -- * Combinators+ , integerToBounded+ , integerToNatural+ ) where++import Data.Bits (toIntegralSized, xor)+import Data.Int (Int, Int16, Int32, Int64, Int8)+import Data.Word (Word, Word16, Word32, Word64, Word8, byteSwap16, byteSwap32, byteSwap64)+import GHC.Base (maxInt, minInt)+import GHC.Float (Double (..), Float (..), Floating (acos, acosh, asin, asinh, atan, atanh, cos, cosh, exp, logBase, pi, sin, sinh, sqrt, tan, tanh, (**)),+ RealFloat (atan2, decodeFloat, encodeFloat, floatDigits, floatRadix, floatRange, isDenormalized, isIEEE, isInfinite, isNaN, isNegativeZero))+import GHC.Num (Integer, Num (..), subtract)+import GHC.Real (Fractional (..), Integral (..), Ratio, Rational, Real (..), RealFrac (..),+ denominator, even, fromIntegral, gcd, lcm, numerator, odd, realToFrac, (^), (^^))+import Numeric.Natural (Natural)++import Relude.Base ((<), (>))+import Relude.Bool (otherwise)+import Relude.Enum (Bounded (..))+import Relude.Function (($))+import Relude.Monad (Maybe (..))+++-- $setup+-- import Relude.Monad (Maybe (..))++{- | Transforms an integer number to a bounded integral.+It returns `Nothing` for integers outside the bound of the return type.++>>> integerToBounded @Int 42+Just 42++>>> integerToBounded @Int8 1024+Nothing++>>> integerToBounded @Int (toInteger (minBound :: Int))+Just (-9223372036854775808)+>>> integerToBounded @Int $ (toInteger (minBound :: Int)) - 1+Nothing++>>> integerToBounded @Int (toInteger (maxBound :: Int))+Just 9223372036854775807+>>> integerToBounded @Int $ (toInteger (maxBound :: Int)) + 1+Nothing++If you want to convert 'Int' or 'Word' to a bounded type, take a look at+'toIntegralSized' function instead.++@since 0.5.0+-}+integerToBounded :: forall a. (Integral a, Bounded a) => Integer -> Maybe a+integerToBounded n+ | n < toInteger (minBound @a) = Nothing+ | n > toInteger (maxBound @a) = Nothing+ | otherwise = Just (fromIntegral n)+{-# INLINE integerToBounded #-}++{- | Transforms an integer number to a natural.+Only non-negative integers are considered natural, everything else will return `Nothing`.++>>> integerToNatural (-1)+Nothing++>>> integerToNatural 0+Just 0++>>> integerToNatural 10+Just 10++@since 0.5.0+-}+integerToNatural :: Integer -> Maybe Natural+integerToNatural n+ | n < 0 = Nothing+ | otherwise = Just $ fromIntegral n+{-# INLINE integerToNatural #-}
src/Relude/Print.hs view
@@ -1,88 +1,140 @@ {-# LANGUAGE ExplicitForAll #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE Safe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Print+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | Generalization of 'Prelude.putStr' and 'Prelude.putStrLn' functions.+Functions like 'Prelude.putStr' and 'Prelude.putStrLn' but for 'Text', 'LText',+'ByteString' and 'LByteString'.+-} module Relude.Print- ( Print (..)- , print- , putText- , putTextLn- , putLText- , putLTextLn- ) where--import Data.Function ((.))+ ( -- * 'Text' & 'LText'+ putText+ , putTextLn+ , putLText+ , putLTextLn -import Relude.Monad.Reexport (MonadIO, liftIO)+ -- * 'ByteString' & 'LByteString'+ , putBS+ , putBSLn+ , putLBS+ , putLBSLn+ ) where -import qualified Prelude (print, putStr, putStrLn)+import Relude.Function ((.))+import Relude.Monad.Reexport (MonadIO (..))+import Relude.String (ByteString, LByteString, LText, Text) import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as BL--import qualified Data.Text as T+import qualified Data.ByteString.Lazy.Char8 as LBS import qualified Data.Text.IO as T--import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.IO as TL- import qualified Relude.Base as Base --- | Polymorfic over string and lifted to 'MonadIO' printing functions.-class Print a where- putStr :: MonadIO m => a -> m ()- putStrLn :: MonadIO m => a -> m ()+----------------------------------------------------------------------------+-- Text+---------------------------------------------------------------------------- -instance Print T.Text where- putStr = liftIO . T.putStr- putStrLn = liftIO . T.putStrLn+{- | Lifted version of 'T.putStr'. -instance Print TL.Text where- putStr = liftIO . TL.putStr- putStrLn = liftIO . TL.putStrLn+>>> putText "Hello, world!"+Hello, world!+-}+putText :: MonadIO m => Text -> m ()+putText = liftIO . T.putStr+{-# SPECIALIZE putText :: Text -> Base.IO () #-}+{-# INLINE putText #-} -instance Print BS.ByteString where- putStr = liftIO . BS.putStr- putStrLn = liftIO . BS.putStrLn -instance Print BL.ByteString where- putStr = liftIO . BL.putStr- putStrLn = liftIO . BL.putStrLn+{- | Lifted version of 'T.putStrLn'. -instance Print [Base.Char] where- putStr = liftIO . Prelude.putStr- putStrLn = liftIO . Prelude.putStrLn+>>> putTextLn "Hello, world!"+Hello, world!+-}+putTextLn :: MonadIO m => Text -> m ()+putTextLn = liftIO . T.putStrLn+{-# SPECIALIZE putTextLn :: Text -> Base.IO () #-}+{-# INLINE putTextLn #-} --- | Lifted version of 'Prelude.print'.-print :: forall a m . (MonadIO m, Base.Show a) => a -> m ()-print = liftIO . Prelude.print+{- | Lifted version of 'TL.putStr'. --- | Specialized to 'T.Text' version of 'putStr' or forcing type inference.-putText :: MonadIO m => T.Text -> m ()-putText = putStr-{-# SPECIALIZE putText :: T.Text -> Base.IO () #-}+>>> putLText "Hello, world!"+Hello, world!+-}+putLText :: MonadIO m => LText -> m ()+putLText = liftIO . TL.putStr+{-# SPECIALIZE putLText :: LText -> Base.IO () #-}+{-# INLINE putLText #-} --- | Specialized to 'T.Text' version of 'putStrLn' or forcing type inference.-putTextLn :: MonadIO m => T.Text -> m ()-putTextLn = putStrLn-{-# SPECIALIZE putTextLn :: T.Text -> Base.IO () #-}+{- | Lifted version of 'TL.putStrLn'. --- | Specialized to 'TL.Text' version of 'putStr' or forcing type inference.-putLText :: MonadIO m => TL.Text -> m ()-putLText = putStr-{-# SPECIALIZE putLText :: TL.Text -> Base.IO () #-}+>>> putLTextLn "Hello, world!"+Hello, world!+-}+putLTextLn :: MonadIO m => LText -> m ()+putLTextLn = liftIO . TL.putStrLn+{-# SPECIALIZE putLTextLn :: LText -> Base.IO () #-}+{-# INLINE putLTextLn #-} --- | Specialized to 'TL.Text' version of 'putStrLn' or forcing type inference.-putLTextLn :: MonadIO m => TL.Text -> m ()-putLTextLn = putStrLn-{-# SPECIALIZE putLTextLn :: TL.Text -> Base.IO () #-}+----------------------------------------------------------------------------+-- ByteString+----------------------------------------------------------------------------++{- | Lifted version of 'BS.putStr'.++>>> putBS ("Hello, world!" :: ByteString)+Hello, world!++@since 0.3.0+-}+putBS :: MonadIO m => ByteString -> m ()+putBS = liftIO . BS.putStr+{-# SPECIALIZE putBS :: ByteString -> Base.IO () #-}+{-# INLINE putBS #-}+++{- | Lifted version of 'BS.putStrLn'.++>>> putBSLn ("Hello, world!" :: ByteString)+Hello, world!++@since 0.3.0+-}+putBSLn :: MonadIO m => ByteString -> m ()+putBSLn = liftIO . BS.putStrLn+{-# SPECIALIZE putBSLn :: ByteString -> Base.IO () #-}+{-# INLINE putBSLn #-}++{- | Lifted version of 'LBS.putStr'.++>>> putLBS ("Hello, world!" :: LByteString)+Hello, world!++@since 0.3.0+-}+putLBS :: MonadIO m => LByteString -> m ()+putLBS = liftIO . LBS.putStr+{-# SPECIALIZE putLBS :: LByteString -> Base.IO () #-}+{-# INLINE putLBS #-}++{- | Lifted version of 'LBS.putStrLn'.++>>> putLBSLn ("Hello, world!" :: LByteString)+Hello, world!++@since 0.3.0+-}+putLBSLn :: MonadIO m => LByteString -> m ()+putLBSLn = liftIO . LBS.putStrLn+{-# SPECIALIZE putLBSLn :: LByteString -> Base.IO () #-}+{-# INLINE putLBSLn #-}
src/Relude/String.hs view
@@ -1,16 +1,59 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE Safe #-} --- | Type classes for convertion between different string representations.+{- |+Module : Relude.String+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable +Type classes for conversion between different string representations.++The table below represents the @relude@ concept of conversion between the following types:++* 'Text'+* 'String'+* 'ByteString'+* 'LText'+* 'LByteString'+* 'ShortByteString'++++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------++| From \\ To | 'Text' | 'String' | 'ByteString' | 'LText' | 'LByteString' | 'ShortByteString' |++=======================+=====================+==============+==============+====================+========================+==========================++| __'Text'__ | ~ | 'toString' | 'encodeUtf8' | 'toLazy'/'toLText' | 'encodeUtf8' | 'encodeUtf8' |++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------++| __'String'__ | 'toText' | ~ | 'encodeUtf8' | 'toLText' | 'encodeUtf8' | 'encodeUtf8' |++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------++| __'ByteString'__ | 'decodeUtf8' | 'decodeUtf8' | ~ | 'decodeUtf8' | 'toLazy' | 'toShort' |++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------++| __'LText'__ | 'toStrict'/'toText' | 'toString' | 'encodeUtf8' | ~ | 'encodeUtf8' | 'encodeUtf8' |++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------++| __'LByteString'__ | 'decodeUtf8' | 'decodeUtf8' | 'toStrict' | 'decodeUtf8' | ~ | @'toShort' . 'toStrict'@ |++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------++| __'ShortByteString'__ | 'decodeUtf8' | 'decodeUtf8' | 'fromShort' | 'decodeUtf8' |@'toLazy' . 'fromShort'@| ~ |++-----------------------+---------------------+--------------+--------------+--------------------+------------------------+--------------------------++-}+ module Relude.String- ( module Relude.String.Conversion- , module Relude.String.Reexport- ) where+ ( module Relude.String.Reexport+ -- $reexport+ , module Relude.String.Conversion+ -- $conversion+ ) where import Relude.String.Conversion import Relude.String.Reexport++{- $reexport+Reexport data types and functions to work with 'Text', 'ByteString',+'ShortByteString'.+-}+{- $conversion+Conversion functions between 'Text', 'String', 'ByteString'.+Also some read|show helper functions.+-}
src/Relude/String/Conversion.hs view
@@ -1,68 +1,78 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE ExplicitForAll #-}++#if __GLASGOW_HASKELL__ >= 902+{-# OPTIONS_GHC -Wno-operator-whitespace #-}+#endif++{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE UndecidableInstances #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.String.Conversion+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable --- | This module implements type class which allow to have conversion to and--- from 'Text', 'String' and 'ByteString' types (including both strict and lazy--- versions). Usually you need to export 'Text' modules qualified and use--- 'T.pack' \/ 'T.unpack' functions to convert to\/from 'Text'. Now you can--- just use 'toText' \/ 'toString' functions.+This module implements type class which allow to have conversion to and from+'Relude.String.Reexport.Text', 'String' and 'ByteString' types+(including both strict and lazy versions). Usually you need to export+'Relude.String.Reexport.Text' modules qualified and use 'T.pack' \/ 'T.unpack'+functions to convert to\/from 'Relude.String.Reexport.Text'. Now you can just+use 'toText' \/ 'toString' functions.+-} module Relude.String.Conversion- ( -- * Convenient type aliases- LText- , LByteString+ ( -- * Convenient type aliases+ LText+ , LByteString - -- * Conversion type classes- , ConvertUtf8 (..)- , ToString (..)- , ToLText (..)- , ToText (..)- , LazyStrict (..)- , fromLazy- , fromStrict+ -- * Conversion type classes+ , ConvertUtf8 (..)+ , ToText (..)+ , ToLText (..)+ , ToString (..)+ , LazyStrict (..)+ , fromLazy+ , fromStrict - -- * Show and read functions- , readEither- , show- ) where+ -- * Show and read functions+ , readEither+ , show+ ) where -import Data.Bifunctor (first)-import Data.Either (Either)-import Data.Function (id, (.))-import Data.String (String)+import GHC.TypeLits (ErrorMessage (..), Symbol, TypeError)+import Prelude (error) -import Relude.Functor ((<$>))-import Relude.String.Reexport (ByteString, IsString, Read, Text, fromString)+import Relude.Base (Constraint, Type)+import Relude.Function (id, (.))+import Relude.Functor (first, (<$>))+import Relude.Monad.Reexport (Either)+import Relude.String.Reexport (ByteString, IsString, Read, ShortByteString, String, Text, fromShort,+ fromString, toShort) -import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as LB-import qualified Data.ByteString.Lazy.UTF8 as LBU-import qualified Data.ByteString.UTF8 as BU import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Encoding.Error as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Encoding as LT+import qualified GHC.Show as Show (Show (show)) import qualified Text.Read (readEither) -import qualified GHC.Show as Show (Show (show)) -- $setup--- >>> :set -XTypeApplications -XOverloadedStrings--- >>> import Relude.Base (Int)--- >>> import Relude.Function (($))--- >>> import Relude.Print (putStrLn)+-- >>> import Relude -- | Type synonym for 'Data.Text.Lazy.Text'. type LText = LT.Text@@ -83,7 +93,7 @@ -- -- >>> decodeUtf8 @Text @ByteString "\208\191\208\176\209\130\208\176\208\186" -- "\1087\1072\1090\1072\1082"- -- >>> putStrLn $ decodeUtf8 @Text @ByteString "\208\191\208\176\209\130\208\176\208\186"+ -- >>> putTextLn $ decodeUtf8 @Text @ByteString "\208\191\208\176\209\130\208\176\208\186" -- патак decodeUtf8 :: b -> a @@ -96,110 +106,448 @@ >>> decodeUtf8 @Text @ByteString "\208\208\176\209\130\208\176\208\186" "\65533\65533\1090\1072\1082" #endif++#if MIN_VERSION_text(2,0,2) >>> decodeUtf8Strict @Text @ByteString "\208\208\176\209\130\208\176\208\186"- Left Cannot decode byte '\xd0': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream+ Left Cannot decode byte '\xd0': ...: Invalid UTF-8 stream+#elif MIN_VERSION_text(2,0,0)+ >>> decodeUtf8Strict @Text @ByteString "\208\208\176\209\130\208\176\208\186"+ Left Cannot decode byte '\xd0': ...: Invalid UTF-8 stream+#else+ >>> decodeUtf8Strict @Text @ByteString "\208\208\176\209\130\208\176\208\186"+ Left Cannot decode byte '\xd0': ...: Invalid UTF-8 stream+#endif -} decodeUtf8Strict :: b -> Either T.UnicodeException a -instance ConvertUtf8 String B.ByteString where- encodeUtf8 = BU.fromString- decodeUtf8 = BU.toString+instance ConvertUtf8 String ByteString where+ encodeUtf8 :: String -> ByteString+ encodeUtf8 = T.encodeUtf8 . T.pack+ {-# INLINE encodeUtf8 #-}++ decodeUtf8 :: ByteString -> String+ decodeUtf8 = T.unpack . T.decodeUtf8+ {-# INLINE decodeUtf8 #-}++ decodeUtf8Strict :: ByteString -> Either T.UnicodeException String decodeUtf8Strict = (T.unpack <$>) . decodeUtf8Strict+ {-# INLINE decodeUtf8Strict #-} -instance ConvertUtf8 T.Text B.ByteString where+instance ConvertUtf8 Text ByteString where+ encodeUtf8 :: Text -> ByteString encodeUtf8 = T.encodeUtf8+ {-# INLINE encodeUtf8 #-}++ decodeUtf8 :: ByteString -> Text decodeUtf8 = T.decodeUtf8With T.lenientDecode+ {-# INLINE decodeUtf8 #-}++ decodeUtf8Strict :: ByteString -> Either T.UnicodeException Text decodeUtf8Strict = T.decodeUtf8'+ {-# INLINE decodeUtf8Strict #-} -instance ConvertUtf8 LT.Text B.ByteString where+instance ConvertUtf8 LText ByteString where+ encodeUtf8 :: LText -> ByteString encodeUtf8 = LB.toStrict . encodeUtf8+ {-# INLINE encodeUtf8 #-}++ decodeUtf8 :: ByteString -> LText decodeUtf8 = LT.decodeUtf8With T.lenientDecode . LB.fromStrict+ {-# INLINE decodeUtf8 #-}++ decodeUtf8Strict :: ByteString -> Either T.UnicodeException LText decodeUtf8Strict = decodeUtf8Strict . LB.fromStrict+ {-# INLINE decodeUtf8Strict #-} -instance ConvertUtf8 String LB.ByteString where- encodeUtf8 = LBU.fromString- decodeUtf8 = LBU.toString+{- | Converting 'String' to 'LB.ByteString' might be a slow operation.+Consider using lazy bytestring at first place.+-}+instance ConvertUtf8 String LByteString where+ encodeUtf8 :: String -> LByteString+ encodeUtf8 = LT.encodeUtf8 . LT.pack+ {-# INLINE encodeUtf8 #-}++ decodeUtf8 :: LByteString -> String+ decodeUtf8 = LT.unpack . LT.decodeUtf8+ {-# INLINE decodeUtf8 #-}++ decodeUtf8Strict :: LByteString -> Either T.UnicodeException String decodeUtf8Strict = (T.unpack <$>) . decodeUtf8Strict+ {-# INLINE decodeUtf8Strict #-} -instance ConvertUtf8 T.Text LB.ByteString where+instance ConvertUtf8 Text LByteString where+ encodeUtf8 :: Text -> LByteString encodeUtf8 = LB.fromStrict . T.encodeUtf8+ {-# INLINE encodeUtf8 #-}++ decodeUtf8 :: LByteString -> Text decodeUtf8 = T.decodeUtf8With T.lenientDecode . LB.toStrict+ {-# INLINE decodeUtf8 #-}++ decodeUtf8Strict :: LByteString -> Either T.UnicodeException Text decodeUtf8Strict = T.decodeUtf8' . LB.toStrict+ {-# INLINE decodeUtf8Strict #-} -instance ConvertUtf8 LT.Text LB.ByteString where+instance ConvertUtf8 LText LByteString where+ encodeUtf8 :: LText -> LByteString encodeUtf8 = LT.encodeUtf8+ {-# INLINE encodeUtf8 #-}++ decodeUtf8 :: LByteString -> LText decodeUtf8 = LT.decodeUtf8With T.lenientDecode+ {-# INLINE decodeUtf8 #-}++ decodeUtf8Strict :: LByteString -> Either T.UnicodeException LText decodeUtf8Strict = LT.decodeUtf8'+ {-# INLINE decodeUtf8Strict #-} +-- | @since 0.6.0.0+instance ConvertUtf8 String ShortByteString where+ encodeUtf8 :: String -> ShortByteString+ encodeUtf8 = toShort . encodeUtf8+ {-# INLINE encodeUtf8 #-}++ decodeUtf8 :: ShortByteString -> String+ decodeUtf8 = decodeUtf8 . fromShort+ {-# INLINE decodeUtf8 #-}++ decodeUtf8Strict :: ShortByteString -> Either T.UnicodeException String+ decodeUtf8Strict = decodeUtf8Strict . fromShort+ {-# INLINE decodeUtf8Strict #-}++-- | @since 0.6.0.0+instance ConvertUtf8 Text ShortByteString where+ encodeUtf8 :: Text -> ShortByteString+ encodeUtf8 = toShort . encodeUtf8+ {-# INLINE encodeUtf8 #-}++ decodeUtf8 :: ShortByteString -> Text+ decodeUtf8 = decodeUtf8 . fromShort+ {-# INLINE decodeUtf8 #-}++ decodeUtf8Strict :: ShortByteString -> Either T.UnicodeException Text+ decodeUtf8Strict = decodeUtf8Strict . fromShort+ {-# INLINE decodeUtf8Strict #-}++-- | @since 0.6.0.0+instance ConvertUtf8 LText ShortByteString where+ encodeUtf8 :: LText -> ShortByteString+ encodeUtf8 = toShort . encodeUtf8+ {-# INLINE encodeUtf8 #-}++ decodeUtf8 :: ShortByteString -> LText+ decodeUtf8 = decodeUtf8 . fromShort+ {-# INLINE decodeUtf8 #-}++ decodeUtf8Strict :: ShortByteString -> Either T.UnicodeException LText+ decodeUtf8Strict = decodeUtf8Strict . fromShort+ {-# INLINE decodeUtf8Strict #-}+ -- | Type class for converting other strings to 'T.Text'. class ToText a where- toText :: a -> T.Text+ toText :: a -> Text instance ToText String where+ toText :: String -> Text toText = T.pack+ {-# INLINE toText #-} -instance ToText T.Text where+instance ToText Text where+ toText :: Text -> Text toText = id+ {-# INLINE toText #-} -instance ToText LT.Text where+instance ToText LText where+ toText :: LText -> Text toText = LT.toStrict+ {-# INLINE toText #-} +{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.++You should always specify encoding of bytes explicitly.++In case it is used by mistake, the user will see the following:++>>> toText ("some string" :: ByteString)+...+... Type 'ByteString' doesn't have instance of 'ToText'.+ Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:+ decodeUtf8 :: ByteString -> Text+ decodeUtf8Strict :: ByteString -> Either UnicodeException Text+...++@since 0.6.0.0+-}+instance EncodingError ToText "ByteString" "Text" => ToText ByteString where+ toText :: ByteString -> Text+ toText = error "Unreachable ByteString instance of ToText"++{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.++You should always specify encoding of bytes explicitly.++In case it is used by mistake, the user will see the following:++>>> toText ("some string" :: LByteString)+...+... Type 'LByteString' doesn't have instance of 'ToText'.+ Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:+ decodeUtf8 :: LByteString -> Text+ decodeUtf8Strict :: LByteString -> Either UnicodeException Text+...++@since 0.6.0.0+-}+instance EncodingError ToText "LByteString" "Text" => ToText LByteString where+ toText :: LByteString -> Text+ toText = error "Unreachable LByteString instance of ToText"++{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.++You should always specify encoding of bytes explicitly.++In case it is used by mistake, the user will see the following:++>>> toText ("some string" :: ShortByteString)+...+... Type 'ShortByteString' doesn't have instance of 'ToText'.+ Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:+ decodeUtf8 :: ShortByteString -> Text+ decodeUtf8Strict :: ShortByteString -> Either UnicodeException Text+...++@since 0.6.0.0+-}+instance EncodingError ToText "ShortByteString" "Text" => ToText ShortByteString where+ toText :: ShortByteString -> Text+ toText = error "Unreachable ShortByteString instance of ToText"+ -- | Type class for converting other strings to 'LT.Text'. class ToLText a where- toLText :: a -> LT.Text+ toLText :: a -> LText instance ToLText String where+ toLText :: String -> LText toLText = LT.pack+ {-# INLINE toLText #-} -instance ToLText T.Text where+instance ToLText Text where+ toLText :: Text -> LText toLText = LT.fromStrict+ {-# INLINE toLText #-} instance ToLText LT.Text where+ toLText :: LText -> LText toLText = id+ {-# INLINE toLText #-} +{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.++You should always specify encoding of bytes explicitly.++In case it is used by mistake, the user will see the following:++>>> toLText ("some string" :: ByteString)+...+... Type 'ByteString' doesn't have instance of 'ToLText'.+ Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:+ decodeUtf8 :: ByteString -> LText+ decodeUtf8Strict :: ByteString -> Either UnicodeException LText+...++@since 0.6.0.0+-}+instance EncodingError ToLText "ByteString" "LText" => ToLText ByteString where+ toLText :: ByteString -> LText+ toLText = error "Unreachable ByteString instance of ToLText"++{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.++You should always specify encoding of bytes explicitly.++In case it is used by mistake, the user will see the following:++>>> toLText ("some string" :: LByteString)+...+... Type 'LByteString' doesn't have instance of 'ToLText'.+ Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:+ decodeUtf8 :: LByteString -> LText+ decodeUtf8Strict :: LByteString -> Either UnicodeException LText+...++@since 0.6.0.0+-}+instance EncodingError ToLText "LByteString" "LText" => ToLText LByteString where+ toLText :: LByteString -> LText+ toLText = error "Unreachable LByteString instance of ToLText"++{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.++You should always specify encoding of bytes explicitly.++In case it is used by mistake, the user will see the following:++>>> toLText ("some string" :: ShortByteString)+...+... Type 'ShortByteString' doesn't have instance of 'ToLText'.+ Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:+ decodeUtf8 :: ShortByteString -> LText+ decodeUtf8Strict :: ShortByteString -> Either UnicodeException LText+...++@since 0.6.0.0+-}+instance EncodingError ToLText "ShortByteString" "LText" => ToLText ShortByteString where+ toLText :: ShortByteString -> LText+ toLText = error "Unreachable ShortByteString instance of ToLText"+ -- | Type class for converting other strings to 'String'. class ToString a where toString :: a -> String instance ToString String where+ toString :: String -> String toString = id+ {-# INLINE toString #-} -instance ToString T.Text where+instance ToString Text where+ toString :: Text -> String toString = T.unpack+ {-# INLINE toString #-} -instance ToString LT.Text where+instance ToString LText where+ toString :: LText -> String toString = LT.unpack+ {-# INLINE toString #-} --- | Polymorhpic version of 'Text.Read.readEither'.------ >>> readEither @Text @Int "123"--- Right 123--- >>> readEither @Text @Int "aa"--- Left "Prelude.read: no parse"-readEither :: (ToString a, Read b) => a -> Either Text b-readEither = first toText . Text.Read.readEither . toString+{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only. --- | Generalized version of 'Prelude.show'.+You should always specify encoding of bytes explicitly.++In case it is used by mistake, the user will see the following:++>>> toString ("some string" :: ByteString)+...+... Type 'ByteString' doesn't have instance of 'ToString'.+ Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:+ decodeUtf8 :: ByteString -> String+ decodeUtf8Strict :: ByteString -> Either UnicodeException String+...++@since 0.6.0.0+-}+instance EncodingError ToString "ByteString" "String" => ToString ByteString where+ toString :: ByteString -> String+ toString = error "Unreachable ByteString instance of ToString"++{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.++You should always specify encoding of bytes explicitly.++In case it is used by mistake, the user will see the following:++>>> toString ("some string" :: LByteString)+...+... Type 'LByteString' doesn't have instance of 'ToString'.+ Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:+ decodeUtf8 :: LByteString -> String+ decodeUtf8Strict :: LByteString -> Either UnicodeException String+...++@since 0.6.0.0+-}+instance EncodingError ToString "LByteString" "String" => ToString LByteString where+ toString :: LByteString -> String+ toString = error "Unreachable LByteString instance of ToString"++{- | ⚠️__CAUTION__⚠️ This instance is for custom error display only.++You should always specify encoding of bytes explicitly.++In case it is used by mistake, the user will see the following:++>>> toString ("some string" :: ShortByteString)+...+... Type 'ShortByteString' doesn't have instance of 'ToString'.+ Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:+ decodeUtf8 :: ShortByteString -> String+ decodeUtf8Strict :: ShortByteString -> Either UnicodeException String+...++@since 0.6.0.0+-}+instance EncodingError ToString "ShortByteString" "String" => ToString ShortByteString where+ toString :: ShortByteString -> String+ toString = error "Unreachable ShortByteString instance of ToString"++-- | Helper type family to produce error messages+type family EncodingError+ (c :: Type -> Constraint)+ (from :: Symbol)+ (to :: Symbol)+ :: Constraint+ where+ EncodingError c from to = TypeError+ ( 'Text "Type '" ':<>: 'Text from ':<>: 'Text "' doesn't have instance of '"+ ':<>: 'ShowType c ':<>: 'Text "'."+ ':$$: 'Text "Use 'decodeUtf8' or 'decodeUtf8Strict' to convert from UTF-8:"+ ':$$: 'Text " decodeUtf8 :: " ':<>: 'Text from+ ':<>: 'Text " -> " ':<>: 'Text to+ ':$$: 'Text " decodeUtf8Strict :: " ':<>: 'Text from+ ':<>: 'Text " -> Either UnicodeException " ':<>: 'Text to+ )++{- | Version of 'Text.Read.readEither' that returns 'Text' in case of the parse+error.++>>> readEither @Int "123"+Right 123+>>> readEither @Int "aa"+Left "Prelude.read: no parse"+-}+readEither :: (Read a) => String -> Either Text a+readEither = first toText . Text.Read.readEither+{-# INLINEABLE readEither #-}++{- | Generalized version of 'Prelude.show'. Unlike 'Prelude.show' this function+is polymorphic in its result type. This makes it more convenient to work with+data types like 'Relude.String.Reexport.Text' or 'ByteString'. However, if you+pass the result of 'show' to a function that expects polymorphic argument, this+can break type inference, so use @-XTypeApplications@ to specify the textual+type explicitly.++>>> show (42 :: Int)+"42"+>>> show (42 :: Double)+"42.0"+>>> print (show @Text True)+"True"+-} show :: forall b a . (Show.Show a, IsString b) => a -> b show x = fromString (Show.show x)+{-# INLINE show #-} {-# SPECIALIZE show :: Show.Show a => a -> Text #-} {-# SPECIALIZE show :: Show.Show a => a -> LText #-} {-# SPECIALIZE show :: Show.Show a => a -> ByteString #-} {-# SPECIALIZE show :: Show.Show a => a -> LByteString #-} {-# SPECIALIZE show :: Show.Show a => a -> String #-} +{- | Type class for lazy-strict conversions. --- | Type class for lazy-strict conversions.+@since 0.1.0+-} class LazyStrict l s | l -> s, s -> l where- toLazy :: s -> l+ toLazy :: s -> l toStrict :: l -> s +-- | Alias for 'toStrict' function. fromLazy :: LazyStrict l s => l -> s fromLazy = toStrict {-# INLINE fromLazy #-} {-# SPECIALIZE fromLazy :: LByteString -> ByteString #-} {-# SPECIALIZE fromLazy :: LText -> Text #-} +-- | Alias for 'toLazy' function. fromStrict :: LazyStrict l s => s -> l fromStrict = toLazy {-# INLINE fromStrict #-}@@ -207,13 +555,19 @@ {-# SPECIALIZE fromStrict :: Text -> LText #-} instance LazyStrict LByteString ByteString where+ toLazy :: ByteString -> LByteString toLazy = LB.fromStrict {-# INLINE toLazy #-}++ toStrict :: LByteString -> ByteString toStrict = LB.toStrict {-# INLINE toStrict #-} instance LazyStrict LText Text where+ toLazy :: Text -> LText toLazy = LT.fromStrict {-# INLINE toLazy #-}++ toStrict :: LText -> Text toStrict = LT.toStrict {-# INLINE toStrict #-}
src/Relude/String/Reexport.hs view
@@ -1,30 +1,227 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{-# LANGUAGE CPP #-} --- | This module reexports functions to work with 'Text' and 'ByteString' types.+#if __GLASGOW_HASKELL__ >= 902+{-# OPTIONS_GHC -Wno-operator-whitespace #-}+#endif +{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++{- |+Module : Relude.String.Reexport+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable++Reexports functions to work with 'Data.Text.Text', 'ByteString' and+'ShortByteString' types.+-}+ module Relude.String.Reexport- ( -- * String- module Data.String+ ( -- * String+ module Data.String+ , module Text.Read - -- * Text- , module Data.Text- , module Data.Text.Encoding- , module Data.Text.Encoding.Error- , module Text.Read+ -- * Text+ , Text+ , lines+ , unlines+ , words+ , unwords+ , module Data.Text.Encoding+ , module Data.Text.Encoding.Error - -- * ByteString- , module Data.ByteString- ) where+ -- * ByteString+ , ByteString + -- * ShortByteString+ , ShortByteString+ , toShort+ , fromShort+ ) where+ import Data.ByteString (ByteString)-import Data.String (IsString (..))-import Data.Text (Text, lines, unlines, unwords, words)+import Data.ByteString.Short (ShortByteString, fromShort, toShort)+import Data.String (IsString (..), String)+import Data.Text (Text) import Data.Text.Encoding (decodeUtf8', decodeUtf8With) import Data.Text.Encoding.Error (OnDecodeError, OnError, UnicodeException, lenientDecode, strictDecode)+#if __GLASGOW_HASKELL__ >= 904+import Data.Type.Equality (type (~))+#endif+import GHC.TypeLits (ErrorMessage (..), Symbol, TypeError) import Text.Read (Read, readMaybe, reads)++import Relude.Base (Constraint, Type)++import qualified Data.Text as Text+++-- $setup+-- >>> import Relude++-- | For tracking usage of Text instead of String+type IsText+ (t :: Type) -- Textual type, e.g. Text, String+ (fun :: Symbol) -- Function name+ = (t ~ Text, CheckText t fun)++type family CheckText (t :: Type) (fun :: Symbol) :: Constraint where+ CheckText Text _ = ()+ CheckText String fun = TypeError+ ( 'Text "'" ':<>: 'Text fun ':<>: 'Text "' works with 'Text', not 'String'."+ ':$$: 'Text "Possible fixes:"+ ':$$: 'Text " 1. Make sure OverloadedStrings extension is enabled."+ ':$$: 'Text " 2. Apply 'toText' to a single value."+ ':$$: 'Text " 3. Apply 'map toText' to the list value."+ )+ CheckText a fun = TypeError+ ( 'Text "'" ':<>: 'Text fun ':<>: 'Text "' works with 'Text'"+ ':$$: 'Text "But given: '" ':<>: 'ShowType a ':<>: 'Text "'"+ )++{- | 'lines' takes 'Data.Text.Text' and splits it into the list by lines.++Actual type of this function is the following:++@+lines :: 'Data.Text.Text' -> ['Data.Text.Text']+@++but it was given a more complex type to provide friendlier compile time errors.++>>> lines ""+[]+>>> lines "one line"+["one line"]+>>> lines "line 1\nline 2"+["line 1","line 2"]+>>> lines ("string line" :: String)+...+... 'lines' works with 'Text', not 'String'.+ Possible fixes:+ 1. Make sure OverloadedStrings extension is enabled.+ 2. Apply 'toText' to a single value.+ 3. Apply 'map toText' to the list value.+...+>>> lines True+...+... 'lines' works with 'Text'+ But given: 'Bool'+...+-}+lines :: IsText t "lines" => t -> [t]+lines = Text.lines+{-# INLINE lines #-}++{- | 'unlines' takes list of 'Data.Text.Text' values and joins them with line separator.++Actual type of this function is the following:++@+unlines :: ['Data.Text.Text'] -> 'Data.Text.Text'+@++but it was given a more complex type to provide friendlier compile time errors.++>>> unlines []+""+>>> unlines ["line 1"]+"line 1\n"+>>> unlines ["first line", "second line"]+"first line\nsecond line\n"+>>> unlines (["line 1", "line 2"] :: [String])+...+... 'unlines' works with 'Text', not 'String'.+ Possible fixes:+ 1. Make sure OverloadedStrings extension is enabled.+ 2. Apply 'toText' to a single value.+ 3. Apply 'map toText' to the list value.+...+>>> unlines [True, False]+...+... 'unlines' works with 'Text'+ But given: 'Bool'+...+-}+unlines :: IsText t "unlines" => [t] -> t+unlines = Text.unlines+{-# INLINE unlines #-}++{- | 'words' takes 'Data.Text.Text' and splits it into the list by words.++Actual type of this function is the following:++@+words :: 'Data.Text.Text' -> ['Data.Text.Text']+@++but it was given a more complex type to provide friendlier compile time errors.++>>> words ""+[]+>>> words "one line"+["one","line"]+>>> words " >_< "+[">_<"]+>>> words ("string words" :: String)+...+... 'words' works with 'Text', not 'String'.+ Possible fixes:+ 1. Make sure OverloadedStrings extension is enabled.+ 2. Apply 'toText' to a single value.+ 3. Apply 'map toText' to the list value.+...+>>> words True+...+... 'words' works with 'Text'+ But given: 'Bool'+...+-}+words :: IsText t "words" => t -> [t]+words = Text.words+{-# INLINE words #-}++{- | 'unwords' takes list of 'Data.Text.Text' values and joins them with space character.++Actual type of this function is the following:++@+unwords :: ['Data.Text.Text'] -> 'Data.Text.Text'+@++but it was given a more complex type to provide friendlier compile time errors.++>>> unwords []+""+>>> unwords ["singleWord"]+"singleWord"+>>> unwords ["word", "another"]+"word another"+>>> unwords (["word", "another"] :: [String])+...+... 'unwords' works with 'Text', not 'String'.+ Possible fixes:+ 1. Make sure OverloadedStrings extension is enabled.+ 2. Apply 'toText' to a single value.+ 3. Apply 'map toText' to the list value.+...+>>> unwords [True, False]+...+... 'unwords' works with 'Text'+ But given: 'Bool'+...+-}+unwords :: IsText t "unwords" => [t] -> t+unwords = Text.unwords+{-# INLINE unwords #-}
src/Relude/Unsafe.hs view
@@ -1,42 +1,78 @@ {-# LANGUAGE Unsafe #-} -{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}+{- |+Module : Relude.Unsafe+Copyright : (c) 2016 Stephen Diehl+ (c) 2016-2018 Serokell+ (c) 2018-2023 Kowainik+SPDX-License-Identifier : MIT+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable -{- | Unsafe functions to work with lists and 'Maybe'.-Sometimes unavoidable but better don't use them. This module-is intended to be imported qualified and it's not even included-in default prelude exports.+__⚠️ Warning ⚠️__ +This module contains unsafe partial functions. They are unavoidable+sometimes, but we encourage you to use safer analogues:+++-----------------------------------+---------------------------------------------------------------------++| __Partial__ | __Total__ |++===================================+=====================================================================++| @'head' :: [a] -> a@ | @'Relude.List.NonEmpty.head' :: NonEmpty a -> a@ |++-----------------------------------+---------------------------------------------------------------------++| @'tail' :: [a] -> [a]@ | @'Relude.List.NonEmpty.tail' :: NonEmpty a -> [a]@ |++-----------------------------------+---------------------------------------------------------------------++| @'read' :: Read a => String -> a@ | @'Relude.String.Reexport.readMaybe' :: Read a => String -> Maybe a@ |++-----------------------------------+---------------------------------------------------------------------++| @'fromJust' :: Maybe a -> a@ | @'Relude.Monad.Reexport.fromMaybe' :: a -> Maybe a -> a@ |++-----------------------------------+---------------------------------------------------------------------+++This module is intended to be imported qualified and it is not+included in default prelude exports.+ @-import qualified Relude.Unsafe as Unsafe+__import qualified__ Relude.Unsafe as Unsafe foo :: [a] -> a-foo = Unsafe.head+foo = Unsafe.'head' @- -} module Relude.Unsafe- ( head- , tail- , init- , last- , at- , (!!)- , fromJust- ) where+ ( -- * Unsafe list functions+ head+ , tail+ , last+ , init+ , (!!)+ , at+ -- * Unsafe 'Data.Maybe.Maybe' functions+ , fromJust+ -- * Unsafe "Text.Read" functions+ , read+ ) where import Data.List (head, init, last, tail, (!!)) import Data.Maybe (fromJust)+import Text.Read (read) -import Relude.Base (Int) import Relude.Function (flip)+import Relude.Numeric (Int) --- | Similar to '!!' but with flipped arguments.++-- $setup+-- >>> import Relude++{- | Similar to '!!' but with flipped arguments.+get element from list using index value starting from `0`.++>>> at 2 ["a", "b", "c"]+"c"++it is also useful when used in a partially applied position like:++>>> map (at 1) [["a","b","c"], ["a","b","c"], ["a","b","c"]]+["b","b","b"]+-} at :: Int -> [a] -> a at = flip (!!)
test/Doctest.hs view
@@ -1,16 +1,18 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}- module Main (main) where +import Relude+ import System.FilePath.Glob (glob) import Test.DocTest (doctest) main :: IO () main = do sourceFiles <- glob "src/**/*.hs"- doctest $ "-XNoImplicitPrelude" : sourceFiles+ doctest+ $ "-XHaskell2010"+ : "-XInstanceSigs"+ : "-XNoImplicitPrelude"+ : "-XOverloadedStrings"+ : "-XScopedTypeVariables"+ : "-XTypeApplications"+ : sourceFiles
test/Spec.hs view
@@ -1,18 +1,19 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}--module Main where+module Main (main) where import Relude -import Test.Tasty (defaultMain)+import Hedgehog (checkParallel)+import System.IO (hSetEncoding, utf8)+import Test.Relude.Property (hedgehogTestList) -import Test.Relude.Property (hedgehogTestTree) main :: IO () main = do- defaultMain hedgehogTestTree+ -- fix terminal encoding+ hSetEncoding stdout utf8+ hSetEncoding stderr utf8++ mapM checkParallel hedgehogTestList >>= \p ->+ if and p+ then exitSuccess+ else exitFailure
+ test/Test/Relude/Container/One.hs view
@@ -0,0 +1,114 @@+module Test.Relude.Container.One+ ( oneProps+ ) where++import Relude++import Hedgehog (Gen, Group (..), Property, forAll, property, (===))++import Test.Relude.Gen (genInt, genUtf8Text)++import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.ByteString.Short as SBS+import qualified Data.IntSet as IntSet+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range+++oneProps :: Group+oneProps = Group "'One' typeclass property tests"+ [ ( "length (one @[a] x) ≡ 1", oneListProp)+ , ( "length (one @(NonEmpty a) x) ≡ 1", oneNonEmptyProp)+ , ( "length (one @(Seq a) x) ≡ 1", oneSeqProp)+ , ( "length (one @Text x) ≡ 1", oneTextProp)+ , ( "length (one @LText x) ≡ 1", oneLTextProp)+ , ( "length (one @ByteString x) ≡ 1", oneByteStringProp)+ , ( "length (one @LByteString x) ≡ 1", oneLByteStringProp)+ , ( "length (one @ShortByteString x) ≡ 1", oneShortByteStringProp)+ , ( "length (one @(Map k v) x) ≡ 1", oneMapProp)+ , ( "length (one @(HashMap k v) x) ≡ 1", oneHashMapProp)+ , ( "length (one @(IntMap v) x) ≡ 1", oneIntMapProp)+ , ( "length (one @(Set a) x) ≡ 1", oneSetProp)+ , ( "length (one @(HashSet a) x) ≡ 1", oneHashSetProp)+ , ( "length (one @(IntSet x) ≡ 1", oneIntSetProp)+ ]++oneListProp :: Property+oneListProp = property $ do+ x <- forAll genInt+ length (one @[Int] x) === 1++oneNonEmptyProp :: Property+oneNonEmptyProp = property $ do+ x <- forAll genInt+ length (one @(NonEmpty Int) x) === 1++oneSeqProp :: Property+oneSeqProp = property $ do+ x <- forAll genInt+ length (one @(Seq Int) x) === 1++oneTextProp :: Property+oneTextProp = property $ do+ x <- forAll Gen.unicode+ T.length (one @Text x) === 1++oneLTextProp :: Property+oneLTextProp = property $ do+ x <- forAll Gen.unicode+ TL.length (one @LText x) === 1++oneByteStringProp :: Property+oneByteStringProp = property $ do+ x <- forAll genWord8+ BS.length (one @ByteString x) === 1++oneLByteStringProp :: Property+oneLByteStringProp = property $ do+ x <- forAll genWord8+ LBS.length (one @LByteString x) === 1++oneShortByteStringProp :: Property+oneShortByteStringProp = property $ do+ x <- forAll genWord8+ SBS.length (one @ShortByteString x) === 1++oneMapProp :: Property+oneMapProp = property $ do+ k <- forAll genUtf8Text+ v <- forAll genInt+ length (one @(Map Text Int) (k, v)) === 1++oneHashMapProp :: Property+oneHashMapProp = property $ do+ k <- forAll genUtf8Text+ v <- forAll genInt+ length (one @(HashMap Text Int) (k, v)) === 1++oneIntMapProp :: Property+oneIntMapProp = property $ do+ k <- forAll genInt+ v <- forAll genUtf8Text+ length (one @(IntMap Text) (k, v)) === 1++oneSetProp :: Property+oneSetProp = property $ do+ v <- forAll genUtf8Text+ length (one @(Set Text) v) === 1++oneHashSetProp :: Property+oneHashSetProp = property $ do+ v <- forAll genUtf8Text+ length (one @(HashSet Text) v) === 1++oneIntSetProp :: Property+oneIntSetProp = property $ do+ v <- forAll genInt+ IntSet.size (one v) === 1+++genWord8 :: Gen Word8+genWord8 = Gen.word8 Range.constantBounded
+ test/Test/Relude/Gen.hs view
@@ -0,0 +1,36 @@+module Test.Relude.Gen+ ( genInt+ -- * Strings+ , genUtf8String+ , genUtf8Text+ , genUtf8ByteString+ -- * Lists+ , genIntList+ , genBoolList+ ) where++import Relude++import Hedgehog (Gen)++import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range+++genInt :: Gen Int+genInt = Gen.enumBounded++genUtf8String :: Gen String+genUtf8String = Gen.string (Range.linear 0 1000) Gen.unicode++genUtf8Text :: Gen Text+genUtf8Text = Gen.text (Range.linear 0 1000) Gen.unicode++genUtf8ByteString :: Gen ByteString+genUtf8ByteString = Gen.utf8 (Range.linear 0 1000) Gen.unicode++genIntList :: Gen [Int]+genIntList = Gen.list (Range.linear 0 1000) Gen.enumBounded++genBoolList :: Gen [Bool]+genBoolList = Gen.list (Range.linear 0 1000) Gen.bool
test/Test/Relude/Property.hs view
@@ -1,133 +1,108 @@-{--Copyright: (c) 2016 Stephen Diehl- (c) 20016-2018 Serokell- (c) 2018 Kowainik-License: MIT--}- module Test.Relude.Property- ( hedgehogTestTree- ) where+ ( hedgehogTestList+ ) where import Relude import Data.List (nub)-import Hedgehog (Gen, MonadGen, Property, assert, forAll, property, (===))-import Test.Tasty (TestTree, testGroup)-import Test.Tasty.Hedgehog+import Hedgehog (Group (..), Property, assert, forAll, property, (===)) -import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as LB-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range-import qualified Relude as U+import Test.Relude.Container.One (oneProps)+import Test.Relude.Gen (genBoolList, genIntList, genUtf8ByteString, genUtf8String, genUtf8Text) -hedgehogTestTree :: TestTree-hedgehogTestTree = testGroup "Tests" [utfProps, listProps, boolMProps] -utfProps :: TestTree-utfProps = testGroup "utf8 conversion property tests"- [ testProperty "String to ByteString invertible" prop_StringToBytes- , testProperty "Text to ByteString invertible" prop_TextToBytes- , testProperty "ByteString to Text or String invertible" prop_BytesTo+hedgehogTestList :: [Group]+hedgehogTestList =+ [ utfProps+ , listProps+ , logicProps+ , oneProps ] -unicode' :: MonadGen m => m U.Char-unicode' = do- a <- Gen.unicode- if elem a ['\65534', '\65535']- then unicode'- else return a--utf8String :: Gen U.String-utf8String = Gen.string (Range.linear 0 10000) unicode'--utf8Text :: Gen T.Text-utf8Text = Gen.text (Range.linear 0 10000) unicode'+----------------------------------------------------------------------------+-- utf8 conversion+---------------------------------------------------------------------------- -utf8Bytes :: Gen B.ByteString-utf8Bytes = Gen.utf8 (Range.linear 0 10000) unicode'+utfProps :: Group+utfProps = Group "utf8 conversion property tests"+ [ ("String to ByteString invertible:", prop_StringToBytes)+ , ("Text to ByteString invertible:", prop_TextToBytes)+ , ("ByteString to Text or String invertible:" , prop_BytesTo)+ ] -- "\65534" fails, but this is from BU.toString -- > import qualified Data.ByteString.UTF8 as BU -- > BU.toString (BU.fromString "\65534") == "\65533" -- > True- prop_StringToBytes :: Property prop_StringToBytes = property $ do- str <- forAll utf8String- assert $ str == (decodeUtf8 (encodeUtf8 str :: B.ByteString))- && str == (decodeUtf8 (encodeUtf8 str :: LB.ByteString))+ str <- forAll genUtf8String+ assert $ str == decodeUtf8 @_ @ByteString (encodeUtf8 str)+ && str == decodeUtf8 @_ @LByteString (encodeUtf8 str) prop_TextToBytes :: Property prop_TextToBytes = property $ do- txt <- forAll utf8Text- assert $ txt == (decodeUtf8 (encodeUtf8 txt :: B.ByteString))- && txt == (decodeUtf8 (encodeUtf8 txt :: LB.ByteString))+ txt <- forAll genUtf8Text+ assert $ txt == decodeUtf8 @_ @ByteString (encodeUtf8 txt)+ && txt == decodeUtf8 @_ @LByteString (encodeUtf8 txt) -- "\239\191\190" fails, but this is the same as "\65534" :: String prop_BytesTo :: Property prop_BytesTo = property $ do- utf <- forAll utf8Bytes- assert $ utf == (encodeUtf8 (decodeUtf8 utf :: U.String))- && utf == (encodeUtf8 (decodeUtf8 utf :: T.Text))- && utf == (encodeUtf8 (decodeUtf8 utf :: LT.Text))+ utf <- forAll genUtf8ByteString+ assert $ utf == encodeUtf8 @String (decodeUtf8 utf)+ && utf == encodeUtf8 @Text (decodeUtf8 utf)+ && utf == encodeUtf8 @LText (decodeUtf8 utf) +---------------------------------------------------------------------------- -- ordNub+---------------------------------------------------------------------------- -listProps :: TestTree-listProps = testGroup "list function property tests"- [ testProperty "Hedgehog ordNub xs == nub xs" prop_ordNubCorrect- , testProperty "Hedgehog hashNub xs == nub xs" prop_hashNubCorrect- , testProperty "Hedgehog sortNub xs == sort $ nub xs" prop_sortNubCorrect- , testProperty "Hedgehog sort $ unstableNub xs == sort $ nub xs" prop_unstableNubCorrect+listProps :: Group+listProps = Group "list function property tests"+ [ ("ordNub xs == nub xs:", prop_ordNubCorrect)+ , ("hashNub xs == nub xs:", prop_hashNubCorrect)+ , ("sortNub xs == sort (nub xs):" , prop_sortNubCorrect)+ , ("sort (unstableNub xs) == sort (nub xs):" , prop_unstableNubCorrect) ] -genIntList :: Gen [U.Int]-genIntList = Gen.list (Range.linear 0 10000) Gen.enumBounded- prop_ordNubCorrect :: Property prop_ordNubCorrect = property $ do xs <- forAll genIntList- U.ordNub xs === nub xs+ ordNub xs === nub xs prop_hashNubCorrect :: Property prop_hashNubCorrect = property $ do xs <- forAll genIntList- U.hashNub xs === nub xs+ hashNub xs === ordNub xs prop_sortNubCorrect :: Property prop_sortNubCorrect = property $ do xs <- forAll genIntList- U.sortNub xs === (U.sort $ nub xs)+ sortNub xs === sort (ordNub xs) prop_unstableNubCorrect :: Property prop_unstableNubCorrect = property $ do xs <- forAll genIntList- (U.sort $ U.unstableNub xs) === (U.sort $ nub xs)-+ sort (unstableNub xs) === sortNub xs +---------------------------------------------------------------------------- -- logicM--- this section needs a little more thought--genBoolList :: Gen [U.Bool]-genBoolList = Gen.list (Range.linear 0 1000) Gen.bool+---------------------------------------------------------------------------- -boolMProps :: TestTree-boolMProps = testGroup "lifted logic function property tests"- [ testProperty "Hedgehog andM" prop_andM- , testProperty "Hedgehog orM" prop_orM+logicProps :: Group+logicProps = Group "lifted logic function property tests"+ [ ("andM:", prop_andM)+ , ("orM:", prop_orM) ] prop_andM :: Property prop_andM = property $ do bs <- forAll genBoolList- U.andM (return <$> bs) === ((return $ U.and bs) :: U.Maybe U.Bool)+ andM (pure <$> bs) === pure @Maybe (and bs) prop_orM :: Property prop_orM = property $ do bs <- forAll genBoolList- U.orM (return <$> bs) === ((return $ U.or bs) :: U.Maybe U.Bool)+ orM (pure <$> bs) === pure @Maybe (or bs)