Skip to content

Add rhine-cassava #226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ outputs = { self, nixpkgs, flake-utils, haskell-flake-utils, flake-compat, ... }
};

name = "rhine";
packageNames = [ "rhine-gloss" "rhine-terminal" "rhine-examples" "rhine-bayes" ];
packageNames = [ "rhine-gloss" "rhine-terminal" "rhine-examples" "rhine-bayes" "rhine-cassava" ];
};
}
2 changes: 1 addition & 1 deletion rhine-bayes/rhine-bayes.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ library
Data.MonadicStreamFunction.Bayes
build-depends: base >= 4.11 && < 4.18
, transformers >= 0.5
, rhine == 1.0
, rhine ^>= 1.0
, dunai ^>= 0.9
, log-domain >= 0.12
, monad-bayes ^>= 1.1
Expand Down
5 changes: 5 additions & 0 deletions rhine-cassava/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Revision history for rhine-cassava

## 1.0.1

* Added `CsvClock`
20 changes: 20 additions & 0 deletions rhine-cassava/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Copyright (c) 2023 Manuel Bärenz

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
136 changes: 136 additions & 0 deletions rhine-cassava/rhine-cassava.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
cabal-version: 3.0
-- The cabal-version field refers to the version of the .cabal specification,
-- and can be different from the cabal-install (the tool) version and the
-- Cabal (the library) version you are using. As such, the Cabal (the library)
-- version used must be equal or greater than the version stated in this field.
-- Starting from the specification version 2.2, the cabal-version field must be
-- the first thing in the cabal file.

-- Initial package description 'rhine-cassava' generated by
-- 'cabal init'. For further documentation, see:
-- http://haskell.org/cabal/users-guide/
--
-- The name of the package.
name: rhine-cassava

-- The package version.
-- See the Haskell package versioning policy (PVP) for standards
-- guiding when and how versions should be incremented.
-- https://pvp.haskell.org
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 1.0.1

-- A short (one-line) description of the package.
-- synopsis:

-- A longer description of the package.
-- description:

-- The license under which the package is released.
license: MIT

-- The file containing the license text.
license-file: LICENSE

-- The package author(s).
author: Manuel Bärenz

-- An email address to which users can send suggestions, bug reports, and patches.
maintainer: [email protected]

-- A copyright notice.
-- copyright:
category: FRP
build-type: Simple

-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README.
extra-doc-files: CHANGELOG.md

-- Extra source files to be distributed with the package, such as examples, or a tutorial module.
-- extra-source-files:

common warnings
ghc-options: -Wall
if flag(dev)
ghc-options: -Werror

flag dev
description: Enable warnings as errors. Active on ci.
default: False
manual: True

library
-- Import common warning flags.
import: warnings

-- Modules exported by the library.
exposed-modules: FRP.Rhine.Clock.Csv

-- Modules included in this library but not exported.
-- other-modules:

-- LANGUAGE extensions used by modules in this package.
-- other-extensions:

-- Other library packages from which modules are imported.
build-depends: base >= 4.11 && < 4.18
, rhine ^>= 1.0.1
, transformers >= 0.5
, cassava ^>= 0.5.3
, bytestring ^>= 0.11
, vector ^>= 0.12

-- Directories containing source files.
hs-source-dirs: src

-- Base language which the package is written in.
default-language: Haskell2010

default-extensions:
FlexibleInstances
MultiParamTypeClasses
NamedFieldPuns
TypeFamilies

test-suite rhine-cassava-test
-- Import common warning flags.
import: warnings

-- Base language which the package is written in.
default-language: Haskell2010

-- Modules included in this executable, other than Main.
-- other-modules:

-- LANGUAGE extensions used by modules in this package.
-- other-extensions:

-- The interface type and version of the test suite.
type: exitcode-stdio-1.0

-- Directories containing source files.
hs-source-dirs: test

-- The entrypoint to the test suite.
main-is: Main.hs

-- Test dependencies.
build-depends:
base >= 4.11 && < 4.18
, hspec ^>= 2.11
, transformers
, bytestring
, vector
, directory
, cassava
, rhine
, rhine-cassava

default-extensions:
Arrows
ImportQualifiedPost
OverloadedLists
OverloadedStrings
TypeApplications
45 changes: 45 additions & 0 deletions rhine-cassava/src/FRP/Rhine/Clock/Csv.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module FRP.Rhine.Clock.Csv (
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a better hierarchy would be:

FRP.Rhine.Csv.Clock -- this module
FRP.Rhine.Csv -- Csv utils like CSV writer ClSF, and clock reexport

CsvClock,
csvClock,
)
where

-- transformers
import Control.Monad.Trans.Except

-- bytestring
import Data.ByteString

-- vector
import Data.Vector

-- cassava
import Data.Csv

-- rhine
import FRP.Rhine (GetClockProxy)
import FRP.Rhine.Clock
import FRP.Rhine.Clock.File

{- | Similar to 'File', but ticks at every line of a CSV file,
and returns a parsed record.

Additionally to an end-of-file error, it can also return a parse error as a 'String'.
-}
newtype CsvClock = CsvClock (File String (Vector Record))
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming: Better call it Csv?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit inconsistent in the code base. I think I've sometimes called something e.g. SelectClock to avoid clashing with Select (an obscure monad transformer). But in principle it would be nicer not to have Clock in the name every time.

Copy link
Owner Author

@turion turion Oct 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Csv clashes prominently with a type of the same name from cassava. I could call it CsvRow or something like that. But CsvClock is more memorable.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CsvRecord? And a corresponding better name for the parsing variant?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if the clock automatically did the parsing (and throws an error if it fails)


-- | Create a 'CsvClock' from a file path.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document how the CSV file needs to be formatted, probably in the module doc

csvClock :: FilePath -> CsvClock
csvClock filename =
CsvClock
File
{ action = fmap (decode NoHeader . fromStrict) . Data.ByteString.hGetLine
, filename
}

instance Clock (ExceptT (Either String FileException) IO) CsvClock where
type Time CsvClock = Integer
type Tag CsvClock = Vector Record
initClock (CsvClock fileClock) = initClock fileClock

instance GetClockProxy CsvClock
80 changes: 80 additions & 0 deletions rhine-cassava/test/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
module Main (main) where

-- transformers
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Writer (WriterT (..), tell)

-- bytestring
import Data.ByteString
import Data.ByteString qualified as ByteString

-- directory
import System.Directory (removeFile)

-- hspec
import Test.Hspec

-- vector
import Data.Vector (Vector)

-- cassava
import Data.Csv (Record)

-- rhine
import FRP.Rhine
import FRP.Rhine.Clock.File (FileException (..))

-- rhine-cassava
import FRP.Rhine.Clock.Csv

main :: IO ()
main = hspec $ do
it "Can read a file" $ do
let bytestring = "hi,this\nis,dog\n" :: ByteString
filepath = "testfile.csv"
ByteString.writeFile filepath bytestring
Right result <- runExceptT @(Either String FileException) $ (flip embed [(), ()] =<<) $ eraseClock $ tagS @@ csvClock filepath
removeFile filepath
result `shouldBe` ([Just [["hi", "this"]], Just [["is", "dog"]]] :: [Maybe (Vector Record)])

it "Throws EndOfFile when run for more ticks than the file is long" $ do
let bytestring = "hi,this\nis,dog\n" :: ByteString
filepath = "testfile.csv"
ByteString.writeFile filepath bytestring
Left e <- runExceptT @(Either String FileException) $ flow $ clId @@ csvClock filepath
removeFile filepath
e `shouldBe` Right EndOfFile

it "Parses the CSV before throwing EndOfFile when run for more ticks than the file is long" $ do
let bytestring = "hi,this\nis,dog\n" :: ByteString
filepath = "testfile.csv"
writerClock = HoistClock (csvClock filepath) (mapExceptT lift) :: HoistClock (ExceptT (Either String FileException) IO) (ExceptT (Either String FileException) (WriterT (Vector Record) IO)) CsvClock
ByteString.writeFile filepath bytestring
(Left e, contents) <- runWriterT $ runExceptT @(Either String FileException) $ flow $ tagS >-> arrMCl (lift . tell) @@ writerClock
removeFile filepath
e `shouldBe` Right EndOfFile
contents `shouldBe` ([["hi", "this"], ["is", "dog"]] :: Vector Record)

it "Throws EndOfFile when given incorrect separator" $ do
let bytestring = "oh,that\ncat,is,rude\n" :: ByteString
filepath = "testfile.csv"
ByteString.writeFile filepath bytestring
Left e <- runExceptT @(Either String FileException) $ flow $ clId @@ csvClock filepath
removeFile filepath
e `shouldBe` Left "hi"

it "Throws EndOfFile when given incorrect separator" $ do
let bytestring = "oh,that\ncat\n" :: ByteString
filepath = "testfile.csv"
ByteString.writeFile filepath bytestring
Left e <- runExceptT @(Either String FileException) $ flow $ clId @@ csvClock filepath
removeFile filepath
e `shouldBe` Left "hi"

it "Throws EndOfFile when given incorrect separator" $ do
let bytestring = "oh,that\ncat" :: ByteString
filepath = "testfile.csv"
ByteString.writeFile filepath bytestring
Left e <- runExceptT @(Either String FileException) $ flow $ clId @@ csvClock filepath
removeFile filepath
e `shouldBe` Left "hi"
18 changes: 9 additions & 9 deletions rhine-examples/rhine-examples.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ maintainer: [email protected]
category: FRP
build-type: Simple
extra-source-files: ChangeLog.md
cabal-version: 1.18
cabal-version: 2.0

executable HelloWorld
hs-source-dirs: src
main-is: HelloWorld.hs
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends: base >= 4.14 && < 4.18
, rhine == 1.0
, rhine ^>= 1.0
default-language: Haskell2010
default-extensions:
TypeOperators
Expand All @@ -32,7 +32,7 @@ executable Demonstration
main-is: Demonstration.hs
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends: base >= 4.14 && < 4.18
, rhine == 1.0
, rhine ^>= 1.0
default-language: Haskell2010
default-extensions:
TypeOperators
Expand All @@ -44,7 +44,7 @@ executable ADSR
main-is: ADSR.hs
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends: base >= 4.14 && < 4.18
, rhine == 1.0
, rhine ^>= 1.0
default-language: Haskell2010
default-extensions:
TypeOperators
Expand All @@ -56,7 +56,7 @@ executable Ball
main-is: Ball.hs
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends: base >= 4.14 && < 4.18
, rhine == 1.0
, rhine ^>= 1.0
, vector-sized >= 1.4
, random >= 1.1
default-language: Haskell2010
Expand All @@ -74,7 +74,7 @@ executable Periodic
main-is: Periodic.hs
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends: base >= 4.14 && < 4.18
, rhine == 1.0
, rhine ^>= 1.0
, transformers >= 0.5
, monad-schedule >= 0.1
default-language: Haskell2010
Expand All @@ -88,7 +88,7 @@ executable EventClock
main-is: EventClock.hs
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends: base >= 4.14 && < 4.18
, rhine == 1.0
, rhine ^>= 1.0
, random >= 1.1
default-language: Haskell2010
default-extensions:
Expand All @@ -101,7 +101,7 @@ executable Sawtooth
main-is: Sawtooth.hs
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends: base >= 4.14 && < 4.18
, rhine == 1.0
, rhine ^>= 1.0
default-language: Haskell2010
default-extensions:
TypeOperators
Expand All @@ -113,7 +113,7 @@ executable RandomWalk
main-is: RandomWalk.hs
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends: base >= 4.14 && < 4.18
, rhine == 1.0
, rhine ^>= 1.0
, random >= 1.1
, simple-affine-space
default-language: Haskell2010
Expand Down
2 changes: 1 addition & 1 deletion rhine-gloss/rhine-gloss.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ library
FRP.Rhine.Gloss.Pure.Combined
build-depends: base >= 4.14 && < 4.18
, transformers >= 0.5
, rhine == 1.0
, rhine ^>= 1.0
, dunai ^>= 0.9
, gloss >= 1.12
, mmorph >= 1.1
Expand Down
Loading