packages feed

relude 0.1.1 → 1.2.2.2

raw patch · 69 files changed

Files

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 -[![Build Status](https://travis-ci.org/kowainik/relude.svg?branch=master)](https://travis-ci.org/kowainik/relude)-[![Hackage](https://img.shields.io/hackage/v/relude.svg)](https://hackage.haskell.org/package/relude)+![Logo](https://user-images.githubusercontent.com/8126674/67678250-9d8eab80-f99f-11e9-96ca-27883ceeefa6.png)++[![GitHub CI](https://github.com/kowainik/relude/workflows/CI/badge.svg)](https://github.com/kowainik/relude/actions)+[![Hackage](https://img.shields.io/hackage/v/relude.svg?logo=haskell)](https://hackage.haskell.org/package/relude) [![Stackage LTS](http://stackage.org/package/relude/badge/lts)](http://stackage.org/lts/package/relude) [![Stackage Nightly](http://stackage.org/package/relude/badge/nightly)](http://stackage.org/nightly/package/relude) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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:++- [![Introduction to `relude` — an alternative Haskell prelude](https://img.youtube.com/vi/qwAmiJ5M_zM/0.jpg)](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)