module LawsTests where

import Common

type TB a = BatchT (Req Integer) (Writer [Integer]) a
type V = Integer
type F = Fun' Integer Integer
type MF = Fun' Integer (TB Integer)

-- TODO: dunno if these Arbitrary instances are any good.

instance Arbitrary (TB Integer) where
    arbitrary = oneof
        [ pure <$> arbitrary
        , liftA2 (>>) (lift . tell . pure <$> arbitrary) arbitrary
        , liftA2 (\f r -> f <$> request r) arbitrary arbitrary
        , liftA2 (<*>) (fmap apply' <$> (arbitrary :: Gen (TB (Fun' Integer Integer)))) arbitrary
        ]

instance Arbitrary (TB F) where
    arbitrary = oneof
        [ pure <$> arbitrary
        , liftA2 (>>) (arbitrary :: Gen (TB Integer)) arbitrary
        ]

instance Show a => Show (TB a) where
    show = show . run

run = runWriter . runBatchT handleReq id

handleReq :: [Req a] -> Writer [Integer] [a]
handleReq = pure . map unReq

-- A property meaning 'a and b have the same effect and return value'
a =~= b = run a === run b
infix 0 =~=

-- Functor laws
prop_functorId (b :: TB V) =
    fmap id b =~= b

prop_functorComp :: TB V -> F -> F -> Property
prop_functorComp b (apply' -> f) (apply' -> g) =
    fmap (f . g) b =~= fmap f (fmap g b)

-- Applicative laws
prop_applicativeId (b :: TB V) =
    pure id <*> b =~= b

prop_applicativeComp :: TB F -> TB F -> TB V -> Property
prop_applicativeComp (fmap apply' -> u) (fmap apply' -> v) w =
    pure (.) <*> u <*> v <*> w =~= u <*> (v <*> w)

prop_applicativeHomomorphism :: F -> V -> Property
prop_applicativeHomomorphism (apply' -> f) x =
    pure f <*> pure x =~= pure (f x)

-- Monad laws
prop_monadRightId (b :: TB V) =
    b >>= return =~= b

prop_monadLeftId (v :: V) (apply' -> k) =
    return v >>= k =~= k v

prop_monadAssoc :: TB V -> MF -> MF -> Property
prop_monadAssoc b (apply' -> f) (apply' -> g) =
    (b >>= f) >>= g =~= b >>= (\x -> f x >>= g)

return []
runTests = $quickCheckAll
