From e8e79919ec2443c68e90717cf7f5a787b0039387 Mon Sep 17 00:00:00 2001 From: Amiri Barksdale at Home Date: Wed, 4 Feb 2026 15:42:02 -0800 Subject: [PATCH 1/5] An attempt at using monadicIO --- dataframe.cabal | 3 ++- src/DataFrame/Monad.hs | 24 ++++++++++++------------ tests/GenDataFrame.hs | 16 ++++++++-------- tests/Main.hs | 30 ++++++++++++++++-------------- tests/Monad.hs | 20 ++++++++++++++++++++ 5 files changed, 58 insertions(+), 35 deletions(-) create mode 100644 tests/Monad.hs diff --git a/dataframe.cabal b/dataframe.cabal index 631b3671..69f48384 100644 --- a/dataframe.cabal +++ b/dataframe.cabal @@ -201,7 +201,8 @@ test-suite tests Operations.Subset, Operations.Statistics, Operations.Take, - Parquet + Parquet, + Monad build-depends: base >= 4 && < 5, dataframe ^>= 0.4, directory >= 1.3.0.0 && < 2, diff --git a/src/DataFrame/Monad.hs b/src/DataFrame/Monad.hs index 48ad84b7..a98e79a6 100644 --- a/src/DataFrame/Monad.hs +++ b/src/DataFrame/Monad.hs @@ -1,20 +1,20 @@ -{-# LANGUAGE ExplicitNamespaces #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE InstanceSigs #-} -{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ExplicitNamespaces #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TupleSections #-} module DataFrame.Monad where -import DataFrame (DataFrame) -import qualified DataFrame as D -import DataFrame.Internal.Column (Columnable) -import DataFrame.Internal.Expression (Expr (..)) +import DataFrame (DataFrame) +import qualified DataFrame as D +import DataFrame.Internal.Column (Columnable) +import DataFrame.Internal.Expression (Expr (..)) -import qualified Data.Text as T -import System.Random +import qualified Data.Text as T +import System.Random -- A re-implementation of the state monad. -- `mtl` might be too heavy a dependency just to get diff --git a/tests/GenDataFrame.hs b/tests/GenDataFrame.hs index b7d9250d..21f9084c 100644 --- a/tests/GenDataFrame.hs +++ b/tests/GenDataFrame.hs @@ -1,15 +1,15 @@ -{-# LANGUAGE GADTs #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE TypeApplications #-} module GenDataFrame where -import qualified Data.Map as M -import qualified Data.Text as T -import qualified Data.Vector as V -import qualified Data.Vector.Unboxed as VU -import DataFrame.Internal.Column -import DataFrame.Internal.DataFrame -import Test.QuickCheck +import qualified Data.Map as M +import qualified Data.Text as T +import qualified Data.Vector as V +import qualified Data.Vector.Unboxed as VU +import DataFrame.Internal.Column +import DataFrame.Internal.DataFrame +import Test.QuickCheck genColumn :: Int -> Gen Column genColumn len = diff --git a/tests/Main.hs b/tests/Main.hs index 1455ffd1..d1eabbdf 100644 --- a/tests/Main.hs +++ b/tests/Main.hs @@ -1,23 +1,24 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} module Main where -import qualified Data.Text as T -import qualified Data.Vector as V -import qualified Data.Vector.Unboxed as VU -import qualified DataFrame as D -import qualified DataFrame.Internal.Column as DI +import qualified Data.Text as T +import qualified Data.Vector as V +import qualified Data.Vector.Unboxed as VU +import qualified DataFrame as D +import qualified DataFrame.Internal.Column as DI import qualified DataFrame.Operations.Typing as D -import qualified System.Exit as Exit +import qualified System.Exit as Exit -import Data.Time -import GenDataFrame () -import Test.HUnit -import Test.QuickCheck +import Data.Time +import GenDataFrame () +import Test.HUnit +import Test.QuickCheck import qualified Functions +import qualified Monad import qualified Operations.Aggregations import qualified Operations.Apply import qualified Operations.Core @@ -5128,7 +5129,7 @@ tests = isSuccessful :: Result -> Bool isSuccessful (Success{..}) = True -isSuccessful _ = False +isSuccessful _ = False main :: IO () main = do @@ -5138,6 +5139,7 @@ main = do mapM (quickCheckWithResult stdArgs) Operations.Subset.tests - if failures result > 0 || errors result > 0 || not (all isSuccessful propRes) + monadRes <- mapM (quickCheckWithResult stdArgs) Monad.tests + if failures result > 0 || errors result > 0 || not (all isSuccessful propRes) || not (all isSuccessful monadRes) then Exit.exitFailure else Exit.exitSuccess diff --git a/tests/Monad.hs b/tests/Monad.hs new file mode 100644 index 00000000..09ee81dd --- /dev/null +++ b/tests/Monad.hs @@ -0,0 +1,20 @@ +module Monad where + +import qualified DataFrame as D +import DataFrame.Internal.DataFrame +import DataFrame.Monad +import GenDataFrame () +import System.Random +import Test.QuickCheck +import Test.QuickCheck.Monadic + +prop_sampleM :: DataFrame -> Property +prop_sampleM df = monadic $ do + let rowCount = D.nRows df + pre (rowCount >= 10) + let finalDf = execFrameM df (sampleM (mkStdGen 142) 0.1) + let finalRowCount = D.nRows finalDf + assert (rowCount > finalRowCount) + + +tests = [prop_sampleM] From 62030e893d71f2c7605df98a7e9678c431d2958b Mon Sep 17 00:00:00 2001 From: Amiri Barksdale Date: Thu, 19 Feb 2026 17:00:47 -0800 Subject: [PATCH 2/5] Strange behavior with D.nRows --- .gitignore | 1 + tests/GenDataFrame.hs | 2 +- tests/Monad.hs | 42 ++++++++++++++++++++++++++++++------------ 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index e6876c8e..8adf10fc 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ bin/ coverage-html .DS_Store flake.lock +tags diff --git a/tests/GenDataFrame.hs b/tests/GenDataFrame.hs index 21f9084c..b013eaa1 100644 --- a/tests/GenDataFrame.hs +++ b/tests/GenDataFrame.hs @@ -21,7 +21,7 @@ genColumn len = genDataFrame :: Gen DataFrame genDataFrame = do - numRows <- choose (0, 100) + numRows <- choose (100, 1000) numCols <- choose (0, 10) colNames <- V.fromList <$> vectorOf numCols genUniqueColName cols <- V.fromList <$> vectorOf numCols (genColumn numRows) diff --git a/tests/Monad.hs b/tests/Monad.hs index 09ee81dd..0c96a34a 100644 --- a/tests/Monad.hs +++ b/tests/Monad.hs @@ -1,20 +1,38 @@ module Monad where -import qualified DataFrame as D -import DataFrame.Internal.DataFrame -import DataFrame.Monad -import GenDataFrame () -import System.Random -import Test.QuickCheck -import Test.QuickCheck.Monadic +import qualified DataFrame as D +import DataFrame.Internal.DataFrame +import DataFrame.Monad +import Debug.Trace +import GenDataFrame () +import System.Random +import Test.QuickCheck +import Test.QuickCheck.Monadic + +roundToTwoPlaces x = fromIntegral (round (x * 100)) / 100.0 prop_sampleM :: DataFrame -> Property -prop_sampleM df = monadic $ do +prop_sampleM df = monadicIO $ do + p <- run $ generate (choose (0.0 :: Double, 1.0 :: Double)) + let proportion = roundToTwoPlaces p + seed <- run $ generate (choose (0, 1000)) let rowCount = D.nRows df - pre (rowCount >= 10) - let finalDf = execFrameM df (sampleM (mkStdGen 142) 0.1) + pre (rowCount > 100 && proportion > 0.1 && proportion < 0.8) + traceM $ "Rows in initialDf: " ++ show rowCount + -- traceM $ show df + let finalDf = execFrameM df (sampleM (mkStdGen seed) proportion) let finalRowCount = D.nRows finalDf - assert (rowCount > finalRowCount) - + let realProportion = roundToTwoPlaces $ fromIntegral finalRowCount / fromIntegral rowCount + traceM $ + "Rows in finalDf: " + ++ show finalRowCount + ++ "; proportion is " + ++ show realProportion + ++ " where " + ++ show proportion + ++ " was expected" + let diff = abs $ proportion - realProportion + traceM $ "Diff is " ++ show diff + assert (diff <= 0.1) tests = [prop_sampleM] From 4184ad7edcc7004af5661202687d2ae841c31d3a Mon Sep 17 00:00:00 2001 From: Amiri Barksdale Date: Thu, 19 Feb 2026 17:16:48 -0800 Subject: [PATCH 3/5] Change proportion to rate --- tests/Monad.hs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/Monad.hs b/tests/Monad.hs index 0c96a34a..15b6f3ec 100644 --- a/tests/Monad.hs +++ b/tests/Monad.hs @@ -14,24 +14,24 @@ roundToTwoPlaces x = fromIntegral (round (x * 100)) / 100.0 prop_sampleM :: DataFrame -> Property prop_sampleM df = monadicIO $ do p <- run $ generate (choose (0.0 :: Double, 1.0 :: Double)) - let proportion = roundToTwoPlaces p + let expectedRate = roundToTwoPlaces p seed <- run $ generate (choose (0, 1000)) let rowCount = D.nRows df - pre (rowCount > 100 && proportion > 0.1 && proportion < 0.8) + pre (rowCount > 100 && expectedRate > 0.1 && expectedRate < 0.8) traceM $ "Rows in initialDf: " ++ show rowCount -- traceM $ show df - let finalDf = execFrameM df (sampleM (mkStdGen seed) proportion) + let finalDf = execFrameM df (sampleM (mkStdGen seed) expectedRate) let finalRowCount = D.nRows finalDf - let realProportion = roundToTwoPlaces $ fromIntegral finalRowCount / fromIntegral rowCount + let realRate = roundToTwoPlaces $ fromIntegral finalRowCount / fromIntegral rowCount traceM $ "Rows in finalDf: " ++ show finalRowCount - ++ "; proportion is " - ++ show realProportion + ++ "; expectedRate is " + ++ show realRate ++ " where " - ++ show proportion + ++ show expectedRate ++ " was expected" - let diff = abs $ proportion - realProportion + let diff = abs $ expectedRate - realRate traceM $ "Diff is " ++ show diff assert (diff <= 0.1) From 84be9760c35f4ba96228cd146e6e1ae52dac0047 Mon Sep 17 00:00:00 2001 From: Amiri Barksdale at Home Date: Fri, 20 Feb 2026 08:10:58 -0800 Subject: [PATCH 4/5] Script run, cleanup --- src/DataFrame/Monad.hs | 24 ++++++++++++------------ tests/GenDataFrame.hs | 16 ++++++++-------- tests/Main.hs | 5 ++++- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/DataFrame/Monad.hs b/src/DataFrame/Monad.hs index a98e79a6..48ad84b7 100644 --- a/src/DataFrame/Monad.hs +++ b/src/DataFrame/Monad.hs @@ -1,20 +1,20 @@ -{-# LANGUAGE ExplicitNamespaces #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE InstanceSigs #-} -{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ExplicitNamespaces #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TupleSections #-} module DataFrame.Monad where -import DataFrame (DataFrame) -import qualified DataFrame as D -import DataFrame.Internal.Column (Columnable) -import DataFrame.Internal.Expression (Expr (..)) +import DataFrame (DataFrame) +import qualified DataFrame as D +import DataFrame.Internal.Column (Columnable) +import DataFrame.Internal.Expression (Expr (..)) -import qualified Data.Text as T -import System.Random +import qualified Data.Text as T +import System.Random -- A re-implementation of the state monad. -- `mtl` might be too heavy a dependency just to get diff --git a/tests/GenDataFrame.hs b/tests/GenDataFrame.hs index b013eaa1..15321c67 100644 --- a/tests/GenDataFrame.hs +++ b/tests/GenDataFrame.hs @@ -1,15 +1,15 @@ -{-# LANGUAGE GADTs #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE TypeApplications #-} module GenDataFrame where -import qualified Data.Map as M -import qualified Data.Text as T -import qualified Data.Vector as V -import qualified Data.Vector.Unboxed as VU -import DataFrame.Internal.Column -import DataFrame.Internal.DataFrame -import Test.QuickCheck +import qualified Data.Map as M +import qualified Data.Text as T +import qualified Data.Vector as V +import qualified Data.Vector.Unboxed as VU +import DataFrame.Internal.Column +import DataFrame.Internal.DataFrame +import Test.QuickCheck genColumn :: Int -> Gen Column genColumn len = diff --git a/tests/Main.hs b/tests/Main.hs index d1eabbdf..08379cc9 100644 --- a/tests/Main.hs +++ b/tests/Main.hs @@ -5140,6 +5140,9 @@ main = do (quickCheckWithResult stdArgs) Operations.Subset.tests monadRes <- mapM (quickCheckWithResult stdArgs) Monad.tests - if failures result > 0 || errors result > 0 || not (all isSuccessful propRes) || not (all isSuccessful monadRes) + if failures result > 0 + || errors result > 0 + || not (all isSuccessful propRes) + || not (all isSuccessful monadRes) then Exit.exitFailure else Exit.exitSuccess From 93a235d5109900fc40daa6a022fd269f87124832 Mon Sep 17 00:00:00 2001 From: Amiri Barksdale Date: Sat, 21 Feb 2026 14:31:07 -0800 Subject: [PATCH 5/5] Fix test, run presubmit and lint scripts --- tests/GenDataFrame.hs | 2 +- tests/Main.hs | 26 +++++++++++++------------- tests/Monad.hs | 25 +++++++------------------ 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/tests/GenDataFrame.hs b/tests/GenDataFrame.hs index 15321c67..c4d63ac7 100644 --- a/tests/GenDataFrame.hs +++ b/tests/GenDataFrame.hs @@ -22,7 +22,7 @@ genColumn len = genDataFrame :: Gen DataFrame genDataFrame = do numRows <- choose (100, 1000) - numCols <- choose (0, 10) + numCols <- choose (1, 10) colNames <- V.fromList <$> vectorOf numCols genUniqueColName cols <- V.fromList <$> vectorOf numCols (genColumn numRows) let indices = M.fromList $ zip (V.toList colNames) [0 ..] diff --git a/tests/Main.hs b/tests/Main.hs index 08379cc9..37820ed2 100644 --- a/tests/Main.hs +++ b/tests/Main.hs @@ -1,21 +1,21 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} module Main where -import qualified Data.Text as T -import qualified Data.Vector as V -import qualified Data.Vector.Unboxed as VU -import qualified DataFrame as D -import qualified DataFrame.Internal.Column as DI +import qualified Data.Text as T +import qualified Data.Vector as V +import qualified Data.Vector.Unboxed as VU +import qualified DataFrame as D +import qualified DataFrame.Internal.Column as DI import qualified DataFrame.Operations.Typing as D -import qualified System.Exit as Exit +import qualified System.Exit as Exit -import Data.Time -import GenDataFrame () -import Test.HUnit -import Test.QuickCheck +import Data.Time +import GenDataFrame () +import Test.HUnit +import Test.QuickCheck import qualified Functions import qualified Monad @@ -5129,7 +5129,7 @@ tests = isSuccessful :: Result -> Bool isSuccessful (Success{..}) = True -isSuccessful _ = False +isSuccessful _ = False main :: IO () main = do diff --git a/tests/Monad.hs b/tests/Monad.hs index 15b6f3ec..03e93902 100644 --- a/tests/Monad.hs +++ b/tests/Monad.hs @@ -3,7 +3,6 @@ module Monad where import qualified DataFrame as D import DataFrame.Internal.DataFrame import DataFrame.Monad -import Debug.Trace import GenDataFrame () import System.Random import Test.QuickCheck @@ -11,28 +10,18 @@ import Test.QuickCheck.Monadic roundToTwoPlaces x = fromIntegral (round (x * 100)) / 100.0 -prop_sampleM :: DataFrame -> Property -prop_sampleM df = monadicIO $ do - p <- run $ generate (choose (0.0 :: Double, 1.0 :: Double)) +prop_sampleM :: DataFrame -> Gen (Gen Property) +prop_sampleM df = monadic' $ do + p <- run $ choose (0.0 :: Double, 1.0 :: Double) let expectedRate = roundToTwoPlaces p - seed <- run $ generate (choose (0, 1000)) + seed <- run $ choose (0, 1000) let rowCount = D.nRows df - pre (rowCount > 100 && expectedRate > 0.1 && expectedRate < 0.8) - traceM $ "Rows in initialDf: " ++ show rowCount - -- traceM $ show df + let colCount = D.nColumns df + pre (colCount > 1 && rowCount > 100) let finalDf = execFrameM df (sampleM (mkStdGen seed) expectedRate) let finalRowCount = D.nRows finalDf let realRate = roundToTwoPlaces $ fromIntegral finalRowCount / fromIntegral rowCount - traceM $ - "Rows in finalDf: " - ++ show finalRowCount - ++ "; expectedRate is " - ++ show realRate - ++ " where " - ++ show expectedRate - ++ " was expected" let diff = abs $ expectedRate - realRate - traceM $ "Diff is " ++ show diff - assert (diff <= 0.1) + assert (diff <= 0.11) tests = [prop_sampleM]