{-# OPTIONS_GHC -Wno-missing-methods #-} {-# LANGUAGE BlockArguments #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ViewPatterns #-} module Test.Regression.Issue7 where import Data.Function ((&)) import Data.Hashable (Hashable) import Data.Holmes import GHC.Generics (Generic) import Test.Tasty.Hspec (Spec, describe, it, shouldBe) ------------------------------------------------------------------------------- -- #7: stack overflow / infinite loop when solving with Intersect instead of -- Defined countEqual :: ( Eq (f x) , Lifting f c , Mapping f c , c x, c v , Num v, Num (f v), SumR (f v) , MonadCell m ) => [(Int, x)] -> [Prop m (f x)] -> Prop m (f v) countEqual values cells = foldr (.+) (lift 0) (map f values) where f (index, expected) = cells !! index & over \actual -> if lift' expected == actual then 1 else 0 spec_17_defined :: Spec spec_17_defined = describe "Issue #17" do it "Defined" do let checks :: [( Int, Int )] checks = [( 0, 3 )] threshold :: MonadCell m => Prop m (Defined Int) threshold = lift 0 example <- (1 `from` [ 1 ]) `satisfying` \cells -> do countEqual checks cells .>= threshold example `shouldBe` Just [ 1 ] it "Intersect" do let checks :: [( Int, Val4 )] checks = [( 0, 3 )] threshold :: MonadCell m => Prop m (Intersect Val4) threshold = lift 0 example <- (1 `from` [ 1 ]) `satisfying` \cells -> do countEqual checks cells .>= threshold example `shouldBe` Just [ 1 ] newtype Val4 = V4 Int deriving stock (Show, Eq, Ord, Generic) deriving anyclass (Hashable) instance Num Val4 where fromInteger = toEnum . fromInteger -- this is not a valid Num instance, we just want to use -- it for counting V4 a + V4 b = if a + b > 4 then V4 4 else V4 (a + b) V4 a - V4 b = if a - b < 0 then V4 0 else V4 (a - b) instance Enum Val4 where toEnum n | n < 0 || n > 4 = error $ "toEnum Val4 out of bounds: " ++ show n | otherwise = V4 n fromEnum v@(V4 m) | m < 0 || m > 4 = error $ "fromEnum Val4 out of bounds: " ++ show v | otherwise = m instance Bounded Val4 where minBound = toEnum 0 maxBound = toEnum 4