module Ch07a1 where

import Prelude        (Unit, discard, (<>))
import Effect         (Effect)
import Effect.Console (log)

-------------------- JS Primitives --------------------------------------------------------

foreign import ordIntImpl     :: Ordering -> Ordering -> Ordering -> Int -> Int -> Ordering
foreign import eqIntImpl      :: Int -> Int -> Boolean
foreign import showIntImpl    :: Int -> String
foreign import showStringImpl :: String -> String

-------------------- Functions ------------------------------------------------------------

apply :: ∀ a b. (a -> b) -> a -> b
apply f = f

infixr 0 apply as $

-----------------
-- Comparisons --
-----------------
lessThan :: ∀ a. Ord a => a -> a -> Boolean
lessThan a b = case compare a b of
  LT -> true
  _  -> false

lessThanOrEq :: ∀ a. Ord a => a -> a -> Boolean
lessThanOrEq a b    = case compare a b of
                        GT -> false
                        _  -> true

greaterThan :: ∀ a. Ord a => a -> a -> Boolean
greaterThan a b     = case compare a b of
                        GT -> true
                        _  -> false

greaterThanOrEq :: ∀ a. Ord a => a -> a -> Boolean
greaterThanOrEq a b = case compare a b of
                        LT -> false
                        _  -> true

infixl 4 lessThanOrEq    as <=
infixl 4 lessThan        as <
infixl 4 greaterThan     as >
infixl 4 greaterThanOrEq as >=

-------------------- Data Types -----------------------------------------------------------

data Maybe a    = Nothing | Just a
data Either a b = Left a | Right b
data Ordering   = LT | GT | EQ

-------------------- Type Classes ---------------------------------------------------------

class Eq a where
  eq :: a -> a -> Boolean

infix 1 eq as ==

-- class Ord a where (would lead to same results in our cases)
class Eq a <= Ord a where
  compare :: a -> a -> Ordering

class Show a where
  show :: a -> String

-------------------- Type Classes Instances -----------------------------------------------

----------
-- Show --
----------
instance showUnit :: Show Unit where
  show _ = "unit"

instance showBoolean :: Show Boolean where
  show false = "false"
  show true  = "true"

instance showInt :: Show Int where
  show = showIntImpl

instance showString :: Show String where
  show = showStringImpl

instance showMaybe :: Show a => Show (Maybe a) where
  show Nothing  = "Nothing"
  show (Just a) = "(Just " <> show a <> ")"

instance showEither :: (Show a, Show b) => Show (Either a b) where
  show (Left  a) = "(Left "  <> show a <> ")"
  show (Right b) = "(Right " <> show b <> ")"

--------
-- Eq --
--------
instance eqUnit :: Eq Unit where
  eq _ _ = true

instance eqInt :: Eq Int where
  eq = eqIntImpl

instance eqMaybe :: Eq a => Eq (Maybe a) where
  eq Nothing   Nothing   = true
  eq (Just a1) (Just a2) = a1 == a2
  eq _         _         = false

instance eqEither :: (Eq a, Eq b) => Eq (Either a b) where
  eq (Left  a1) (Left  a2) = a1 == a2
  eq (Right b1) (Right b2) = b1 == b2
  eq _          _          = false

---------
-- Ord --
---------
instance ordInt :: Ord Int where
  compare = ordIntImpl LT EQ GT

instance ordMaybe :: Ord a => Ord (Maybe a) where
  compare Nothing  Nothing  = EQ
  compare Nothing  _        = LT
  compare _        Nothing  = GT
  compare (Just a) (Just b) = compare a b

instance ordEither :: (Ord a, Ord b) => Ord (Either a b) where
  compare (Left  a1) (Left  a2) = compare a1 a2
  compare (Right b1) (Right b2) = compare b1 b2
  compare (Left  _)  _          = LT
  compare (Right _)  _          = GT

-------------------- Tests ----------------------------------------------------------------

test :: Effect Unit
test = do
  log "Uncomment each line. IMPLEMENT missing functions BY HAND !!! No further imports!"
  log $ show $ Just 5  == Just 5                             -- true
  log $ show $ Just 5  == Just 2                             -- false
  log $ show $ Just 5  == Nothing                            -- false
  log $ show $ Nothing == Just 5                             -- false
  log $ show $ Nothing == (Nothing :: Maybe Unit)            -- true
  log $ show $ (Left "left" :: Either String Unit)           -- (Left "left")
  log $ show $ (Right (Just 42) :: Either Unit (Maybe Int))  -- (Right (Just 42))
  log $ show $ Just 1  <  Just 5                             -- true
  log $ show $ Just 5  <= Just 5                             -- true
  log $ show $ Just 5  >  Just 10                            -- false
  log $ show $ Just 10 >= Just 10                            -- true
  log $ show $ Just 99 >  Nothing                            -- true
  log $ show $ Just 99 <  Nothing                            -- false
  log $ show $ Just "abc"                                    -- (Just "abc")
  log $ show $ (Nothing :: Maybe Unit)                       -- Nothing