hydra-0.15.0: src/main/haskell/Hydra/Sources/Test/Lib/Eithers.hs
{-# LANGUAGE FlexibleContexts #-}
module Hydra.Sources.Test.Lib.Eithers where
-- Standard imports for shallow DSL tests
import Hydra.Kernel
import Hydra.Dsl.Meta.Testing as Testing
import Hydra.Dsl.Meta.Terms as Terms
import Hydra.Sources.Kernel.Types.All
import qualified Hydra.Dsl.Meta.Core as Core
import qualified Hydra.Dsl.Meta.Phantoms as Phantoms
import qualified Hydra.Dsl.Meta.Types as T
import qualified Hydra.Sources.Test.TestGraph as TestGraph
import qualified Hydra.Sources.Test.TestTerms as TestTerms
import qualified Hydra.Sources.Test.TestTypes as TestTypes
import qualified Data.List as L
import qualified Data.Map as M
-- Additional imports specific to this file
import Hydra.Testing
import Hydra.Sources.Libraries
import qualified Hydra.Dsl.Meta.Lib.Eithers as Eithers
import qualified Hydra.Dsl.Meta.Lib.Equality as Equality
import qualified Hydra.Dsl.Meta.Lib.Literals as Literals
import qualified Hydra.Dsl.Meta.Lib.Logic as Logic
import qualified Hydra.Dsl.Meta.Lib.Math as Math
import qualified Hydra.Dsl.Meta.Lib.Maybes as Maybes
import qualified Hydra.Dsl.Meta.Lib.Strings as Strings
import qualified Hydra.Sources.Kernel.Terms.Show.Core as ShowCore
ns :: Namespace
ns = Namespace "hydra.test.lib.eithers"
module_ :: Module
module_ = Module {
moduleNamespace = ns,
moduleDefinitions = definitions,
moduleTermDependencies = [Namespace "hydra.reduction", ShowCore.ns],
moduleTypeDependencies = kernelTypesNamespaces,
moduleDescription = Just "Test cases for hydra.lib.eithers primitives"}
where
definitions = [Phantoms.toDefinition allTests]
(#) :: (AsTerm f (a -> b), AsTerm g a) => f -> g -> TTerm b
(#) = (Phantoms.@@)
infixl 1 #
-- Show functions
showInt32 :: TTerm (Int -> String)
showInt32 = Phantoms.lambda "n" $ Literals.showInt32 (Phantoms.var "n")
showBool :: TTerm (Bool -> String)
showBool = Phantoms.lambda "b" $ Literals.showBoolean (Phantoms.var "b")
showStr :: TTerm (String -> String)
showStr = Phantoms.lambda "s" $ Literals.showString (Phantoms.var "s")
showIntList :: TTerm ([Int] -> String)
showIntList = Phantoms.lambda "xs" $ ShowCore.list_ # showInt32 # Phantoms.var "xs"
showStrList :: TTerm ([String] -> String)
showStrList = Phantoms.lambda "xs" $ ShowCore.list_ # showStr # Phantoms.var "xs"
showEitherIntString :: TTerm (Either Int String -> String)
showEitherIntString = Phantoms.lambda "e" $ ShowCore.either_ # showInt32 # showStr # Phantoms.var "e"
showEitherIntInt :: TTerm (Either Int Int -> String)
showEitherIntInt = Phantoms.lambda "e" $ ShowCore.either_ # showInt32 # showInt32 # Phantoms.var "e"
showEitherStringIntList :: TTerm (Either String [Int] -> String)
showEitherStringIntList = Phantoms.lambda "e" $ ShowCore.either_ # showStr # showIntList # Phantoms.var "e"
showEitherStringMaybeInt :: TTerm (Either String (Maybe Int) -> String)
showEitherStringMaybeInt = Phantoms.lambda "e" $ ShowCore.either_ # showStr # showMaybeInt # Phantoms.var "e"
showMaybeInt :: TTerm (Maybe Int -> String)
showMaybeInt = Phantoms.lambda "mx" $ ShowCore.maybe_ # showInt32 # Phantoms.var "mx"
showPairIntListStringList :: TTerm (([Int], [String]) -> String)
showPairIntListStringList = Phantoms.lambda "p" $ ShowCore.pair_ # showIntList # showStrList # Phantoms.var "p"
-- Phantom-typed helpers
leftInt :: Int -> TTerm (Either Int b)
leftInt x = Phantoms.left (Phantoms.int32 x)
rightStr :: String -> TTerm (Either a String)
rightStr s = Phantoms.right (Phantoms.string s)
rightInt :: Int -> TTerm (Either a Int)
rightInt x = Phantoms.right (Phantoms.int32 x)
-- Test groups for hydra.lib.eithers primitives
eithersBind :: TTerm TestGroup
eithersBind = subgroup "bind" [
test "bind Right with success" (rightStr "ab") (rightInt 2),
test "bind Right with failure" (rightStr "") (leftInt 0),
test "bind Left returns Left unchanged" (leftInt 42) (leftInt 42)]
where
bindFn = Phantoms.lambda "s" $
Logic.ifElse (Strings.null (Phantoms.var "s"))
(Phantoms.left (Phantoms.int32 0))
(Phantoms.right (Strings.length (Phantoms.var "s")))
test name input expected = evalPair name showEitherIntInt
(Eithers.bind input bindFn)
expected
eithersBimap :: TTerm TestGroup
eithersBimap = subgroup "bimap" [
test "map left value" (leftInt 5) (leftInt 10),
test "map right value" (rightStr "ab") (rightInt 2)]
where
test name input expected = evalPair name showEitherIntInt
(Eithers.bimap
(Phantoms.lambda "x" $ Math.mul (Phantoms.var "x") (Phantoms.int32 2))
(Phantoms.lambda "s" $ Strings.length (Phantoms.var "s"))
input)
expected
eithersIsLeft :: TTerm TestGroup
eithersIsLeft = subgroup "isLeft" [
test "left value" (leftInt 42) True,
test "right value" (rightStr "test") False]
where
test name x result = evalPair name showBool
(Eithers.isLeft x)
(Phantoms.boolean result)
eithersIsRight :: TTerm TestGroup
eithersIsRight = subgroup "isRight" [
test "right value" (rightStr "test") True,
test "left value" (leftInt 42) False]
where
test name x result = evalPair name showBool
(Eithers.isRight x)
(Phantoms.boolean result)
eithersFromLeft :: TTerm TestGroup
eithersFromLeft = subgroup "fromLeft" [
test "extract left" 99 (leftInt 42) 42,
test "use default for right" 99 (rightStr "test") 99]
where
test name def x result = evalPair name showInt32
(Eithers.fromLeft (Phantoms.int32 def) x)
(Phantoms.int32 result)
eithersFromRight :: TTerm TestGroup
eithersFromRight = subgroup "fromRight" [
test "extract right" "default" (rightStr "test") "test",
test "use default for left" "default" (leftInt 42) "default"]
where
test name def x result = evalPair name showStr
(Eithers.fromRight (Phantoms.string def) x)
(Phantoms.string result)
eithersEither :: TTerm TestGroup
eithersEither = subgroup "either" [
test "apply left function" (leftInt 5) 10,
test "apply right function" (rightStr "ab") 2]
where
test name x result = evalPair name showInt32
(Eithers.either_
(Phantoms.lambda "x" $ Math.mul (Phantoms.var "x") (Phantoms.int32 2))
(Phantoms.lambda "s" $ Strings.length (Phantoms.var "s"))
x)
(Phantoms.int32 result)
eithersLefts :: TTerm TestGroup
eithersLefts = subgroup "lefts" [
test "filter left values" [leftInt 1, rightStr "a", leftInt 2, rightStr "b"] [1, 2],
test "all lefts" [leftInt 1, leftInt 2] [1, 2],
test "all rights" [rightStr "a", rightStr "b"] [],
test "empty list" ([] :: [TTerm (Either Int String)]) []]
where
test name input expected = evalPair name showIntList
(Eithers.lefts (Phantoms.list input))
(Phantoms.list (Phantoms.int32 <$> expected))
eithersRights :: TTerm TestGroup
eithersRights = subgroup "rights" [
test "filter right values" [leftInt 1, rightStr "a", leftInt 2, rightStr "b"] ["a", "b"],
test "all rights" [rightStr "a", rightStr "b"] ["a", "b"],
test "all lefts" [leftInt 1, leftInt 2] [],
test "empty list" ([] :: [TTerm (Either Int String)]) []]
where
test name input expected = evalPair name showStrList
(Eithers.rights (Phantoms.list input))
(Phantoms.list (Phantoms.string <$> expected))
eithersPartitionEithers :: TTerm TestGroup
eithersPartitionEithers = subgroup "partitionEithers" [
test "partition mixed" [leftInt 1, rightStr "a", leftInt 2, rightStr "b"] ([1, 2], ["a", "b"]),
test "all lefts" [leftInt 1, leftInt 2] ([1, 2], []),
test "all rights" [rightStr "a", rightStr "b"] ([], ["a", "b"]),
test "empty list" ([] :: [TTerm (Either Int String)]) ([], [])]
where
test name input (lefts, rights) = evalPair name showPairIntListStringList
(Eithers.partitionEithers (Phantoms.list input))
(Phantoms.pair (Phantoms.list (Phantoms.int32 <$> lefts)) (Phantoms.list (Phantoms.string <$> rights)))
eithersMap :: TTerm TestGroup
eithersMap = subgroup "map" [
test "map right value" (rightInt 5) (rightInt 10),
test "preserve left" (leftInt 99) (leftInt 99)]
where
test name input expected = evalPair name showEitherIntInt
(Eithers.map
(Phantoms.lambda "x" $ Math.mul (Phantoms.var "x") (Phantoms.int32 2))
input)
expected
eithersMapList :: TTerm TestGroup
eithersMapList = subgroup "mapList" [
test "all succeed" [1, 2, 3] (Phantoms.right (Phantoms.list [Phantoms.int32 2, Phantoms.int32 4, Phantoms.int32 6])),
test "first fails" [1, 0, 3] (Phantoms.left (Phantoms.string "zero")),
test "empty list" [] (Phantoms.right (Phantoms.list ([] :: [TTerm Int])))]
where
mapFn = Phantoms.lambda "x" $
Logic.ifElse (Equality.equal (Phantoms.var "x") (Phantoms.int32 0))
(Phantoms.left (Phantoms.string "zero"))
(Phantoms.right (Math.mul (Phantoms.var "x") (Phantoms.int32 2)))
test name input expected = evalPair name showEitherStringIntList
(Eithers.mapList mapFn (Phantoms.list (Phantoms.int32 <$> input)))
expected
eithersMapMaybe :: TTerm TestGroup
eithersMapMaybe = subgroup "mapMaybe" [
test "just succeeds" (Phantoms.just (Phantoms.int32 5)) (Phantoms.right (Phantoms.just (Phantoms.int32 10))),
test "just fails" (Phantoms.just (Phantoms.int32 0)) (Phantoms.left (Phantoms.string "zero")),
test "nothing" (Phantoms.nothing :: TTerm (Maybe Int)) (Phantoms.right (Phantoms.nothing :: TTerm (Maybe Int)))]
where
mapFn = Phantoms.lambda "x" $
Logic.ifElse (Equality.equal (Phantoms.var "x") (Phantoms.int32 0))
(Phantoms.left (Phantoms.string "zero"))
(Phantoms.right (Math.mul (Phantoms.var "x") (Phantoms.int32 2)))
test name input expected = evalPair name showEitherStringMaybeInt
(Eithers.mapMaybe mapFn input)
expected
allTests :: TTermDefinition TestGroup
allTests = definitionInModule module_ "allTests" $
Phantoms.doc "Test cases for hydra.lib.eithers primitives" $
supergroup "hydra.lib.eithers primitives" [
eithersBind,
eithersBimap,
eithersIsLeft,
eithersIsRight,
eithersFromLeft,
eithersFromRight,
eithersEither,
eithersLefts,
eithersRights,
eithersPartitionEithers,
eithersMap,
eithersMapList,
eithersMapMaybe]