packages feed

fcf-containers-0.8.2: test/Test/Control/Monad.hs

{-# LANGUAGE ScopedTypeVariables    #-}
{-# LANGUAGE TypeApplications       #-}
{-# LANGUAGE TypeFamilies           #-}
{-# LANGUAGE RankNTypes             #-}
{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE TupleSections          #-}
{-# LANGUAGE AllowAmbiguousTypes    #-}
{-# LANGUAGE UndecidableInstances   #-}
{-# OPTIONS_GHC -Wall                       #-}
{-# OPTIONS_GHC -Werror=incomplete-patterns #-}

module Test.Control.Monad where

import           Data.Functor ((<&>))
import           Data.Proxy
import           Control.Applicative
import           Test.Hspec (describe, it, shouldBe, Spec)
import           GHC.TypeLits (Nat, Symbol)

import           Fcf hiding (type (<*>))
import           Fcf.Data.Tuple
import           Fcf.Data.List (Cons)
import           Fcf.Control.Monad
import           Fcf.Class.Monoid (type (.<>))
import           Fcf.Data.Reflect (fromType)

-- data TraversableTest = TraversableTest Symbol [(Symbol, Nat)]

data SafeMinus :: Nat -> Nat -> Exp (Either Symbol Nat)
type instance Eval (SafeMinus a b) = If (Eval (a >= b)) ('Right (Eval (a - b))) ('Left "Nat cannot be negative")


spec :: Spec
spec = describe "Monad" $ do
  it "liftA2 (,) instance" $ do
    let test :: forall r. (r ~ Eval (LiftA2 Tuple2 '("hello ", 5) '("world", 6))) => (String, (Int, Int))
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      liftA2 (,) ("hello ", 5) ("world", 6)
  it "<*> (,) instance" $ do
    let test :: forall r. (r ~ Eval ('("hello ", (+) 15) <*> '("world!", 2002))) => (String,Int)
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      ("hello ", (+) 15) <*> ("world!", 2002)
  it ">>= (,) instance" $ do
    let test :: forall r. (r ~ Eval ('("age", 5) >>= (Return <=< (+) 1))) => (String,Int)
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      (("age",5) <&> (+) 1)
      -- Above was suggested by hlint for the following:
      -- (("age",5) >>= return . (+) 1)
  it "liftA3 (,,) instance" $ do
    let test :: forall r. (r ~ Eval (LiftA3 Tuple3 '("hello ", "<", 6) '("world", " ", 8) '("!", ">", 10))) => (String, String, (Int, Int, Int))
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      liftA3 (,,) ("hello ","<",6) ("world"," ",8) ("!",">",10)
  it ">>= (,,) instance" $ do
    let test :: forall r. (r ~ Eval ('("he","wo",19) >>= (Tuple3 "llo" "rld" <=< Flip (-) 1))) => (String,String,Int)
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      (("he","wo",19) >>= ("llo","rld",) . flip (-) 1)
  it "liftA4 Either instance test Left" $ do
    let test :: forall r. (r ~ Eval (LiftA4 Tuple4 ('Right 5) ('Right 6) ('Right 7) ('Left "fail"))) => Either String (Int,Int,Int,Int)
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      (,,,) <$> Right 5 <*> Right 6 <*> Right 7 <*> Left "fail"
  it "liftA4 Either instance test Right" $ do
    let test :: forall r. (r ~ Eval (LiftA4 Tuple4 ('Right 5) ('Right 6) ('Right 7) ('Right 0))) => Either String (Int,Int,Int,Int)
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      (,,,) <$> Right 5 <*> Right 6 <*> Right 7 <*> Right 0
  it "Traversable Either instance, test with MapM" $ do
    let typeLevelFunc :: forall (list :: [(Symbol,Nat)]) r. 
            (r ~ Eval 
              (Map 
                (Flip Cons '[] <=< Tuple2 "test") 
                (MapM
                  (MapM (Flip SafeMinus 1))
                  @@ list
                )
              )
            ) => Proxy r
        typeLevelFunc = Proxy @r
    let safeMinus a b = if a >= b then Right (a - b) else Left "Nat cannot be negative"
    let valTest :: [(String,Int)] -> Either String [(String, [(String, Int)])]
        valTest list = fmap (flip (:) [] . ("test",)) (mapM (mapM (`safeMinus` 1)) list)
    fromType (typeLevelFunc @'[ '("key1",1), '("key2",2)])
      `shouldBe` 
      valTest [("key1",1),("key2",2)]
    fromType (typeLevelFunc @'[ '("key1",0), '("key2",2)])
      `shouldBe` 
      valTest [("key1",0),("key2",2)]
  it "liftA5 [] instance" $ do
    let test :: forall r. (r ~ Eval (LiftA5 Tuple5 '[1,5] '[2,4] '[6,7,8] '[9,11,10] '[0] )) => [(Int,Int,Int,Int,Int)]
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      (,,,,) <$> [1,5] <*> [2,4] <*> [6,7,8] <*> [9,11,10] <*> [0]
  it "<*> [] instance" $ do
    let test :: forall r. (r ~ Eval ('[(+) 1,(+) 2] <*> '[3,4])) => [Int]
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      [(+) 1,(+) 2] <*> [3,4]
  it ">>= [] instance" $ do
    let test :: forall r. (r ~ Eval ('[5,6,7] >>= Plus2M)) => [Int]
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      ([5,6,7] >>= (\n -> [n + 2, n + 3]))
  it ">>= Maybe instance test Just" $ do
    let test :: forall r. (r ~ Eval ('Just "hello" >>= (Return <=< Flip (.<>) " world"))) => Maybe String
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      (Just "hello" <&> flip (<>) " world")
      -- Above was suggested by hlint for the following:
      -- (Just "hello" >>= (return . flip (<>) " world"))
  it ">>= Maybe instance test Nothing" $ do
    let test :: forall r. (r ~ Eval ('Nothing >>= (Return <=< Flip (.<>) " world"))) => Maybe String
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      (Nothing <&> flip (<>) " world")
      -- Above was suggested by hlint for the following:
      -- (Nothing >>= (return . flip (<>) " world"))
  it ">> Maybe instance" $ do
    let test :: forall r. (r ~ Eval ('Just 5 >> 'Just "hello world")) => Maybe String
        test = fromType (Proxy @r)
    test 
      `shouldBe` 
      (Just (5 :: Int) >> Just "hello world")