{-# LANGUAGE BlockArguments #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} module Main where import Data.Monoid (Any(..)) import Data.Override (Override(..), As, At) import Encode (Encode(encode), ListOf(..)) import GHC.Generics (Generic) import Test.Hspec main :: IO () main = hspec do describe "Override" do describe "Semigroup" do it "Rec1" testRec1'Semigroup describe "Monoid" do it "Rec1" testRec1'Monoid describe "Eq" do it "Rec2" testRec2'Eq it "Sum1" testSum1'Eq describe "Ord" do it "Rec2" testRec2'Ord describe "Encode" do it "Sum1" testSum1'Encode it "Sum2" testSum2'Encode it "Sum3" testSum3'Encode it "Sum4" testSum4'Encode it "Sum5" testSum5'Encode -- | Overriding instances by type. data Rec1 = Rec1 { foo :: String , bar :: Bool } deriving stock (Show, Eq, Generic) deriving (Semigroup, Monoid) via Override Rec1 '[ Bool `As` Any ] testRec1'Semigroup :: IO () testRec1'Semigroup = do Rec1 { foo = "a", bar = False } <> Rec1 { foo = "b", bar = True } `shouldBe` Rec1 { foo = "ab", bar = True } testRec1'Monoid :: IO () testRec1'Monoid = do mempty `shouldBe` Rec1 { foo = "", bar = False } Rec1 { foo = "a", bar = False } `mappend` Rec1 { foo = "b", bar = True } `shouldBe` Rec1 { foo = "ab", bar = True } data Rec2 = Rec2 String Int deriving stock (Show, Generic) deriving (Eq, Ord) via (Override Rec2 '[String `As` ByLength]) newtype ByLength = ByLength { byLength :: String } deriving (Show) instance Eq ByLength where ByLength x == ByLength y = length x == length y instance Ord ByLength where ByLength x <= ByLength y = length x <= length y testRec2'Eq :: IO () testRec2'Eq = do (Rec2 "foo" 0 == Rec2 "bar" 0) `shouldBe` True (Rec2 "foo" 0 == Rec2 "bar" 1) `shouldBe` False (Rec2 "" 0 == Rec2 "bar" 0) `shouldBe` False testRec2'Ord :: IO () testRec2'Ord = do (Rec2 "foo" 0 <= Rec2 "bar" 0) `shouldBe` True (Rec2 "foo" 1 <= Rec2 "bar" 0) `shouldBe` False (Rec2 "" 0 >= Rec2 "bar" 0) `shouldBe` False -- | Override support for sum types. data Sum1 = Sum1String String | Sum1Int Int deriving stock (Show, Generic) deriving (Eq) via (Override Sum1 '[String `As` ByLength]) deriving (Encode) via (Override Sum1 '[String `As` ListOf Char]) testSum1'Eq :: IO () testSum1'Eq = do (Sum1String "foo" == Sum1String "bar") `shouldBe` True (Sum1Int 3 == Sum1Int 3) `shouldBe` True (Sum1String "foo" == Sum1String "ba") `shouldBe` False (Sum1String "foo" == Sum1Int 3) `shouldBe` False (Sum1Int 3 == Sum1Int 2) `shouldBe` False testSum1'Encode :: IO () testSum1'Encode = do encode (Sum1String "foo") `shouldBe` "Sum1String:f,o,o" encode (Sum1Int 1) `shouldBe` "Sum1Int:1" -- | Override using 'As' which includes a type variable. data Sum2 a = Sum2List [a] | Sum2Null deriving stock (Show, Eq, Generic) deriving (Encode) via (Override (Sum2 a) '[[a] `As` ListOf a]) testSum2'Encode :: IO () testSum2'Encode = do encode (Sum2List [1, 2, 3 :: Int]) `shouldBe` "Sum2List:1,2,3" encode (Sum2Null @Int) `shouldBe` "Sum2Null:" -- | Override using 'As' which uses a kind of @* -> *@. Convenient -- so you don't have to apply the type parameter. data Sum3 a = Sum3List [a] | Sum3Null deriving stock (Show, Eq, Generic) deriving (Encode) via (Override (Sum3 a) '[[] `As` ListOf]) testSum3'Encode :: IO () testSum3'Encode = do encode (Sum3List [1, 2, 3 :: Int]) `shouldBe` "Sum3List:1,2,3" encode (Sum3Null @Int) `shouldBe` "Sum3Null:" -- | Override using 'At' which includes a type variable. data Sum4 a = Sum4List [a] | Sum4String String deriving stock (Show, Eq, Generic) deriving (Encode) via Override (Sum4 a) '[ At "Sum4List" 0 (ListOf a) ] testSum4'Encode :: IO () testSum4'Encode = do encode (Sum4List [1, 2, 3 :: Int]) `shouldBe` "Sum4List:1,2,3" encode (Sum4List "foo") `shouldBe` "Sum4List:f,o,o" encode (Sum4String @Char "foo") `shouldBe` "Sum4String:foo" -- | Override using 'At' which uses a kind of @* -> *@. data Sum5 a = Sum5String String | Sum5List Char [a] deriving stock (Show, Eq, Generic) deriving (Encode) via Override (Sum5 a) '[ At "Sum5List" 1 (ListOf a) ] testSum5'Encode :: IO () testSum5'Encode = do encode (Sum5List 'a' [1, 2, 3 :: Int]) `shouldBe` "Sum5List:a,1,2,3" encode (Sum5List 'a' "foo") `shouldBe` "Sum5List:a,f,o,o" encode (Sum5String @Char "foo") `shouldBe` "Sum5String:foo"