diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..f85e791d2f --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Ignore blame for commit that moved protolude files under src/protolude +d4949c633e8172d0e4dd8f5c991eaaae6b48fbb0 diff --git a/nix/tools/style.nix b/nix/tools/style.nix index f9c92afb32..563a4330b8 100644 --- a/nix/tools/style.nix +++ b/nix/tools/style.nix @@ -29,7 +29,8 @@ let # Format Haskell files # --vimgrep fixes a bug in ag: https://github.com/ggreer/the_silver_searcher/issues/753 - ${silver-searcher}/bin/ag -l --vimgrep -g '\.l?hs$' . \ + # TODO: fix style issues in src/protolude and include it + ${silver-searcher}/bin/ag -l --vimgrep -g '\.l?hs$' --ignore-dir=src/protolude . \ | xargs ${stylish-haskell}/bin/stylish-haskell -i # Format Python files @@ -89,11 +90,12 @@ let ${ruff}/bin/ruff check . echo "Checking consistency of import aliases in Haskell code..." - ${hsie} check-aliases main src + ${hsie} check-aliases main src/PostgREST echo "Linting Haskell files..." # --vimgrep fixes a bug in ag: https://github.com/ggreer/the_silver_searcher/issues/753 - ${silver-searcher}/bin/ag -l --vimgrep -g '\.l?hs$' . \ + # TODO: fix lint issues in src/protolude and include it + ${silver-searcher}/bin/ag -l --vimgrep -g '\.l?hs$' --ignore-dir=src/protolude . \ | xargs ${hlint}/bin/hlint --hint=${hlintConfig} ''; diff --git a/postgrest.cabal b/postgrest.cabal index 2a819d80bf..19ad691319 100644 --- a/postgrest.cabal +++ b/postgrest.cabal @@ -136,7 +136,7 @@ library , parsec >= 3.1.11 && < 3.2 , postgresql-libpq >= 0.10 , prometheus-client >= 1.1.1 && < 1.2.0 - , protolude >= 0.3.1 && < 0.4 + , protolude , regex-tdfa >= 1.2.2 && < 1.4 , retry >= 0.7.4 && < 0.10 , scientific >= 0.3.4 && < 0.4 @@ -180,6 +180,54 @@ library build-depends: unix +library protolude + visibility: private + default-language: Haskell2010 + default-extensions: NoImplicitPrelude + FlexibleContexts + MultiParamTypeClasses + OverloadedStrings + hs-source-dirs: src/protolude + exposed-modules: Protolude + Protolude.Applicative + Protolude.Base + Protolude.Bifunctor + Protolude.Bool + Protolude.CallStack + Protolude.Conv + Protolude.ConvertText + Protolude.Debug + Protolude.Either + Protolude.Error + Protolude.Exceptions + Protolude.Functor + Protolude.List + Protolude.Monad + Protolude.Panic + Protolude.Partial + Protolude.Safe + Protolude.Semiring + Protolude.Show + Protolude.Unsafe + build-depends: array >= 0.4 && < 0.6 + , async >= 2.0 && < 2.3 + , base >= 4.6 && < 4.22 + , bytestring >= 0.10.8 && < 0.13 + , containers >= 0.5.7 && < 0.8 + , deepseq >= 1.3 && < 1.6 + , ghc-prim >= 0.3 && < 0.14 + , hashable >= 1.2 && < 1.6 + , mtl >= 2.1 && < 2.4 + , mtl-compat >= 0.2 && < 0.3 + , stm >= 2.5 && < 3 + , text >= 1.2.2 && < 2.2 + , transformers >= 0.2 && < 0.7 + , transformers-compat >= 0.4 && < 0.8 + -- Protolude has some partial functions, so + -- it is fine to disable that specific warning + ghc-options: -Werror -Wall -fwarn-identities -Wno-x-partial + -fno-spec-constr -optP-Wno-nonportable-include-path + executable postgrest default-language: Haskell2010 default-extensions: OverloadedStrings @@ -189,7 +237,7 @@ executable postgrest build-depends: base >= 4.9 && < 4.22 , containers >= 0.5.7 && < 0.8 , postgrest - , protolude >= 0.3.1 && < 0.4 + , protolude ghc-options: -threaded -rtsopts "-with-rtsopts=-N -I0 -qg" -O2 -Werror -Wall -fwarn-identities -fno-spec-constr -optP-Wno-nonportable-include-path @@ -285,7 +333,7 @@ test-suite spec , postgrest , process >= 1.4.2 && < 1.7 , prometheus-client >= 1.1.1 && < 1.2.0 - , protolude >= 0.3.1 && < 0.4 + , protolude , regex-tdfa >= 1.2.2 && < 1.4 , scientific >= 0.3.4 && < 0.4 , text >= 1.2.2 && < 2.2 @@ -324,7 +372,7 @@ test-suite observability , jose-jwt >= 0.9.6 && < 0.11 , postgrest , prometheus-client >= 1.1.1 && < 1.2.0 - , protolude >= 0.3.1 && < 0.4 + , protolude , text >= 1.2.2 && < 2.2 , wai >= 3.2.1 && < 3.3 ghc-options: -threaded -O0 -Werror -Wall -fwarn-identities @@ -344,6 +392,6 @@ test-suite doctests , doctest >= 0.8 , postgrest , pretty-simple - , protolude >= 0.3.1 && < 0.4 + , protolude ghc-options: -threaded -O0 -Werror -Wall -fwarn-identities -fno-spec-constr -optP-Wno-nonportable-include-path diff --git a/src/protolude/LICENSE b/src/protolude/LICENSE new file mode 100644 index 0000000000..6e6bfc0e33 --- /dev/null +++ b/src/protolude/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016-2020, Stephen Diehl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/src/protolude/Protolude.hs b/src/protolude/Protolude.hs new file mode 100644 index 0000000000..9126fd6690 --- /dev/null +++ b/src/protolude/Protolude.hs @@ -0,0 +1,1060 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE ExplicitNamespaces #-} +{-# OPTIONS_GHC -fno-warn-unused-imports #-} + +module Protolude ( + -- * Base functions + module Base, + identity, + pass, +#if !MIN_VERSION_base(4,8,0) + (&), + scanl', +#endif + -- * Function functions + module Function, + applyN, + -- * List functions + module List, + map, + uncons, + unsnoc, + -- * Data Structures + module DataStructures, + -- * Show functions + module Show, + show, + print, + -- * Bool functions + module Bool, + -- * Monad functions + module Monad, + liftIO1, + liftIO2, + -- * Functor functions + module Functor, + -- * Either functions + module Either, + -- * Applicative functions + module Applicative, + guarded, + guardedA, + -- * String conversion + module ConvertText, + -- * Debug functions + module Debug, + + -- * Panic functions + module Panic, + -- * Exception functions + module Exception, + Protolude.throwIO, + Protolude.throwTo, + -- * Semiring functions + module Semiring, + + -- * String functions + module String, + -- * Safe functions + module Safe, + -- * Eq functions + module Eq, + -- * Ord functions + module Ord, + -- * Traversable functions + module Traversable, + -- * Foldable functions + module Foldable, + -- * Semigroup functions +#if MIN_VERSION_base(4,9,0) + module Semigroup, +#endif + -- * Monoid functions + module Monoid, + -- * Bifunctor functions + module Bifunctor, + -- * Bifunctor functions + module Hashable, + + -- * Deepseq functions + module DeepSeq, + + -- * Tuple functions + module Tuple, + + module Typeable, + +#if MIN_VERSION_base(4,7,0) + -- * Typelevel programming + module Typelevel, +#endif + + -- * Monads + module Fail, + module State, + module Reader, + module Except, + module Trans, + module ST, + module STM, + + -- * Integers + module Int, + module Bits, + + -- * Complex functions + module Complex, + + -- * Char functions + module Char, + + -- * Maybe functions + module Maybe, + + -- * Generics functions + module Generics, + + -- * ByteString functions + module ByteString, + LByteString, + + -- * Text functions + module Text, + LText, + + -- * Read functions + module Read, + readMaybe, + readEither, + + -- * System functions + module System, + die, + + -- * Concurrency functions + module Concurrency, + + -- * Foreign functions + module Foreign, +) where + +-- Protolude module exports. +import Protolude.Debug as Debug +import Protolude.List as List +import Protolude.Show as Show +import Protolude.Bool as Bool +import Protolude.Monad as Monad +import Protolude.Functor as Functor +import Protolude.Either as Either +import Protolude.Applicative as Applicative +import Protolude.ConvertText as ConvertText +import Protolude.Panic as Panic +import Protolude.Exceptions as Exception +import Protolude.Semiring as Semiring +import qualified Protolude.Conv as Conv + +import Protolude.Base as Base hiding ( + putStr -- Overriden by Show.putStr + , putStrLn -- Overriden by Show.putStrLn + , print -- Overriden by Protolude.print + , show -- Overriden by Protolude.show + , showFloat -- Custom Show instances deprecated. + , showList -- Custom Show instances deprecated. + , showSigned -- Custom Show instances deprecated. + , showSignedFloat -- Custom Show instances deprecated. + , showsPrec -- Custom Show instances deprecated. + ) +import qualified Protolude.Base as PBase + +-- Used for 'show', not exported. +import Data.String (String) +import Data.String as String (IsString) + +-- Maybe'ized version of partial functions +import Protolude.Safe as Safe ( + headMay + , headDef + , initMay + , initDef + , initSafe + , tailMay + , tailDef + , tailSafe + , lastDef + , lastMay + , foldr1May + , foldl1May + , foldl1May' + , maximumMay + , minimumMay + , maximumDef + , minimumDef + , atMay + , atDef + ) + +-- Applicatives +import Control.Applicative as Applicative ( + Applicative(..) + , Alternative(..) + , Const(Const,getConst) + , ZipList(ZipList,getZipList) + , (<**>) + , liftA + , liftA2 + , liftA3 + , optional + ) + +-- Base typeclasses +import Data.Eq as Eq ( + Eq(..) + ) +import Data.Ord as Ord ( + Ord(..) + , Ordering(LT,EQ,GT) + , Down(Down) + , comparing + ) +import Data.Traversable as Traversable +import Data.Foldable as Foldable ( + Foldable, + fold, + foldMap, + foldr, + foldr', + foldl, + foldl', + toList, +#if MIN_VERSION_base(4,8,0) + null, + length, +#endif + elem, + maximum, + minimum, + foldrM, + foldlM, + traverse_, + for_, + mapM_, + forM_, + sequence_, + sequenceA_, + asum, + msum, + concat, + concatMap, + and, + or, + any, + all, + maximumBy, + minimumBy, + notElem, + find, + ) +import Data.Functor.Identity as Functor ( + Identity(Identity, runIdentity) + ) + +#if MIN_VERSION_base(4,9,0) +import Data.List.NonEmpty as List ( + NonEmpty((:|)) + , nonEmpty + ) +import Data.Semigroup as Semigroup ( + Semigroup(sconcat, stimes) + , WrappedMonoid + , diff + , cycle1 + , stimesMonoid + , stimesIdempotent + , stimesIdempotentMonoid + , mtimesDefault + ) +#endif + +#if MIN_VERSION_base(4,9,0) && !MIN_VERSION_base(4,16,0) +import Data.Semigroup as Semigroup ( + Option(..) + , option + ) +#endif + +import Data.Monoid as Monoid + +#if !MIN_VERSION_base(4,8,0) +import Protolude.Bifunctor as Bifunctor (Bifunctor(bimap, first, second)) +#else +import Data.Bifunctor as Bifunctor (Bifunctor(bimap, first, second)) +#endif + +-- Deepseq +import Control.DeepSeq as DeepSeq ( + NFData(..) + , ($!!) + , deepseq + , force + ) + +-- Data structures +import Data.Tuple as Tuple ( + fst + , snd + , curry + , uncurry + , swap + ) + +import Data.List as List ( + splitAt + , break + , intercalate + , isPrefixOf + , isInfixOf + , isSuffixOf + , drop + , filter + , reverse + , replicate + , take + , sortBy + , sort + , intersperse + , transpose + , subsequences + , permutations + , scanl +#if MIN_VERSION_base(4,8,0) + , scanl' +#endif + , scanr + , iterate + , repeat + , cycle + , unfoldr + , takeWhile + , dropWhile + , group + , inits + , tails + , zipWith + , zip + , unzip + , genericLength + , genericTake + , genericDrop + , genericSplitAt + , genericReplicate + ) + +#if !MIN_VERSION_base(4,8,0) +-- These imports are required for the scanl' rewrite rules +import GHC.Exts (build) +import Data.List (tail) +#endif + +-- Hashing +import Data.Hashable as Hashable ( + Hashable + , hash + , hashWithSalt + , hashUsing + ) + +import Data.Map as DataStructures (Map) +import Data.Set as DataStructures (Set) +import Data.Sequence as DataStructures (Seq) +import Data.IntMap as DataStructures (IntMap) +import Data.IntSet as DataStructures (IntSet) + +import Data.Typeable as Typeable ( + TypeRep + , Typeable + , typeOf + , cast + , gcast +#if MIN_VERSION_base(4,7,0) + , typeRep + , eqT +#endif + ) + +#if MIN_VERSION_base(4,7,0) +import Data.Proxy as Typelevel ( + Proxy(..) + ) + +import Data.Type.Coercion as Typelevel ( + Coercion(..) + , coerceWith + , repr + ) + +import Data.Type.Equality as Typelevel ( + (:~:)(..) + , type (==) + , sym + , trans + , castWith + , gcastWith + ) + +#endif + +#if MIN_VERSION_base(4,8,0) +import Data.Void as Typelevel ( + Void + , absurd + , vacuous + ) +#endif + +import Control.Monad.Fail as Fail ( + MonadFail + ) + +-- Monad transformers +import Control.Monad.State as State ( + MonadState + , State + , StateT(StateT) + , put + , get + , gets + , modify + , state + , withState + + , runState + , execState + , evalState + + , runStateT + , execStateT + , evalStateT + ) + +import Control.Monad.Reader as Reader ( + MonadReader + , Reader + , ReaderT(ReaderT) + , ask + , asks + , local + , reader + , runReader + , runReaderT + ) + +import Control.Monad.Trans.Except as Except ( + throwE + , catchE + ) + +import Control.Monad.Except as Except ( + MonadError + , Except + , ExceptT(ExceptT) + , throwError + , catchError + , runExcept + , runExceptT + , mapExcept + , mapExceptT + , withExcept + , withExceptT + ) + +import Control.Monad.Trans as Trans ( + MonadIO + , lift + , liftIO + ) + +-- Base types +import Data.Int as Int ( + Int + , Int8 + , Int16 + , Int32 + , Int64 + ) +import Data.Bits as Bits ( + Bits, + (.&.), + (.|.), + xor, + complement, + shift, + rotate, +#if MIN_VERSION_base(4,7,0) + zeroBits, +#endif + bit, + setBit, + clearBit, + complementBit, + testBit, +#if MIN_VERSION_base(4,7,0) + bitSizeMaybe, +#endif + bitSize, + isSigned, + shiftL, + shiftR, + rotateL, + rotateR, + popCount, +#if MIN_VERSION_base(4,7,0) + FiniteBits, + finiteBitSize, + bitDefault, + testBitDefault, + popCountDefault, +#endif +#if MIN_VERSION_base(4,8,0) + toIntegralSized, + countLeadingZeros, + countTrailingZeros, +#endif + ) +import Data.Word as Bits ( + Word + , Word8 + , Word16 + , Word32 + , Word64 +#if MIN_VERSION_base(4,7,0) + , byteSwap16 + , byteSwap32 + , byteSwap64 +#endif + ) + +import Data.Either as Either ( + Either(Left,Right) + , either + , lefts + , rights + , partitionEithers +#if MIN_VERSION_base(4,7,0) + , isLeft + , isRight +#endif + ) + +import Data.Complex as Complex ( + Complex((:+)) + , realPart + , imagPart + , mkPolar + , cis + , polar + , magnitude + , phase + , conjugate + ) +import Data.Char as Char ( + Char + , ord + , chr + , digitToInt + , intToDigit + , toUpper + , toLower + , toTitle + , isAscii + , isLetter + , isDigit + , isHexDigit + , isPrint + , isAlpha + , isAlphaNum + , isUpper + , isLower + , isSpace + , isControl + ) +import Data.Bool as Bool ( + Bool(True, False), + (&&), + (||), + not, + otherwise + ) +import Data.Maybe as Maybe ( + Maybe(Nothing, Just) + , maybe + , isJust + , isNothing + , fromMaybe + , listToMaybe + , maybeToList + , catMaybes + , mapMaybe + ) + +import Data.Function as Function ( + const + , (.) + , ($) + , flip + , fix + , on +#if MIN_VERSION_base(4,8,0) + , (&) +#endif + ) + +-- Genericss +import GHC.Generics as Generics ( + Generic(..) + , Generic1 + , Rep + , K1(..) + , M1(..) + , U1(..) + , V1 + , D1 + , C1 + , S1 + , (:+:)(..) + , (:*:)(..) + , (:.:)(..) + , Rec0 + , Constructor(..) + , Datatype(..) + , Selector(..) + , Fixity(..) + , Associativity(..) +#if ( __GLASGOW_HASKELL__ >= 800 ) + , Meta(..) + , FixityI(..) + , URec +#endif + ) + +-- ByteString +import qualified Data.ByteString.Lazy +import Data.ByteString as ByteString (ByteString) + +-- Text +import Data.Text as Text ( + Text + , lines + , words + , unlines + , unwords + ) +import qualified Data.Text.Lazy + +import Data.Text.IO as Text ( + getLine + , getContents + , interact + , readFile + , writeFile + , appendFile + ) + +import Data.Text.Lazy as Text ( + toStrict + , fromStrict + ) + +import Data.Text.Encoding as Text ( + encodeUtf8 + , decodeUtf8 + , decodeUtf8' + , decodeUtf8With + ) + +import Data.Text.Encoding.Error as Text ( + OnDecodeError + , OnError + , UnicodeException + , lenientDecode + , strictDecode + , ignore + , replace + ) + +-- IO +import System.Environment as System (getArgs) +import qualified System.Exit +import System.Exit as System ( + ExitCode(..) + , exitWith + , exitFailure + , exitSuccess + ) +import System.IO as System ( + Handle + , FilePath + , IOMode(..) + , stdin + , stdout + , stderr + , withFile + , openFile + ) + +-- ST +import Control.Monad.ST as ST ( + ST + , runST + , fixST + ) + +-- Concurrency and Parallelism +import Control.Exception as Exception ( + Exception, + toException, + fromException, +#if MIN_VERSION_base(4,8,0) + displayException, +#endif + SomeException(SomeException) + , IOException + , ArithException( + Overflow, + Underflow, + LossOfPrecision, + DivideByZero, + Denormal, + RatioZeroDenominator + ) + , ArrayException(IndexOutOfBounds, UndefinedElement) + , AssertionFailed(AssertionFailed) +#if MIN_VERSION_base(4,7,0) + , SomeAsyncException(SomeAsyncException) + , asyncExceptionToException + , asyncExceptionFromException +#endif + , AsyncException(StackOverflow, HeapOverflow, ThreadKilled, UserInterrupt) + , NonTermination(NonTermination) + , NestedAtomically(NestedAtomically) + , BlockedIndefinitelyOnMVar(BlockedIndefinitelyOnMVar) + , BlockedIndefinitelyOnSTM(BlockedIndefinitelyOnSTM) +#if MIN_VERSION_base(4,8,0) + , AllocationLimitExceeded(AllocationLimitExceeded) +#endif +#if MIN_VERSION_base(4,10,0) + , CompactionFailed(CompactionFailed) +#endif + , Deadlock(Deadlock) + , NoMethodError(NoMethodError) + , PatternMatchFail(PatternMatchFail) + , RecConError(RecConError) + , RecSelError(RecSelError) + , RecUpdError(RecUpdError) +#if MIN_VERSION_base(4,9,0) + , ErrorCall(ErrorCall, ErrorCallWithLocation) +#else + , ErrorCall(ErrorCall) +#endif +#if MIN_VERSION_base(4,9,0) + , TypeError(TypeError) +#endif + , ioError + , catch + , catches + , Handler(Handler) + , catchJust + , handle + , handleJust + , try + , tryJust + , evaluate + , mapException + , mask + , mask_ + , uninterruptibleMask + , uninterruptibleMask_ + , MaskingState(..) + , getMaskingState +#if MIN_VERSION_base(4,9,0) + , interruptible +#endif + , allowInterrupt + , bracket + , bracket_ + , bracketOnError + , finally + , onException + ) +import qualified Control.Exception as PException + +import Control.Monad.STM as STM ( + STM + , atomically +#if !(MIN_VERSION_stm(2,5,0)) + , always + , alwaysSucceeds +#endif + , retry + , orElse + , check + , throwSTM + , catchSTM + ) + +import Control.Concurrent.MVar as Concurrency ( + MVar + , newEmptyMVar + , newMVar + , takeMVar + , putMVar + , readMVar + , swapMVar + , tryTakeMVar + , tryPutMVar + , isEmptyMVar + , withMVar +#if MIN_VERSION_base(4,7,0) + , withMVarMasked +#endif + , modifyMVar_ + , modifyMVar + , modifyMVarMasked_ + , modifyMVarMasked +#if MIN_VERSION_base(4,7,0) + , tryReadMVar + , mkWeakMVar +#endif + , addMVarFinalizer + ) +import Control.Concurrent.Chan as Concurrency ( + Chan + , newChan + , writeChan + , readChan + , dupChan + , getChanContents + , writeList2Chan + ) +import Control.Concurrent.QSem as Concurrency ( + QSem + , newQSem + , waitQSem + , signalQSem + ) +import Control.Concurrent.QSemN as Concurrency ( + QSemN + , newQSemN + , waitQSemN + , signalQSemN + ) +import Control.Concurrent as Concurrency ( + ThreadId + , forkIO + , forkFinally + , forkIOWithUnmask + , killThread + , forkOn + , forkOnWithUnmask + , getNumCapabilities + , setNumCapabilities + , threadCapability + , yield + , threadDelay + , threadWaitRead + , threadWaitWrite +#if MIN_VERSION_base(4,7,0) + , threadWaitReadSTM + , threadWaitWriteSTM +#endif + , rtsSupportsBoundThreads + , forkOS +#if MIN_VERSION_base(4,9,0) + , forkOSWithUnmask +#endif + , isCurrentThreadBound + , runInBoundThread + , runInUnboundThread + , mkWeakThreadId + , myThreadId + ) +import Control.Concurrent.Async as Concurrency ( + Async(..) + , Concurrently(..) + , async + , asyncBound + , asyncOn + , withAsync + , withAsyncBound + , withAsyncOn + , wait + , poll + , waitCatch + , cancel + , cancelWith + , asyncThreadId + , waitAny + , waitAnyCatch + , waitAnyCancel + , waitAnyCatchCancel + , waitEither + , waitEitherCatch + , waitEitherCancel + , waitEitherCatchCancel + , waitEither_ + , waitBoth + , link + , link2 + , race + , race_ + , concurrently + ) + +import Foreign.Ptr as Foreign (IntPtr, WordPtr) +import Foreign.Storable as Foreign (Storable) +import Foreign.StablePtr as Foreign (StablePtr) + +-- Read instances hiding unsafe builtins (read) +import qualified Text.Read as Read +import Text.Read as Read ( + Read + , reads + ) + +-- Type synonymss for lazy texts +type LText = Data.Text.Lazy.Text +type LByteString = Data.ByteString.Lazy.ByteString + + +#if !MIN_VERSION_base(4,8,0) +infixl 1 & + +(&) :: a -> (a -> b) -> b +x & f = f x +#endif + +-- | The identity function, returns the give value unchanged. +identity :: a -> a +identity x = x + +map :: Functor.Functor f => (a -> b) -> f a -> f b +map = Functor.fmap + +uncons :: [a] -> Maybe (a, [a]) +uncons [] = Nothing +uncons (x:xs) = Just (x, xs) + +unsnoc :: [x] -> Maybe ([x],x) +unsnoc = Foldable.foldr go Nothing + where + go x mxs = Just (case mxs of + Nothing -> ([], x) + Just (xs, e) -> (x:xs, e)) + +-- | Apply a function n times to a given value +applyN :: Int -> (a -> a) -> a -> a +applyN n f = Foldable.foldr (.) identity (List.replicate n f) + +-- | Parse a string using the 'Read' instance. +-- Succeeds if there is exactly one valid result. +-- +-- >>> readMaybe ("123" :: Text) :: Maybe Int +-- Just 123 +-- +-- >>> readMaybe ("hello" :: Text) :: Maybe Int +-- Nothing +readMaybe :: (Read b, Conv.StringConv a String) => a -> Maybe b +readMaybe = Read.readMaybe . Conv.toS + +-- | Parse a string using the 'Read' instance. +-- Succeeds if there is exactly one valid result. +-- A 'Left' value indicates a parse error. +-- +-- >>> readEither "123" :: Either Text Int +-- Right 123 +-- +-- >>> readEither "hello" :: Either Text Int +-- Left "Prelude.read: no parse" +readEither :: (Read a, Conv.StringConv String e, Conv.StringConv e String) => e -> Either e a +readEither = first Conv.toS . Read.readEither . Conv.toS + +-- | The print function outputs a value of any printable type to the standard +-- output device. Printable types are those that are instances of class Show; +-- print converts values to strings for output using the show operation and adds +-- a newline. +print :: (Trans.MonadIO m, PBase.Show a) => a -> m () +print = liftIO . PBase.print + +-- | Lifted throwIO +throwIO :: (Trans.MonadIO m, Exception e) => e -> m a +throwIO = liftIO . PException.throwIO + +-- | Lifted throwTo +throwTo :: (Trans.MonadIO m, Exception e) => ThreadId -> e -> m () +throwTo tid e = liftIO (PException.throwTo tid e) + +-- | Do nothing returning unit inside applicative. +pass :: Applicative f => f () +pass = pure () + +guarded :: (Alternative f) => (a -> Bool) -> a -> f a +guarded p x = Bool.bool empty (pure x) (p x) + +guardedA :: (Functor.Functor f, Alternative t) => (a -> f Bool) -> a -> f (t a) +guardedA p x = Bool.bool empty (pure x) `Functor.fmap` p x + +-- | Lift an 'IO' operation with 1 argument into another monad +liftIO1 :: MonadIO m => (a -> IO b) -> a -> m b +liftIO1 = (.) liftIO + +-- | Lift an 'IO' operation with 2 arguments into another monad +liftIO2 :: MonadIO m => (a -> b -> IO c) -> a -> b -> m c +liftIO2 = ((.).(.)) liftIO + +show :: (Show a, Conv.StringConv String b) => a -> b +show x = Conv.toS (PBase.show x) +{-# SPECIALIZE show :: Show a => a -> Text #-} +{-# SPECIALIZE show :: Show a => a -> LText #-} +{-# SPECIALIZE show :: Show a => a -> String #-} + +#if MIN_VERSION_base(4,8,0) +-- | Terminate main process with failure +die :: Text -> IO a +die err = System.Exit.die (ConvertText.toS err) +#else +-- | Terminate main process with failure +die :: Text -> IO a +die err = hPutStrLn stderr err >> exitFailure +#endif + +#if !MIN_VERSION_base(4,8,0) +-- This is a literal copy of the implementation in GHC.List in base-4.10.1.0. + +-- | A strictly accumulating version of 'scanl' +{-# NOINLINE [1] scanl' #-} +scanl' :: (b -> a -> b) -> b -> [a] -> [b] +scanl' = scanlGo' + where + scanlGo' :: (b -> a -> b) -> b -> [a] -> [b] + scanlGo' f !q ls = q : (case ls of + [] -> [] + x:xs -> scanlGo' f (f q x) xs) + +{-# RULES +"scanl'" [~1] forall f a bs . scanl' f a bs = + build (\c n -> a `c` foldr (scanlFB' f c) (flipSeqScanl' n) bs a) +"scanlList'" [1] forall f a bs . + foldr (scanlFB' f (:)) (flipSeqScanl' []) bs a = tail (scanl' f a bs) + #-} + +{-# INLINE [0] scanlFB' #-} +scanlFB' :: (b -> a -> b) -> (b -> c -> c) -> a -> (b -> c) -> b -> c +scanlFB' f c = \b g -> \x -> let !b' = f x b in b' `c` g b' + +{-# INLINE [0] flipSeqScanl' #-} +flipSeqScanl' :: a -> b -> a +flipSeqScanl' a !_b = a +#endif diff --git a/src/protolude/Protolude/Applicative.hs b/src/protolude/Protolude/Applicative.hs new file mode 100644 index 0000000000..0e3c25b35a --- /dev/null +++ b/src/protolude/Protolude/Applicative.hs @@ -0,0 +1,38 @@ +{-# LANGUAGE Safe #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Applicative + ( orAlt, + orEmpty, + eitherA, + purer, + liftAA2, + (<<*>>), + ) +where + +import Control.Applicative +import Data.Bool (Bool) +import Data.Either (Either (Left, Right)) +import Data.Function ((.)) +import Data.Monoid (Monoid (mempty)) + +orAlt :: (Alternative f, Monoid a) => f a -> f a +orAlt f = f <|> pure mempty + +orEmpty :: Alternative f => Bool -> a -> f a +orEmpty b a = if b then pure a else empty + +eitherA :: (Alternative f) => f a -> f b -> f (Either a b) +eitherA a b = (Left <$> a) <|> (Right <$> b) + +purer :: (Applicative f, Applicative g) => a -> f (g a) +purer = pure . pure + +liftAA2 :: (Applicative f, Applicative g) => (a -> b -> c) -> f (g a) -> f (g b) -> f (g c) +liftAA2 = liftA2 . liftA2 + +infixl 4 <<*>> + +(<<*>>) :: (Applicative f, Applicative g) => f (g (a -> b)) -> f (g a) -> f (g b) +(<<*>>) = liftA2 (<*>) diff --git a/src/protolude/Protolude/Base.hs b/src/protolude/Protolude/Base.hs new file mode 100644 index 0000000000..0682d1eb83 --- /dev/null +++ b/src/protolude/Protolude/Base.hs @@ -0,0 +1,225 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE Unsafe #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE ExplicitNamespaces #-} + +module Protolude.Base ( + module Base, + ($!), +) where + +-- Glorious Glasgow Haskell Compiler +#if defined(__GLASGOW_HASKELL__) && ( __GLASGOW_HASKELL__ >= 600 ) + +-- Base GHC types +import GHC.Num as Base ( + Num( + (+), + (-), + (*), + negate, + abs, + signum, + fromInteger + ) + , Integer + , subtract + ) +import GHC.Enum as Base ( + Bounded(minBound, maxBound) + , Enum( + succ, + pred, + toEnum, + fromEnum, + enumFrom, + enumFromThen, + enumFromTo, + enumFromThenTo + ) + , boundedEnumFrom + , boundedEnumFromThen + ) +import GHC.Real as Base ( + (%) + , (/) + , Fractional + , Integral + , Ratio + , Rational + , Real + , RealFrac + , (^) + , (^%^) + , (^^) + , (^^%^^) + , ceiling + , denominator + , div + , divMod +#if MIN_VERSION_base(4,7,0) + , divZeroError +#endif + , even + , floor + , fromIntegral + , fromRational + , gcd +#if MIN_VERSION_base(4,9,0) && !MIN_VERSION_base(4,15,0) +#if defined(MIN_VERSION_integer_gmp) + , gcdInt' + , gcdWord' +#endif +#endif + , infinity + , integralEnumFrom + , integralEnumFromThen + , integralEnumFromThenTo + , integralEnumFromTo + , lcm + , mod + , notANumber + , numerator + , numericEnumFrom + , numericEnumFromThen + , numericEnumFromThenTo + , numericEnumFromTo + , odd +#if MIN_VERSION_base(4,7,0) + , overflowError +#endif + , properFraction + , quot + , quotRem + , ratioPrec + , ratioPrec1 +#if MIN_VERSION_base(4,7,0) + , ratioZeroDenominatorError +#endif + , realToFrac + , recip + , reduce + , rem + , round + , showSigned + , toInteger + , toRational + , truncate +#if MIN_VERSION_base(4,12,0) + , underflowError +#endif + ) +import GHC.Float as Base ( + Float(F#) + , Double(D#) + , Floating (..) + , RealFloat(..) + , showFloat + , showSignedFloat + ) +import GHC.Show as Base ( + Show(showsPrec, show, showList) + ) +import GHC.Exts as Base ( + Constraint + , Ptr + , FunPtr + ) +import GHC.Base as Base ( + (++) + , seq + , asTypeOf + , ord + , maxInt + , minInt + , until + ) + +-- Exported for lifting into new functions. +import System.IO as Base ( + print + , putStr + , putStrLn + ) + +import GHC.Types as Base ( + Bool + , Char + , Int + , Word + , Ordering + , IO +#if ( __GLASGOW_HASKELL__ >= 710 ) + , Coercible +#endif + ) + +#if ( __GLASGOW_HASKELL__ >= 710 ) +import GHC.StaticPtr as Base (StaticPtr) +#endif + +#if ( __GLASGOW_HASKELL__ >= 800 ) +import GHC.OverloadedLabels as Base ( + IsLabel(fromLabel) + ) + +import GHC.ExecutionStack as Base ( + Location(Location, srcLoc, objectName, functionName) + , SrcLoc(SrcLoc, sourceColumn, sourceLine, sourceColumn) + , getStackTrace + , showStackTrace + ) + +import GHC.Stack as Base ( + CallStack + , type HasCallStack + , callStack + , prettySrcLoc + , currentCallStack + , getCallStack + , prettyCallStack + , withFrozenCallStack + ) +#endif + +#if ( __GLASGOW_HASKELL__ >= 710 ) +import GHC.TypeLits as Base ( + Symbol + , SomeSymbol(SomeSymbol) + , Nat + , SomeNat(SomeNat) + , CmpNat + , KnownSymbol + , KnownNat + , natVal + , someNatVal + , symbolVal + , someSymbolVal + ) +#endif + +#if ( __GLASGOW_HASKELL__ >= 802 ) +import GHC.Records as Base ( + HasField(getField) + ) +#endif + +#if ( __GLASGOW_HASKELL__ >= 800 ) +import Data.Kind as Base ( + type Type +#if ( __GLASGOW_HASKELL__ < 805 ) + , type (*) +#endif + , type Type + ) +#endif + +-- Default Prelude defines this at the toplevel module, so we do as well. +infixr 0 $! + +($!) :: (a -> b) -> a -> b +f $! x = let !vx = x in f vx + +#endif diff --git a/src/protolude/Protolude/Bifunctor.hs b/src/protolude/Protolude/Bifunctor.hs new file mode 100644 index 0000000000..6dd9ad4ab2 --- /dev/null +++ b/src/protolude/Protolude/Bifunctor.hs @@ -0,0 +1,51 @@ +{-# LANGUAGE Safe #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Bifunctor + ( Bifunctor, + bimap, + first, + second, + ) +where + +import Control.Applicative (Const (Const)) +import Data.Either (Either (Left, Right)) +import Data.Function ((.), id) + +class Bifunctor p where + {-# MINIMAL bimap | first, second #-} + + bimap :: (a -> b) -> (c -> d) -> p a c -> p b d + bimap f g = first f . second g + + first :: (a -> b) -> p a c -> p b c + first f = bimap f id + + second :: (b -> c) -> p a b -> p a c + second = bimap id + +instance Bifunctor (,) where + bimap f g ~(a, b) = (f a, g b) + +instance Bifunctor ((,,) x1) where + bimap f g ~(x1, a, b) = (x1, f a, g b) + +instance Bifunctor ((,,,) x1 x2) where + bimap f g ~(x1, x2, a, b) = (x1, x2, f a, g b) + +instance Bifunctor ((,,,,) x1 x2 x3) where + bimap f g ~(x1, x2, x3, a, b) = (x1, x2, x3, f a, g b) + +instance Bifunctor ((,,,,,) x1 x2 x3 x4) where + bimap f g ~(x1, x2, x3, x4, a, b) = (x1, x2, x3, x4, f a, g b) + +instance Bifunctor ((,,,,,,) x1 x2 x3 x4 x5) where + bimap f g ~(x1, x2, x3, x4, x5, a, b) = (x1, x2, x3, x4, x5, f a, g b) + +instance Bifunctor Either where + bimap f _ (Left a) = Left (f a) + bimap _ g (Right b) = Right (g b) + +instance Bifunctor Const where + bimap f _ (Const a) = Const (f a) diff --git a/src/protolude/Protolude/Bool.hs b/src/protolude/Protolude/Bool.hs new file mode 100644 index 0000000000..ed987c809a --- /dev/null +++ b/src/protolude/Protolude/Bool.hs @@ -0,0 +1,64 @@ +{-# LANGUAGE Safe #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Bool ( + whenM +, unlessM +, ifM +, guardM +, bool +, (&&^) +, (||^) +, (<&&>) +, (<||>) +) where + +import Data.Bool (Bool(True, False), (&&), (||)) +import Data.Function (flip) +import Control.Applicative(Applicative, liftA2) +import Control.Monad (Monad, MonadPlus, return, when, unless, guard, (>>=), (=<<)) + +bool :: a -> a -> Bool -> a +bool f t p = if p then t else f + +whenM :: Monad m => m Bool -> m () -> m () +whenM p m = + p >>= flip when m + +unlessM :: Monad m => m Bool -> m () -> m () +unlessM p m = + p >>= flip unless m + +ifM :: Monad m => m Bool -> m a -> m a -> m a +ifM p x y = p >>= \b -> if b then x else y + +guardM :: MonadPlus m => m Bool -> m () +guardM f = guard =<< f + +-- | The '||' operator lifted to a monad. If the first +-- argument evaluates to 'True' the second argument will not +-- be evaluated. +infixr 2 ||^ -- same as (||) +(||^) :: Monad m => m Bool -> m Bool -> m Bool +(||^) a b = ifM a (return True) b + +infixr 2 <||> +-- | '||' lifted to an Applicative. +-- Unlike '||^' the operator is __not__ short-circuiting. +(<||>) :: Applicative a => a Bool -> a Bool -> a Bool +(<||>) = liftA2 (||) +{-# INLINE (<||>) #-} + +-- | The '&&' operator lifted to a monad. If the first +-- argument evaluates to 'False' the second argument will not +-- be evaluated. +infixr 3 &&^ -- same as (&&) +(&&^) :: Monad m => m Bool -> m Bool -> m Bool +(&&^) a b = ifM a b (return False) + +infixr 3 <&&> +-- | '&&' lifted to an Applicative. +-- Unlike '&&^' the operator is __not__ short-circuiting. +(<&&>) :: Applicative a => a Bool -> a Bool -> a Bool +(<&&>) = liftA2 (&&) +{-# INLINE (<&&>) #-} diff --git a/src/protolude/Protolude/CallStack.hs b/src/protolude/Protolude/CallStack.hs new file mode 100644 index 0000000000..ce587f6694 --- /dev/null +++ b/src/protolude/Protolude/CallStack.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ImplicitParams #-} +{-# LANGUAGE ConstraintKinds #-} + +module Protolude.CallStack +( HasCallStack +) where + +#if MIN_VERSION_base(4,9,0) +import GHC.Stack (HasCallStack) +#elif MIN_VERSION_base(4,8,1) +import qualified GHC.Stack +type HasCallStack = (?callStack :: GHC.Stack.CallStack) +#else +import GHC.Exts (Constraint) +type HasCallStack = (() :: Constraint) +#endif diff --git a/src/protolude/Protolude/Conv.hs b/src/protolude/Protolude/Conv.hs new file mode 100644 index 0000000000..47a1167dcb --- /dev/null +++ b/src/protolude/Protolude/Conv.hs @@ -0,0 +1,78 @@ +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE TypeSynonymInstances #-} + +-- | An alternative to 'Protolude.ConvertText' that includes +-- partial conversions. Not re-exported by 'Protolude'. +module Protolude.Conv ( + StringConv +, strConv +, toS +, toSL +, Leniency (Lenient, Strict) +) where + +import Data.ByteString.Char8 as B +import Data.ByteString.Lazy.Char8 as LB +import Data.Text as T +import Data.Text.Encoding as T +import Data.Text.Encoding.Error as T +import Data.Text.Lazy as LT +import Data.Text.Lazy.Encoding as LT + +import Protolude.Base +import Data.Eq (Eq) +import Data.Ord (Ord) +import Data.Function ((.), id) +import Data.String (String) +import Control.Applicative (pure) + +data Leniency = Lenient | Strict + deriving (Eq,Show,Ord,Enum,Bounded) + +class StringConv a b where + strConv :: Leniency -> a -> b + +toS :: StringConv a b => a -> b +toS = strConv Strict + +toSL :: StringConv a b => a -> b +toSL = strConv Lenient + +instance StringConv String String where strConv _ = id +instance StringConv String B.ByteString where strConv _ = B.pack +instance StringConv String LB.ByteString where strConv _ = LB.pack +instance StringConv String T.Text where strConv _ = T.pack +instance StringConv String LT.Text where strConv _ = LT.pack + +instance StringConv B.ByteString String where strConv _ = B.unpack +instance StringConv B.ByteString B.ByteString where strConv _ = id +instance StringConv B.ByteString LB.ByteString where strConv _ = LB.fromChunks . pure +instance StringConv B.ByteString T.Text where strConv = decodeUtf8T +instance StringConv B.ByteString LT.Text where strConv l = strConv l . LB.fromChunks . pure + +instance StringConv LB.ByteString String where strConv _ = LB.unpack +instance StringConv LB.ByteString B.ByteString where strConv _ = B.concat . LB.toChunks +instance StringConv LB.ByteString LB.ByteString where strConv _ = id +instance StringConv LB.ByteString T.Text where strConv l = decodeUtf8T l . strConv l +instance StringConv LB.ByteString LT.Text where strConv = decodeUtf8LT + +instance StringConv T.Text String where strConv _ = T.unpack +instance StringConv T.Text B.ByteString where strConv _ = T.encodeUtf8 +instance StringConv T.Text LB.ByteString where strConv l = strConv l . T.encodeUtf8 +instance StringConv T.Text LT.Text where strConv _ = LT.fromStrict +instance StringConv T.Text T.Text where strConv _ = id + +instance StringConv LT.Text String where strConv _ = LT.unpack +instance StringConv LT.Text T.Text where strConv _ = LT.toStrict +instance StringConv LT.Text LT.Text where strConv _ = id +instance StringConv LT.Text LB.ByteString where strConv _ = LT.encodeUtf8 +instance StringConv LT.Text B.ByteString where strConv l = strConv l . LT.encodeUtf8 + +decodeUtf8T :: Leniency -> B.ByteString -> T.Text +decodeUtf8T Lenient = T.decodeUtf8With T.lenientDecode +decodeUtf8T Strict = T.decodeUtf8With T.strictDecode + +decodeUtf8LT :: Leniency -> LB.ByteString -> LT.Text +decodeUtf8LT Lenient = LT.decodeUtf8With T.lenientDecode +decodeUtf8LT Strict = LT.decodeUtf8With T.strictDecode diff --git a/src/protolude/Protolude/ConvertText.hs b/src/protolude/Protolude/ConvertText.hs new file mode 100644 index 0000000000..df3fd26dba --- /dev/null +++ b/src/protolude/Protolude/ConvertText.hs @@ -0,0 +1,50 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE Safe #-} + +-- | Non-partial text conversion typeclass and functions. +-- For an alternative with partial conversions import 'Protolude.Conv'. +module Protolude.ConvertText ( + ConvertText (toS) +, toUtf8 +, toUtf8Lazy +) where + +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 Data.Function (id, (.)) +import Data.String (String) +import Data.Text.Encoding (encodeUtf8) + +-- | Convert from one Unicode textual type to another. Not for serialization/deserialization, +-- so doesn't have instances for bytestrings. +class ConvertText a b where + toS :: a -> b + +instance ConvertText String String where toS = id +instance ConvertText String T.Text where toS = T.pack +instance ConvertText String LT.Text where toS = LT.pack + +instance ConvertText T.Text String where toS = T.unpack +instance ConvertText T.Text LT.Text where toS = LT.fromStrict +instance ConvertText T.Text T.Text where toS = id + +instance ConvertText LT.Text String where toS = LT.unpack +instance ConvertText LT.Text T.Text where toS = LT.toStrict +instance ConvertText LT.Text LT.Text where toS = id + +instance ConvertText LB.ByteString B.ByteString where toS = LB.toStrict +instance ConvertText LB.ByteString LB.ByteString where toS = id + +instance ConvertText B.ByteString B.ByteString where toS = id +instance ConvertText B.ByteString LB.ByteString where toS = LB.fromStrict + +toUtf8 :: ConvertText a T.Text => a -> B.ByteString +toUtf8 = + encodeUtf8 . toS + +toUtf8Lazy :: ConvertText a T.Text => a -> LB.ByteString +toUtf8Lazy = + LB.fromStrict . encodeUtf8 . toS diff --git a/src/protolude/Protolude/Debug.hs b/src/protolude/Protolude/Debug.hs new file mode 100644 index 0000000000..056fabe09a --- /dev/null +++ b/src/protolude/Protolude/Debug.hs @@ -0,0 +1,69 @@ +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-} + +module Protolude.Debug ( + undefined, + trace, + traceM, + traceId, + traceIO, + traceShow, + traceShowId, + traceShowM, + notImplemented, + witness, +) where + +import Data.Text (Text, unpack) +import Control.Monad (Monad, return) + +import qualified Protolude.Base as P +import Protolude.Error (error) +import Protolude.Show (Print, hPutStrLn) + +import System.IO(stderr) +import System.IO.Unsafe (unsafePerformIO) + +{-# WARNING trace "'trace' remains in code" #-} +trace :: Print b => b -> a -> a +trace string expr = unsafePerformIO (do + hPutStrLn stderr string + return expr) + +{-# WARNING traceIO "'traceIO' remains in code" #-} +traceIO :: Print b => b -> a -> P.IO a +traceIO string expr = do + hPutStrLn stderr string + return expr + +{-# WARNING traceShow "'traceShow' remains in code" #-} +traceShow :: P.Show a => a -> b -> b +traceShow a b = trace (P.show a) b + +{-# WARNING traceShowId "'traceShowId' remains in code" #-} +traceShowId :: P.Show a => a -> a +traceShowId a = trace (P.show a) a + +{-# WARNING traceShowM "'traceShowM' remains in code" #-} +traceShowM :: (P.Show a, Monad m) => a -> m () +traceShowM a = trace (P.show a) (return ()) + +{-# WARNING traceM "'traceM' remains in code" #-} +traceM :: (Monad m) => Text -> m () +traceM s = trace (unpack s) (return ()) + +{-# WARNING traceId "'traceId' remains in code" #-} +traceId :: Text -> Text +traceId s = trace s s + +{-# WARNING notImplemented "'notImplemented' remains in code" #-} +notImplemented :: a +notImplemented = error "Not implemented" + +{-# WARNING undefined "'undefined' remains in code" #-} +undefined :: a +undefined = error "Prelude.undefined" + +witness :: a +witness = error "Type witness should not be evaluated" diff --git a/src/protolude/Protolude/Either.hs b/src/protolude/Protolude/Either.hs new file mode 100644 index 0000000000..7cf955a403 --- /dev/null +++ b/src/protolude/Protolude/Either.hs @@ -0,0 +1,51 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE Safe #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Either ( + maybeToLeft +, maybeToRight +, leftToMaybe +, rightToMaybe +, maybeEmpty +, maybeToEither +, fromLeft +, fromRight +) where + +import Data.Function (const) +import Data.Monoid (Monoid, mempty) +import Data.Maybe (Maybe(Nothing, Just), maybe) +import Data.Either (Either(Left, Right), either) +#if MIN_VERSION_base(4,10,0) +import Data.Either (fromLeft, fromRight) +#else +-- | Return the contents of a 'Right'-value or a default value otherwise. +fromLeft :: a -> Either a b -> a +fromLeft _ (Left a) = a +fromLeft a _ = a + +-- | Return the contents of a 'Right'-value or a default value otherwise. +fromRight :: b -> Either a b -> b +fromRight _ (Right b) = b +fromRight b _ = b +#endif + +leftToMaybe :: Either l r -> Maybe l +leftToMaybe = either Just (const Nothing) + +rightToMaybe :: Either l r -> Maybe r +rightToMaybe = either (const Nothing) Just + +maybeToRight :: l -> Maybe r -> Either l r +maybeToRight l = maybe (Left l) Right + +maybeToLeft :: r -> Maybe l -> Either l r +maybeToLeft r = maybe (Right r) Left + +maybeEmpty :: Monoid b => (a -> b) -> Maybe a -> b +maybeEmpty = maybe mempty + +maybeToEither :: e -> Maybe a -> Either e a +maybeToEither e Nothing = Left e +maybeToEither _ (Just a) = Right a diff --git a/src/protolude/Protolude/Error.hs b/src/protolude/Protolude/Error.hs new file mode 100644 index 0000000000..3d9ada7eb6 --- /dev/null +++ b/src/protolude/Protolude/Error.hs @@ -0,0 +1,52 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE ImplicitParams #-} +{-# LANGUAGE ExistentialQuantification #-} +#if ( __GLASGOW_HASKELL__ >= 800 ) +{-# LANGUAGE DataKinds #-} +#endif + +#if MIN_VERSION_base(4,9,0) +{-# OPTIONS_GHC -fno-warn-redundant-constraints #-} +#endif + +module Protolude.Error +( error +) where + +import Data.Text (Text, unpack) + +#if MIN_VERSION_base(4,9,0) +-- Full stack trace. + +import GHC.Prim (TYPE, raise#) +import GHC.Types (RuntimeRep) +import Protolude.CallStack (HasCallStack) +import GHC.Exception (errorCallWithCallStackException) + +{-# WARNING error "'error' remains in code" #-} +error :: forall (r :: RuntimeRep) . forall (a :: TYPE r) . HasCallStack => Text -> a +error s = raise# (errorCallWithCallStackException (unpack s) ?callStack) + +#elif MIN_VERSION_base(4,7,0) +-- Basic Call Stack with callsite. + +import GHC.Prim (raise#) +import GHC.Exception (errorCallException) + +{-# WARNING error "'error' remains in code" #-} +error :: Text -> a +error s = raise# (errorCallException (unpack s)) + +#else + +-- No exception tracing. +import GHC.Types +import GHC.Exception + +{-# WARNING error "'error' remains in code" #-} +error :: Text -> a +error s = throw (ErrorCall (unpack s)) + +#endif diff --git a/src/protolude/Protolude/Exceptions.hs b/src/protolude/Protolude/Exceptions.hs new file mode 100644 index 0000000000..76d090faf9 --- /dev/null +++ b/src/protolude/Protolude/Exceptions.hs @@ -0,0 +1,35 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Exceptions ( + hush, + note, + tryIO, +) where + +import Protolude.Base (IO) +import Data.Function ((.)) +import Control.Monad.Trans (liftIO) +import Control.Monad.IO.Class (MonadIO) +import Control.Monad.Except (ExceptT(ExceptT), MonadError, throwError) +import Control.Exception as Exception +import Control.Applicative +import Data.Maybe (Maybe, maybe) +import Data.Either (Either(Left,Right)) + +hush :: Alternative m => Either e a -> m a +hush (Left _) = empty +hush (Right x) = pure x + +-- To suppress redundant applicative constraint warning on GHC 8.0 +#if ( __GLASGOW_HASKELL__ >= 800 ) +note :: (MonadError e m) => e -> Maybe a -> m a +note err = maybe (throwError err) pure +#else +note :: (MonadError e m, Applicative m) => e -> Maybe a -> m a +note err = maybe (throwError err) pure +#endif + +tryIO :: MonadIO m => IO a -> ExceptT IOException m a +tryIO = ExceptT . liftIO . Exception.try diff --git a/src/protolude/Protolude/Functor.hs b/src/protolude/Protolude/Functor.hs new file mode 100644 index 0000000000..32fa09280d --- /dev/null +++ b/src/protolude/Protolude/Functor.hs @@ -0,0 +1,62 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE Safe #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Functor ( + Functor(fmap), + ($>), + (<$), + (<$>), + (<<$>>), + (<&>), + void, + foreach, +) where + +import Data.Function ((.), flip) +#if MIN_VERSION_base(4,11,0) +import Data.Functor ((<&>)) +#endif + +#if MIN_VERSION_base(4,7,0) +import Data.Functor ( + Functor(fmap) + , (<$) + , ($>) + , (<$>) + , void + ) +#else +import Data.Functor ( + Functor(fmap) + , (<$) + , (<$>) + ) + + +infixl 4 $> + +($>) :: Functor f => f a -> b -> f b +($>) = flip (<$) + +void :: Functor f => f a -> f () +void x = () <$ x +#endif + +infixl 4 <<$>> + +(<<$>>) :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b) +(<<$>>) = fmap . fmap + +foreach :: Functor f => f a -> (a -> b) -> f b +foreach = flip fmap + +#if !MIN_VERSION_base(4,11,0) +-- | Infix version of foreach. +-- +-- '<&>' is to '<$>' what '&' is to '$'. + +infixl 1 <&> +(<&>) :: Functor f => f a -> (a -> b) -> f b +(<&>) = foreach +#endif diff --git a/src/protolude/Protolude/List.hs b/src/protolude/Protolude/List.hs new file mode 100644 index 0000000000..bd7978bc54 --- /dev/null +++ b/src/protolude/Protolude/List.hs @@ -0,0 +1,52 @@ +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE Safe #-} + +module Protolude.List + ( head, + ordNub, + sortOn, + list, + product, + sum, + groupBy, + ) +where + +import Control.Applicative (pure) +import Data.Foldable (Foldable, foldl', foldr) +import Data.Function ((.)) +import Data.Functor (fmap) +import Data.List (groupBy, sortBy) +import Data.Maybe (Maybe (Nothing)) +import Data.Ord (Ord, comparing) +import qualified Data.Set as Set +import Prelude ((*), (+), Num) + +head :: (Foldable f) => f a -> Maybe a +head = foldr (\x _ -> pure x) Nothing + +sortOn :: (Ord o) => (a -> o) -> [a] -> [a] +sortOn = sortBy . comparing + +-- O(n * log n) +ordNub :: (Ord a) => [a] -> [a] +ordNub l = go Set.empty l + where + go _ [] = [] + go s (x : xs) = + if x `Set.member` s + then go s xs + else x : go (Set.insert x s) xs + +list :: [b] -> (a -> b) -> [a] -> [b] +list def f xs = case xs of + [] -> def + _ -> fmap f xs + +{-# INLINE product #-} +product :: (Foldable f, Num a) => f a -> a +product = foldl' (*) 1 + +{-# INLINE sum #-} +sum :: (Foldable f, Num a) => f a -> a +sum = foldl' (+) 0 diff --git a/src/protolude/Protolude/Monad.hs b/src/protolude/Protolude/Monad.hs new file mode 100644 index 0000000000..cf1674f2de --- /dev/null +++ b/src/protolude/Protolude/Monad.hs @@ -0,0 +1,69 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Monad ( + Monad((>>=), return) + , MonadPlus(mzero, mplus) + + , (=<<) + , (>=>) + , (<=<) + , (>>) + , forever + + , join + , mfilter + , filterM + , mapAndUnzipM + , zipWithM + , zipWithM_ + , foldM + , foldM_ + , replicateM + , replicateM_ + , concatMapM + + , guard + , when + , unless + + , liftM + , liftM2 + , liftM3 + , liftM4 + , liftM5 + , liftM' + , liftM2' + , ap + + , (<$!>) + ) where + +import Protolude.Base (seq) +import Data.List (concat) +import Control.Monad + +concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b] +concatMapM f xs = liftM concat (mapM f xs) + +liftM' :: Monad m => (a -> b) -> m a -> m b +liftM' = (<$!>) +{-# INLINE liftM' #-} + +liftM2' :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c +liftM2' f a b = do + x <- a + y <- b + let z = f x y + z `seq` return z +{-# INLINE liftM2' #-} + +#if !MIN_VERSION_base(4,8,0) +(<$!>) :: Monad m => (a -> b) -> m a -> m b +f <$!> m = do + x <- m + let z = f x + z `seq` return z +{-# INLINE (<$!>) #-} +#endif diff --git a/src/protolude/Protolude/Panic.hs b/src/protolude/Protolude/Panic.hs new file mode 100644 index 0000000000..217dac228a --- /dev/null +++ b/src/protolude/Protolude/Panic.hs @@ -0,0 +1,26 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +#if MIN_VERSION_base(4,9,0) +{-# OPTIONS_GHC -fno-warn-redundant-constraints #-} +#endif + +module Protolude.Panic ( + FatalError(FatalError, fatalErrorMessage), + panic, +) where + +import Protolude.Base (Show) +import Protolude.CallStack (HasCallStack) +import Data.Text (Text) +import Control.Exception as X + +-- | Uncatchable exceptions thrown and never caught. +newtype FatalError = FatalError { fatalErrorMessage :: Text } + deriving (Show) + +instance Exception FatalError + +panic :: HasCallStack => Text -> a +panic a = throw (FatalError a) diff --git a/src/protolude/Protolude/Partial.hs b/src/protolude/Protolude/Partial.hs new file mode 100644 index 0000000000..ae2da86873 --- /dev/null +++ b/src/protolude/Protolude/Partial.hs @@ -0,0 +1,26 @@ +module Protolude.Partial + ( head, + init, + tail, + last, + foldl, + foldr, + foldl', + foldr', + foldr1, + foldl1, + cycle, + maximum, + minimum, + (!!), + sum, + product, + fromJust, + read, + ) +where + +import Data.Foldable (foldl, foldl', foldl1, foldr, foldr', foldr1, product, sum) +import Data.List ((!!), cycle, head, init, last, maximum, minimum, tail) +import Data.Maybe (fromJust) +import Text.Read (read) diff --git a/src/protolude/Protolude/Safe.hs b/src/protolude/Protolude/Safe.hs new file mode 100644 index 0000000000..a1bdb5efaa --- /dev/null +++ b/src/protolude/Protolude/Safe.hs @@ -0,0 +1,137 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE Safe #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Safe ( + headMay + , headDef + , initMay + , initDef + , initSafe + , tailMay + , tailDef + , tailSafe + , lastDef + , lastMay + , foldr1May + , foldl1May + , foldl1May' + , maximumMay + , minimumMay + , maximumDef + , minimumDef + , atMay + , atDef +) where + + +import Data.Ord (Ord, (<)) +import Data.Int (Int) +import Data.Char (Char) +import Data.Bool (Bool, otherwise) +import Data.Maybe (Maybe(Nothing, Just), fromMaybe) +import Data.Either (Either(Left, Right)) +import Data.Function ((.)) +import Data.List (null, head, last, tail, init, maximum, minimum, foldr1, foldl1, foldl1', (++)) + +import Prelude ((-)) +import GHC.Show (show) + +liftMay :: (a -> Bool) -> (a -> b) -> (a -> Maybe b) +liftMay test f val = if test val then Nothing else Just (f val) + +------------------------------------------------------------------------------- +-- Head +------------------------------------------------------------------------------- + +headMay :: [a] -> Maybe a +headMay = liftMay null head + +headDef :: a -> [a] -> a +headDef def = fromMaybe def . headMay + +------------------------------------------------------------------------------- +-- Init +------------------------------------------------------------------------------- + +initMay :: [a] -> Maybe [a] +initMay = liftMay null init + +initDef :: [a] -> [a] -> [a] +initDef def = fromMaybe def . initMay + +initSafe :: [a] -> [a] +initSafe = initDef [] + +------------------------------------------------------------------------------- +-- Tail +------------------------------------------------------------------------------- + +tailMay :: [a] -> Maybe [a] +tailMay = liftMay null tail + +tailDef :: [a] -> [a] -> [a] +tailDef def = fromMaybe def . tailMay + +tailSafe :: [a] -> [a] +tailSafe = tailDef [] + +------------------------------------------------------------------------------- +-- Last +------------------------------------------------------------------------------- + +lastMay :: [a] -> Maybe a +lastMay = liftMay null last + +lastDef :: a -> [a] -> a +lastDef def = fromMaybe def . lastMay + +------------------------------------------------------------------------------- +-- Maximum +------------------------------------------------------------------------------- + +minimumMay, maximumMay :: Ord a => [a] -> Maybe a +minimumMay = liftMay null minimum +maximumMay = liftMay null maximum + +minimumDef, maximumDef :: Ord a => a -> [a] -> a +minimumDef def = fromMaybe def . minimumMay +maximumDef def = fromMaybe def . maximumMay + +------------------------------------------------------------------------------- +-- Foldr +------------------------------------------------------------------------------- + +foldr1May, foldl1May, foldl1May' :: (a -> a -> a) -> [a] -> Maybe a +foldr1May = liftMay null . foldr1 + +------------------------------------------------------------------------------- +-- Foldl +------------------------------------------------------------------------------- + +foldl1May = liftMay null . foldl1 +foldl1May' = liftMay null . foldl1' + +------------------------------------------------------------------------------- +-- At +------------------------------------------------------------------------------- + +at_ :: [a] -> Int -> Either [Char] a +at_ ys o + | o < 0 = Left ("index must not be negative, index=" ++ show o) + | otherwise = f o ys + where + f 0 (x:_) = Right x + f i (_:xs) = f (i-1) xs + f i [] = Left ("index too large, index=" ++ show o ++ ", length=" ++ show (o-i)) + +atMay :: [a] -> Int -> Maybe a +atMay xs i = case xs `at_` i of + Left _ -> Nothing + Right val -> Just val + +atDef :: a -> [a] -> Int -> a +atDef def xs i = case xs `at_` i of + Left _ -> def + Right val -> val diff --git a/src/protolude/Protolude/Semiring.hs b/src/protolude/Protolude/Semiring.hs new file mode 100644 index 0000000000..1d2ffdd05c --- /dev/null +++ b/src/protolude/Protolude/Semiring.hs @@ -0,0 +1,22 @@ +{-# LANGUAGE Safe #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Semiring + ( Semiring, + one, + (<.>), + zero, + ) +where + +import Data.Monoid + +-- | Alias for 'mempty' +zero :: Monoid m => m +zero = mempty + +class Monoid m => Semiring m where + {-# MINIMAL one, (<.>) #-} + + one :: m + (<.>) :: m -> m -> m diff --git a/src/protolude/Protolude/Show.hs b/src/protolude/Protolude/Show.hs new file mode 100644 index 0000000000..567bcca48e --- /dev/null +++ b/src/protolude/Protolude/Show.hs @@ -0,0 +1,83 @@ +{-# LANGUAGE ExtendedDefaultRules #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Show + ( Print, + hPutStr, + putStr, + hPutStrLn, + putStrLn, + putErrLn, + putText, + putErrText, + putLText, + putByteString, + putLByteString, + ) +where + +import Control.Monad.IO.Class (MonadIO, liftIO) +import qualified Data.ByteString.Char8 as BS +import qualified Data.ByteString.Lazy.Char8 as BL +import Data.Function ((.)) +import qualified Data.Text as T +import qualified Data.Text.IO as T +import qualified Data.Text.Lazy as TL +import qualified Data.Text.Lazy.IO as TL +import qualified Protolude.Base as Base +import qualified System.IO as Base +import System.IO (Handle, stderr, stdout) + +class Print a where + hPutStr :: MonadIO m => Handle -> a -> m () + putStr :: MonadIO m => a -> m () + putStr = hPutStr stdout + hPutStrLn :: MonadIO m => Handle -> a -> m () + putStrLn :: MonadIO m => a -> m () + putStrLn = hPutStrLn stdout + putErrLn :: MonadIO m => a -> m () + putErrLn = hPutStrLn stderr + +instance Print T.Text where + hPutStr = \h -> liftIO . T.hPutStr h + hPutStrLn = \h -> liftIO . T.hPutStrLn h + +instance Print TL.Text where + hPutStr = \h -> liftIO . TL.hPutStr h + hPutStrLn = \h -> liftIO . TL.hPutStrLn h + +instance Print BS.ByteString where + hPutStr = \h -> liftIO . BS.hPutStr h + hPutStrLn = \h -> liftIO . BS.hPutStrLn h + +instance Print BL.ByteString where + hPutStr = \h -> liftIO . BL.hPutStr h + hPutStrLn = \h -> liftIO . BL.hPutStrLn h + +instance Print [Base.Char] where + hPutStr = \h -> liftIO . Base.hPutStr h + hPutStrLn = \h -> liftIO . Base.hPutStrLn h + +-- For forcing type inference +putText :: MonadIO m => T.Text -> m () +putText = putStrLn +{-# SPECIALIZE putText :: T.Text -> Base.IO () #-} + +putLText :: MonadIO m => TL.Text -> m () +putLText = putStrLn +{-# SPECIALIZE putLText :: TL.Text -> Base.IO () #-} + +putByteString :: MonadIO m => BS.ByteString -> m () +putByteString = putStrLn +{-# SPECIALIZE putByteString :: BS.ByteString -> Base.IO () #-} + +putLByteString :: MonadIO m => BL.ByteString -> m () +putLByteString = putStrLn +{-# SPECIALIZE putLByteString :: BL.ByteString -> Base.IO () #-} + +putErrText :: MonadIO m => T.Text -> m () +putErrText = putErrLn +{-# SPECIALIZE putErrText :: T.Text -> Base.IO () #-} diff --git a/src/protolude/Protolude/Unsafe.hs b/src/protolude/Protolude/Unsafe.hs new file mode 100644 index 0000000000..af370d1904 --- /dev/null +++ b/src/protolude/Protolude/Unsafe.hs @@ -0,0 +1,75 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE Unsafe #-} +{-# LANGUAGE NoImplicitPrelude #-} + +module Protolude.Unsafe ( + unsafeHead, + unsafeTail, + unsafeInit, + unsafeLast, + unsafeFromJust, + unsafeIndex, + unsafeThrow, + unsafeRead, +) where + +import Protolude.Base (Int) + +#if ( __GLASGOW_HASKELL__ >= 800 ) +import Protolude.Base (HasCallStack) +#endif +import Data.Char (Char) +import Text.Read (Read, read) +import qualified Data.List as List +import qualified Data.Maybe as Maybe +import qualified Control.Exception as Exc + +unsafeThrow :: Exc.Exception e => e -> a +unsafeThrow = Exc.throw + +#if ( __GLASGOW_HASKELL__ >= 800 ) +unsafeHead :: HasCallStack => [a] -> a +unsafeHead = List.head + +unsafeTail :: HasCallStack => [a] -> [a] +unsafeTail = List.tail + +unsafeInit :: HasCallStack => [a] -> [a] +unsafeInit = List.init + +unsafeLast :: HasCallStack => [a] -> a +unsafeLast = List.last + +unsafeFromJust :: HasCallStack => Maybe.Maybe a -> a +unsafeFromJust = Maybe.fromJust + +unsafeIndex :: HasCallStack => [a] -> Int -> a +unsafeIndex = (List.!!) + +unsafeRead :: (HasCallStack, Read a) => [Char] -> a +unsafeRead = Text.Read.read +#endif + + +#if ( __GLASGOW_HASKELL__ < 800 ) +unsafeHead :: [a] -> a +unsafeHead = List.head + +unsafeTail :: [a] -> [a] +unsafeTail = List.tail + +unsafeInit :: [a] -> [a] +unsafeInit = List.init + +unsafeLast :: [a] -> a +unsafeLast = List.last + +unsafeFromJust :: Maybe.Maybe a -> a +unsafeFromJust = Maybe.fromJust + +unsafeIndex :: [a] -> Int -> a +unsafeIndex = (List.!!) + +unsafeRead :: Read a => [Char] -> a +unsafeRead = Text.Read.read +#endif