From c78ed4ac90feda1262cc02b17cd5d5f3ea7fa942 Mon Sep 17 00:00:00 2001 From: VegOwOtenks Date: Sun, 17 Aug 2025 18:19:00 +0200 Subject: [PATCH 1/3] feat[core]: Function Property: Argument modification --- package.yaml | 3 ++ scalie.cabal | 11 +++++++ src/Data/Enum/Util.hs | 5 +++ src/Language/Scalie/Domain/Type.hs | 10 +++++- src/Language/Scalie/Domain/Type.hs-boot | 9 ++++++ src/Language/Scalie/Domain/Type/Function.hs | 18 +++++++++++ .../Type/Function/ArgumentModification.hs | 26 +++++++++++++++ test/Spec.hs | 8 ++++- test/Test/Data/Map/Implicit.hs | 3 +- .../Type/Function/ArgumentModification.hs | 32 +++++++++++++++++++ test/Test/QuickCheck/Isomorphic.hs | 5 +++ test/Test/Tasty/TH.hs | 11 +++++++ 12 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 src/Data/Enum/Util.hs create mode 100644 src/Language/Scalie/Domain/Type.hs-boot create mode 100644 src/Language/Scalie/Domain/Type/Function.hs create mode 100644 src/Language/Scalie/Domain/Type/Function/ArgumentModification.hs create mode 100644 test/Test/Language/Scalie/Domain/Type/Function/ArgumentModification.hs create mode 100644 test/Test/QuickCheck/Isomorphic.hs create mode 100644 test/Test/Tasty/TH.hs diff --git a/package.yaml b/package.yaml index 70fb2cf..83d9fbc 100644 --- a/package.yaml +++ b/package.yaml @@ -29,6 +29,7 @@ default-extensions: dependencies: - base - containers + - leancheck - text - vector - QuickCheck @@ -64,3 +65,5 @@ tests: - scalie - tasty - tasty-quickcheck + - tasty-leancheck + - template-haskell diff --git a/scalie.cabal b/scalie.cabal index 5d04b75..b96a3b2 100644 --- a/scalie.cabal +++ b/scalie.cabal @@ -19,6 +19,7 @@ extra-source-files: library exposed-modules: + Data.Enum.Util Data.Map.Implicit Language.Scalie.Bytecode.Instruction Language.Scalie.Bytecode.Object @@ -39,6 +40,8 @@ library Language.Scalie.Core.Provenance Language.Scalie.Core.Provenance.SourceLocation Language.Scalie.Domain.Type + Language.Scalie.Domain.Type.Function + Language.Scalie.Domain.Type.Function.ArgumentModification other-modules: Paths_scalie hs-source-dirs: @@ -55,6 +58,7 @@ library QuickCheck , base , containers + , leancheck , text , vector default-language: Haskell2010 @@ -77,6 +81,7 @@ executable scalie-exe QuickCheck , base , containers + , leancheck , scalie , text , vector @@ -87,7 +92,10 @@ test-suite scalie-test main-is: Spec.hs other-modules: Test.Data.Map.Implicit + Test.Language.Scalie.Domain.Type.Function.ArgumentModification + Test.QuickCheck.Isomorphic Test.QuickCheck.Roundtrip + Test.Tasty.TH Paths_scalie hs-source-dirs: test @@ -103,9 +111,12 @@ test-suite scalie-test QuickCheck , base , containers + , leancheck , scalie , tasty + , tasty-leancheck , tasty-quickcheck + , template-haskell , text , vector default-language: Haskell2010 diff --git a/src/Data/Enum/Util.hs b/src/Data/Enum/Util.hs new file mode 100644 index 0000000..ddc3971 --- /dev/null +++ b/src/Data/Enum/Util.hs @@ -0,0 +1,5 @@ +{-# LANGUAGE Safe #-} +module Data.Enum.Util (enumerate) where + +enumerate :: (Enum a, Bounded a) => [a] +enumerate = [minBound .. maxBound] diff --git a/src/Language/Scalie/Domain/Type.hs b/src/Language/Scalie/Domain/Type.hs index 97354de..55fe978 100644 --- a/src/Language/Scalie/Domain/Type.hs +++ b/src/Language/Scalie/Domain/Type.hs @@ -4,17 +4,25 @@ module Language.Scalie.Domain.Type (Type(..)) where import Data.Kind qualified import Test.QuickCheck (Arbitrary (arbitrary), Gen, oneof) +import Language.Scalie.Domain.Type.Function qualified as Type -- | The type of an expression in Scalie, not to be confused with the 'Type' provided by GHC type Type :: Data.Kind.Type data Type = RawInt + | RawRational + | RawString + | Function Type.Function deriving stock (Show, Read, Eq) -- add to the arbitrary instance when defining constructors instance Arbitrary Type where arbitrary :: Gen Type - arbitrary = oneof [ pure RawInt ] + arbitrary = oneof + [ pure RawInt + , pure RawRational + , pure RawString + ] diff --git a/src/Language/Scalie/Domain/Type.hs-boot b/src/Language/Scalie/Domain/Type.hs-boot new file mode 100644 index 0000000..20897d8 --- /dev/null +++ b/src/Language/Scalie/Domain/Type.hs-boot @@ -0,0 +1,9 @@ +{-# LANGUAGE Safe #-} +module Language.Scalie.Domain.Type (Type) where +import Data.Kind qualified + +type Type :: Data.Kind.Type +data Type +instance Show Type +instance Read Type +instance Eq Type diff --git a/src/Language/Scalie/Domain/Type/Function.hs b/src/Language/Scalie/Domain/Type/Function.hs new file mode 100644 index 0000000..233aea1 --- /dev/null +++ b/src/Language/Scalie/Domain/Type/Function.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE Safe #-} +module Language.Scalie.Domain.Type.Function (Function(..)) where + +-- meta +import Data.Kind qualified + +-- scalie +import Language.Scalie.Domain.Type.Function.ArgumentModification (ArgumentModification) + +import {-# SOURCE #-} Language.Scalie.Domain.Type qualified as Scalie.Domain + +type Function :: Data.Kind.Type +data Function = Function + { argument :: Scalie.Domain.Type + , result :: Scalie.Domain.Type + , argumentModification :: ArgumentModification + } + deriving stock (Show, Read, Eq) diff --git a/src/Language/Scalie/Domain/Type/Function/ArgumentModification.hs b/src/Language/Scalie/Domain/Type/Function/ArgumentModification.hs new file mode 100644 index 0000000..5deb58d --- /dev/null +++ b/src/Language/Scalie/Domain/Type/Function/ArgumentModification.hs @@ -0,0 +1,26 @@ +{-# LANGUAGE Trustworthy #-} -- leancheck import somehow is not safe +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE InstanceSigs #-} +module Language.Scalie.Domain.Type.Function.ArgumentModification (ArgumentModification(..), toBool, fromBool) where +import Data.Kind qualified +import Test.LeanCheck (Listable (list)) +import Data.Enum.Util (enumerate) + +type ArgumentModification :: Data.Kind.Type +data ArgumentModification + = PureArguments + | LinearArguments + deriving stock (Show, Read, Eq, Ord, Enum, Bounded) + +toBool :: ArgumentModification -> Bool +toBool = \case + PureArguments -> False + LinearArguments -> True + +fromBool :: Bool -> ArgumentModification +fromBool = toEnum . fromEnum + +instance Listable ArgumentModification where + list :: [ArgumentModification] + list = enumerate + diff --git a/test/Spec.hs b/test/Spec.hs index 58d3a63..6ad4910 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1,10 +1,16 @@ {-# LANGUAGE Unsafe #-} -- unsafe: Uses functions imported from unsafe modules -import Test.Data.Map.Implicit qualified + +-- library import Test.Tasty qualified as Tasty +-- test groups +import Test.Data.Map.Implicit qualified +import Test.Language.Scalie.Domain.Type.Function.ArgumentModification qualified + main :: IO () main = Tasty.defaultMain $ Tasty.testGroup "all" [ Tasty.testGroup "Properties" [ Test.Data.Map.Implicit.testGroup + , Test.Language.Scalie.Domain.Type.Function.ArgumentModification.testGroup ] ] diff --git a/test/Test/Data/Map/Implicit.hs b/test/Test/Data/Map/Implicit.hs index 44bbb1f..a252137 100644 --- a/test/Test/Data/Map/Implicit.hs +++ b/test/Test/Data/Map/Implicit.hs @@ -18,6 +18,7 @@ import Test.Tasty.QuickCheck (QuickCheckMaxSize(QuickCheckMaxSize)) import Test.Tasty qualified as Tasty import Test.Tasty.QuickCheck qualified as Tasty.QuickCheck +import Test.Tasty.TH (moduleName) -- | This is testworthy since I have somehow hand-hacked the read/show de/serialization of 'ImplicitMap' @@ -33,4 +34,4 @@ allTests = $allProperties testGroup :: TestTree testGroup = Tasty.localOption (QuickCheckMaxSize 25) -- it is necessary to restrain the size because the generated core would get veeeeeery big otherwise - $ Tasty.QuickCheck.testProperties "Data.Map.Implicit" allTests + $ Tasty.QuickCheck.testProperties $moduleName allTests diff --git a/test/Test/Language/Scalie/Domain/Type/Function/ArgumentModification.hs b/test/Test/Language/Scalie/Domain/Type/Function/ArgumentModification.hs new file mode 100644 index 0000000..573ecf8 --- /dev/null +++ b/test/Test/Language/Scalie/Domain/Type/Function/ArgumentModification.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE Trustworthy #-} +{-# LANGUAGE TemplateHaskell #-} +module Test.Language.Scalie.Domain.Type.Function.ArgumentModification (testGroup) where + +import Language.Scalie.Domain.Type.Function.ArgumentModification (ArgumentModification, toBool, fromBool) + +-- library: QuickCheck +import Test.QuickCheck.Roundtrip (roundtrips) +import Test.QuickCheck.Isomorphic (isomorphic) + +-- library: Tasty +import Test.Tasty (TestTree) +import Test.Tasty.TH (moduleName) + +import Test.Tasty qualified as Tasty +import Test.Tasty.LeanCheck qualified as Tasty.LeanCheck + +prop_toFromBoolRoundtrips :: Bool -> Bool +prop_toFromBoolRoundtrips = roundtrips toBool fromBool + +prop_IsoToBoolEnumConversion :: ArgumentModification -> Bool +prop_IsoToBoolEnumConversion = isomorphic toBool (toEnum . fromEnum) + +prop_IsoFromBoolEnumConversion :: Bool -> Bool +prop_IsoFromBoolEnumConversion = isomorphic fromBool (toEnum . fromEnum) + +testGroup :: TestTree +testGroup = Tasty.testGroup $moduleName + [ Tasty.LeanCheck.testProperty "id ≡ toBool . fromBool" prop_toFromBoolRoundtrips + , Tasty.LeanCheck.testProperty "toBool ≡ toEnum . fromEnum" prop_IsoToBoolEnumConversion + , Tasty.LeanCheck.testProperty "fromBool ≡ toEnum . fromEnum" prop_IsoFromBoolEnumConversion + ] diff --git a/test/Test/QuickCheck/Isomorphic.hs b/test/Test/QuickCheck/Isomorphic.hs new file mode 100644 index 0000000..dd47ee9 --- /dev/null +++ b/test/Test/QuickCheck/Isomorphic.hs @@ -0,0 +1,5 @@ +{-# LANGUAGE Safe #-} +module Test.QuickCheck.Isomorphic (isomorphic) where + +isomorphic :: Eq a => (t -> a) -> (t -> a) -> t -> Bool +isomorphic f g x = f x == g x diff --git a/test/Test/Tasty/TH.hs b/test/Test/Tasty/TH.hs new file mode 100644 index 0000000..b1f9e93 --- /dev/null +++ b/test/Test/Tasty/TH.hs @@ -0,0 +1,11 @@ +{-# LANGUAGE Safe #-} -- does template haskell, but no IO inside it +module Test.Tasty.TH (moduleName) where + +import Language.Haskell.TH.Lib (thisModule) +import Language.Haskell.TH.Syntax (Q, Exp(LitE), ModName(ModName), Lit(StringL), Module (Module)) + +moduleName :: Q Exp +moduleName = do + Module _ (ModName name) <- thisModule + pure . LitE . StringL $ name + From c87cbb54056cd15ca014be3506757b2ea47c92d1 Mon Sep 17 00:00:00 2001 From: VegOwOtenks Date: Sun, 17 Aug 2025 18:29:16 +0200 Subject: [PATCH 2/3] feat[stack]: dependency juggling --- package.yaml | 9 +++++---- scalie.cabal | 8 -------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/package.yaml b/package.yaml index 83d9fbc..91f40e0 100644 --- a/package.yaml +++ b/package.yaml @@ -28,10 +28,6 @@ default-extensions: dependencies: - base - - containers - - leancheck - - text - - vector - QuickCheck ghc-options: @@ -41,6 +37,11 @@ ghc-options: library: source-dirs: src + dependencies: + - containers + - leancheck + - text + - vector executables: scalie-exe: diff --git a/scalie.cabal b/scalie.cabal index b96a3b2..6764e9c 100644 --- a/scalie.cabal +++ b/scalie.cabal @@ -80,11 +80,7 @@ executable scalie-exe build-depends: QuickCheck , base - , containers - , leancheck , scalie - , text - , vector default-language: Haskell2010 test-suite scalie-test @@ -110,13 +106,9 @@ test-suite scalie-test build-depends: QuickCheck , base - , containers - , leancheck , scalie , tasty , tasty-leancheck , tasty-quickcheck , template-haskell - , text - , vector default-language: Haskell2010 From 5ccb477a50f6f098add11ddbb8d46c0e1e26dd90 Mon Sep 17 00:00:00 2001 From: VegOwOtenks Date: Mon, 18 Aug 2025 20:09:21 +0200 Subject: [PATCH 3/3] fix[test]: Don't test read/show instances for more than a second --- test/Test/Data/Map/Implicit.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Test/Data/Map/Implicit.hs b/test/Test/Data/Map/Implicit.hs index a252137..4f38074 100644 --- a/test/Test/Data/Map/Implicit.hs +++ b/test/Test/Data/Map/Implicit.hs @@ -33,5 +33,6 @@ allTests :: [(String, Property)] allTests = $allProperties testGroup :: TestTree -testGroup = Tasty.localOption (QuickCheckMaxSize 25) -- it is necessary to restrain the size because the generated core would get veeeeeery big otherwise +testGroup = Tasty.localOption (QuickCheckMaxSize 5) -- anything above 25 doesn't finish in reasonable time on my laptop $ Tasty.QuickCheck.testProperties $moduleName allTests + -- I don't want to stress-tess or bench-mark it, just prove it works cause I hacked a little around it.