{-# 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,

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

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

    -- ** Numbers

    -- *** Only Integers for now folks
    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
    List.map, (List.++), List.filter,
    List.head, List.last, List.tail, List.init, (List.!!),
    null, length,
    List.reverse,
    -- *** Scans
    List.scanl, List.scanl1, List.scanr, List.scanr1,
    -- *** Infinite lists
    List.iterate, List.repeat, List.replicate, List.cycle,
    -- ** Sublists
    List.take, List.drop,
    List.takeWhile, List.dropWhile,
    List.span, List.break,
    List.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(..) )
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
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

-- 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
+ forall a. [a] -> Integer
length [a]
xs

-- ** Monomorphised comparisons and arithmetic

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

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

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

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

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

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

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

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

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

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

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

-- ** Monomorphised fold things

null :: [a] -> Bool
null :: forall a. [a] -> Bool
null = 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 = 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 = 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 = 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
(>>) = 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
(>>=) = 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 = (forall (m :: * -> *) a. MonadFail m => String -> m a
Monad.fail)

-- ** Show gets a fancy error message

read :: String -> Integer
read :: String -> Integer
read = 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 = forall a. HasCallStack => a
undefined