{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ExplicitNamespaces #-}

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeOperators #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  FirstPrelude
-- Copyright   :  (c) University of Kent 2022
-- License     :  BSD-style
--
-- Maintainer  :  Dominic Orchard
-- Stability   :  experimental
-- Portability :  portable
--
-- FirstPrelude is a non-exhaustive replacement for Prelude aimed at
-- absolute beginners to Haskell. It largely tries to bypass the need
-- for type classes (arithmetic is specialised to Integers), it
-- provides some simplifications to Prelude, and provides some custom
-- error messages.
--
-----------------------------------------------------------------------------

module FirstPrelude (

    -- * Infrastructure
    ifThenElse,

    -- * Standard types

    -- ** Basic data types
    Bool(False, True),
    (&&), (||), not, otherwise,

    Maybe(Nothing, Just),
    maybe,

    Either(Left, Right),
    either,

    Char, String,
    eqChar, eqString,

    -- *** Tuples
    fst, snd, curry, uncurry,

    -- ** Basic comparators (specialised to Integer)
    -- and enumerations
    (==), (/=),
    (<), (<=), (>=), (>), max, min,
    succ, pred,
    enumFrom, enumFromThen,
    enumFromTo, enumFromThenTo,

    -- ** Numbers

    -- *** Just expose Integer
    Integer,

    -- *** Numeric operations
    (+), (-), (*), negate, abs, signum, fromInteger,
    quot, rem, div, mod, quotRem, divMod, toInteger,
    (^),

    -- ** Monads and functors
    fmap,
    (>>=), (>>), return,
    fail,

    -- ** Higher-order functions on lists
    foldr,     -- :: (a -> b -> b) -> b -> [a] -> b
    foldl,     -- :: (b -> a -> b) -> b -> [a] -> b

    -- ** Miscellaneous functions
    id, const, (.), flip, ($), until,
    asTypeOf, error, errorWithoutStackTrace, undefined,
    seq,

    -- * List operations
    and, or,
    List.map, (List.++), List.filter, List.concat, List.concatMap,
    head, last, tail, init, (!!),
    null, length,
    List.reverse,
    -- *** Scans
    List.scanl, List.scanl1, List.scanr, List.scanr1,
    -- *** Infinite lists
    List.iterate, List.repeat, replicate, List.cycle,
    -- ** Sublists
    take, drop,
    List.takeWhile, List.dropWhile,
    List.span, List.break,
    splitAt,
    -- ** Zipping and unzipping lists
    List.zip, List.zip3,
    List.zipWith, List.zipWith3,
    List.unzip, List.unzip3,
    -- ** Functions on strings
    List.lines, List.words, List.unlines, List.unwords,

    -- * Show / Read (simplified)
    Show(showsPrec, show),
    read,

    -- * Basic Input and output
    IO,
    -- ** Simple I\/O operations
    -- All I/O functions defined here are character oriented.  The
    -- treatment of the newline character will vary on different systems.
    -- For example, two characters of input, return and linefeed, may
    -- read as a single newline character.  These functions cannot be
    -- used portably for binary I/O.
    -- *** Output functions
    putChar,
    putStr, putStrLn, print,
    -- *** Input functions
    getChar,
    getLine, getContents, interact,
    -- *** Files
    FilePath,
    readFile, writeFile, appendFile, readIO, readLn,
    -- ** Exception handling in the I\/O monad
    IOError, ioError, userError,

  ) where

import qualified Control.Monad as Monad
import System.IO
import System.IO.Error
import qualified Data.List as List
import Data.Either
import Data.Functor     ( (<$>) )
import Data.Maybe
import Data.Tuple

import GHC.Base hiding ( foldr, mapM, sequence, Eq(..), Ord(..), Monad(..), eqChar, eqString, error, undefined )
import qualified GHC.Err
import qualified Text.Read as Read
import qualified GHC.Enum as Enum
import qualified GHC.Num as Num
import GHC.Num(Integer)
import qualified GHC.Real as NumR
import qualified Data.Ord as Ord
import qualified Data.Eq  as Eq
import GHC.Show

import GHC.TypeLits

-- Re-export some monomorphised things from foldable
import qualified Data.Foldable as Foldable

default (Integer)

-- So that RebindableSyntax can also be used (though this is optional)
ifThenElse :: Bool -> a -> a -> a
ifThenElse :: forall a. Bool -> a -> a -> a
ifThenElse Bool
True a
x a
_  = a
x
ifThenElse Bool
False a
_ a
y = a
y

-- ** Monomorphised equality for Char and String

eqChar :: Char -> Char -> Bool
eqChar :: Char -> Char -> Bool
eqChar = Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
(Eq.==)

eqString :: String -> String -> Bool
eqString :: String -> String -> Bool
eqString = String -> String -> Bool
forall a. Eq a => a -> a -> Bool
(Eq.==)

-- ** Monomorphised comparisons and arithmetic

(==), (/=), (<), (<=), (>=), (>) :: Integer -> Integer -> Bool
== :: Integer -> Integer -> Bool
(==) = Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
(Eq.==)
/= :: Integer -> Integer -> Bool
(/=) = Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
(Eq./=)
< :: Integer -> Integer -> Bool
(<)  = Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
(Ord.<)
<= :: Integer -> Integer -> Bool
(<=) = Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
(Ord.<=)
>= :: Integer -> Integer -> Bool
(>=) = Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
(Ord.>=)
> :: Integer -> Integer -> Bool
(>)  = Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
(Ord.>)

max, min :: Integer -> Integer -> Integer
max :: Integer -> Integer -> Integer
max = Integer -> Integer -> Integer
forall a. Ord a => a -> a -> a
Ord.max
min :: Integer -> Integer -> Integer
min = Integer -> Integer -> Integer
forall a. Ord a => a -> a -> a
Ord.min

succ, pred :: Integer -> Integer
succ :: Integer -> Integer
succ = Integer -> Integer
forall a. Enum a => a -> a
Enum.succ
pred :: Integer -> Integer
pred = Integer -> Integer
forall a. Enum a => a -> a
Enum.pred

enumFrom :: Integer -> [Integer]
enumFrom :: Integer -> [Integer]
enumFrom = Integer -> [Integer]
forall a. Enum a => a -> [a]
Enum.enumFrom

enumFromThen :: Integer -> Integer -> [Integer]
enumFromThen :: Integer -> Integer -> [Integer]
enumFromThen = Integer -> Integer -> [Integer]
forall a. Enum a => a -> a -> [a]
Enum.enumFromThen

enumFromTo :: Integer -> Integer -> [Integer]
enumFromTo :: Integer -> Integer -> [Integer]
enumFromTo = Integer -> Integer -> [Integer]
forall a. Enum a => a -> a -> [a]
Enum.enumFromTo

enumFromThenTo :: Integer -> Integer -> Integer -> [Integer]
enumFromThenTo :: Integer -> Integer -> Integer -> [Integer]
enumFromThenTo = Integer -> Integer -> Integer -> [Integer]
forall a. Enum a => a -> a -> a -> [a]
Enum.enumFromThenTo

(+), (-), (*), quot, rem, div, mod :: Integer -> Integer -> Integer
+ :: Integer -> Integer -> Integer
(+) = Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
(Num.+)
(-) = Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
(Num.-)
* :: Integer -> Integer -> Integer
(*) = Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
(Num.*)
quot :: Integer -> Integer -> Integer
quot = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
NumR.quot
rem :: Integer -> Integer -> Integer
rem = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
NumR.rem
div :: Integer -> Integer -> Integer
div = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
NumR.div
mod :: Integer -> Integer -> Integer
mod = Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
NumR.mod

negate, abs, signum, fromInteger, toInteger :: Integer -> Integer
negate :: Integer -> Integer
negate = Integer -> Integer
forall a. Num a => a -> a
Num.negate
abs :: Integer -> Integer
abs    = Integer -> Integer
forall a. Num a => a -> a
Num.abs
signum :: Integer -> Integer
signum = Integer -> Integer
forall a. Num a => a -> a
Num.signum
fromInteger :: Integer -> Integer
fromInteger = Integer -> Integer
forall a. a -> a
id
toInteger :: Integer -> Integer
toInteger   = Integer -> Integer
forall a. a -> a
id

quotRem, divMod :: Integer -> Integer -> (Integer, Integer)
quotRem :: Integer -> Integer -> (Integer, Integer)
quotRem = Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
NumR.quotRem
divMod :: Integer -> Integer -> (Integer, Integer)
divMod  = Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
NumR.divMod

(^) :: Integer -> Integer -> Integer
^ :: Integer -> Integer -> Integer
(^) = Integer -> Integer -> Integer
forall a b. (Num a, Integral b) => a -> b -> a
(NumR.^)

-- ** List functions

-- Avoids the Int/Integer problem
length :: [a] -> Integer
length :: forall a. [a] -> Integer
length []     = Integer
0
length (a
_:[a]
xs) = Integer
1 Integer -> Integer -> Integer
+ [a] -> Integer
forall a. [a] -> Integer
length [a]
xs

take :: Integer -> [a] -> [a]
take :: forall a. Integer -> [a] -> [a]
take Integer
n [a]
_      | Integer
n Integer -> Integer -> Bool
<= Integer
0 = []
take Integer
_ []              = []
take Integer
n (a
x:[a]
xs)         = a
x a -> [a] -> [a]
forall a. a -> [a] -> [a]
: Integer -> [a] -> [a]
forall a. Integer -> [a] -> [a]
take (Integer
nInteger -> Integer -> Integer
-Integer
1) [a]
xs

drop :: Integer -> [a] -> [a]
drop :: forall a. Integer -> [a] -> [a]
drop Integer
n [a]
xs     | Integer
n Integer -> Integer -> Bool
<= Integer
0 = [a]
xs
drop Integer
_ []              = []
drop Integer
n (a
_:[a]
xs)         = Integer -> [a] -> [a]
forall a. Integer -> [a] -> [a]
drop (Integer
nInteger -> Integer -> Integer
-Integer
1) [a]
xs

replicate :: Integer -> a -> [a]
replicate :: forall a. Integer -> a -> [a]
replicate Integer
n a
x | Integer
n Integer -> Integer -> Bool
<= Integer
0 = []
replicate Integer
n a
x          = a
x a -> [a] -> [a]
forall a. a -> [a] -> [a]
: Integer -> a -> [a]
forall a. Integer -> a -> [a]
replicate (Integer
nInteger -> Integer -> Integer
-Integer
1) a
x

splitAt :: Integer -> [a] -> ([a], [a])
splitAt :: forall a. Integer -> [a] -> ([a], [a])
splitAt Integer
n [a]
xs | Integer
n Integer -> Integer -> Bool
<= Integer
0 = ([], [a]
xs)
splitAt Integer
_ []          = ([], [])
splitAt Integer
n (a
x:[a]
xs)     = let ([a]
ys, [a]
zs) = Integer -> [a] -> ([a], [a])
forall a. Integer -> [a] -> ([a], [a])
splitAt (Integer
nInteger -> Integer -> Integer
-Integer
1) [a]
xs in (a
xa -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
ys, [a]
zs)

and :: [Bool] -> Bool
and :: [Bool] -> Bool
and []       = Bool
True
and (Bool
b : [Bool]
bs) = Bool
b Bool -> Bool -> Bool
&& [Bool] -> Bool
and [Bool]
bs

or :: [Bool] -> Bool
or :: [Bool] -> Bool
or []        = Bool
False
or (Bool
b : [Bool]
bs)  = Bool
b Bool -> Bool -> Bool
|| [Bool] -> Bool
or [Bool]
bs

(!!) :: [a] -> Integer -> a
(a
x:[a]
_)  !! :: forall a. [a] -> Integer -> a
!! Integer
n | Integer
n Integer -> Integer -> Bool
== Integer
0    = a
x
(a
_:[a]
xs) !! Integer
n | Integer
n Integer -> Integer -> Bool
> Integer
0     = [a]
xs [a] -> Integer -> a
forall a. [a] -> Integer -> a
!! (Integer
n Integer -> Integer -> Integer
- Integer
1)
[a]
_      !! Integer
_              = String -> a
forall a. String -> a
error String
"FirstPrelude.!!: index out of bounds"

-- Overriding these partial functions to remove HasCallStack constraint
head :: [a] -> a
head :: forall a. [a] -> a
head (a
x:[a]
_) = a
x
head []    = String -> a
forall a. String -> a
error String
"FirstPrelude.head: empty list"

tail :: [a] -> [a]
tail :: forall a. [a] -> [a]
tail (a
_:[a]
xs) = [a]
xs
tail []     = String -> [a]
forall a. String -> a
error String
"FirstPrelude.tail: empty list"

last :: [a] -> a
last :: forall a. [a] -> a
last [a
x]    = a
x
last (a
_:[a]
xs) = [a] -> a
forall a. [a] -> a
last [a]
xs
last []     = String -> a
forall a. String -> a
error String
"FirstPrelude.last: empty list"

init :: [a] -> [a]
init :: forall a. [a] -> [a]
init [a
_]    = []
init (a
x:[a]
xs) = a
x a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a] -> [a]
forall a. [a] -> [a]
init [a]
xs
init []     = String -> [a]
forall a. String -> a
error String
"FirstPrelude.init: empty list"

-- ** Monomorphised fold things

null :: [a] -> Bool
null :: forall a. [a] -> Bool
null = [a] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
Foldable.null

foldl :: (b -> a -> b) -> b -> [a] -> b
foldl :: forall b a. (b -> a -> b) -> b -> [a] -> b
foldl = (b -> a -> b) -> b -> [a] -> b
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
Foldable.foldl

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr :: forall a b. (a -> b -> b) -> b -> [a] -> b
foldr = (a -> b -> b) -> b -> [a] -> b
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
Foldable.foldr

-- ** Monomorphised monads

return :: a -> IO a
return :: forall a. a -> IO a
return = a -> IO a
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
Monad.return

(>>) :: IO a -> IO b -> IO b
>> :: forall a b. IO a -> IO b -> IO b
(>>) = IO a -> IO b -> IO b
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
(Monad.>>)

(>>=) :: IO a -> (a -> IO b) -> IO b
>>= :: forall a b. IO a -> (a -> IO b) -> IO b
(>>=) = IO a -> (a -> IO b) -> IO b
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
(Monad.>>=)

fail :: String -> IO a
fail :: forall a. String -> IO a
fail = (String -> IO a
forall a. String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
Monad.fail)

-- ** Overriding error and undefined to remove HasCallStack constraint

error :: String -> a
error :: forall a. String -> a
error = String -> a
forall a. HasCallStack => String -> a
GHC.Err.error

undefined :: a
undefined :: forall a. a
undefined = a
forall a. HasCallStack => a
GHC.Err.undefined

-- ** Show gets a fancy error message

read :: String -> Integer
read :: String -> Integer
read = String -> Integer
forall a. Read a => String -> a
Read.read

instance TypeError
           (Text "Cannot show (pretty print) functions (yours is of type "
           :<>: ShowType a :<>: Text " -> " :<>: ShowType b :<>: Text ")"
           :$$: Text "" :$$: Text "Perhaps there is a missing argument?" :$$: Text "")
           => Show (a -> b) where
   show :: (a -> b) -> String
show = (a -> b) -> String
forall a. a
undefined