Skip to content
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

Rebuild the nix flake; drop htoml dependency #173

Merged
merged 9 commits into from
Oct 22, 2023
Merged
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# 0.7.4
Replace the `htoml` package used in a test with `tomland`.

# 0.7.2

- Add `writeCSVopts` that accepts options to specify the CSV delimiter.
14 changes: 7 additions & 7 deletions Frames.cabal
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Frames
version: 0.7.3
version: 0.7.4
synopsis: Data frames For working with tabular data files
description: User-friendly, type safe, runtime efficient tooling for
working with tabular data deserialized from
@@ -70,7 +70,7 @@ library
text >= 1.1.1.0,
template-haskell,
transformers,
vector,
vector < 0.13,
readable >= 0.3.1,
pipes >= 4.1 && < 5,
pipes-bytestring >= 2.1.6 && < 2.2,
@@ -150,7 +150,7 @@ executable demo
pipes
hs-source-dirs: demo/framestack/app
default-language: Haskell2010
ghc-options: -O2
ghc-options: -O2 -fsimpl-tick-factor=200
-- ghc-options: -O2 -fllvm

executable tutorial
@@ -176,7 +176,7 @@ executable benchdemo
hs-source-dirs: benchmarks
default-language: Haskell2010
-- ghc-options: -O2
ghc-options: -O2 -fllvm
ghc-options: -O2 -fllvm -fsimpl-tick-factor=200

-- A demonstration of dealing with missing data. Provided for source
-- code and experimentation rather than a useful executable.
@@ -235,9 +235,9 @@ test-suite spec
UncurryFold UncurryFoldNoHeader UncurryFoldPartialData
Categorical Chunks Issue145
build-depends: base, text, hspec, Frames, template-haskell,
temporary, directory, htoml, regex-applicative, pretty,
unordered-containers, pipes, HUnit, vinyl,
foldl >= 1.3 && < 1.5,
temporary, directory, tomland, regex-applicative, pretty,
unordered-containers, pipes, HUnit, vinyl,
foldl >= 1.3 && < 1.5, validation-selective,
attoparsec, lens, bytestring
ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
default-language: Haskell2010
39 changes: 0 additions & 39 deletions default.nix

This file was deleted.

290 changes: 22 additions & 268 deletions flake.lock
186 changes: 36 additions & 150 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -1,162 +1,48 @@
{
description = "Data frames for tabular data.";

nixConfig = {
extra-substituters = [
"https://haskell-language-server.cachix.org"
];
extra-trusted-public-keys = [
"haskell-language-server.cachix.org-1:juFfHrwkOxqIOZShtC4YC1uT1bBcq2RSvC7OMKx0Nz8="
];
};

inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
hls.url = "github:haskell/haskell-language-server";
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
};

outputs = { self, nixpkgs, hls, flake-utils}:

flake-utils.lib.eachDefaultSystem (system: let

pkgs = import nixpkgs {
inherit system;
overlays = [ self.overlay ];
};

compilerVersionFromHsPkgs = hsPkgs:
pkgs.lib.replaceStrings [ "." ] [ "" ] hsPkgs.ghc.version;

hspkgs810 = pkgs.haskell.packages."ghc8107".override {
overrides = pkgs.frameHaskellOverlay-8107;
};
hspkgs92 = pkgs.haskell.packages."ghc921".override {
overrides = pkgs.frameHaskellOverlay-921;
};

mkPackage = hspkgs:
hspkgs.developPackage {
root = pkgs.lib.cleanSource ./.;
name = "Frames";
returnShellEnv = false;
withHoogle = true;
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let pkg-name = "Frames";
pkgs = import nixpkgs {
inherit system;
config = { allowBroken = true; };
};

mkShell = hspkgs:
let
compilerVersion = compilerVersionFromHsPkgs hspkgs;
myModifier = drv:
pkgs.haskell.lib.addBuildTools drv (with hspkgs; [
cabal-install
ghcid
hls.packages.${system}."haskell-language-server-${compilerVersion}"
hasktags
]);
in
(myModifier (mkPackage hspkgs)).envFunc {};

mkSimpleShell = compilerVersion:
let
compiler = pkgs.haskell.compiler."ghc${compilerVersion}";
in
pkgs.mkShell {
buildInputs = [
pkgs.haskell.compiler."ghc${compilerVersion}"
pkgs.haskell.packages."ghc${compilerVersion}".cabal-install
pkgs.llvmPackages_latest.llvm
] ++
pkgs.lib.optional (compilerVersion != "921")
hls.packages.${system}."haskell-language-server-${compilerVersion}";
};
in {
packages = {
Frames-8107 = mkPackage hspkgs810;
Frames-921 = mkPackage hspkgs92;
};

devShell = mkSimpleShell "921";

devShells = {
Frames-8107 = mkShell hspkgs810;
Frames-921 = mkShell hspkgs92;
};
}) // {

overlay = final: prev: {
frameHaskellOverlay-921 = hfinal: hprev: (
(final.frameHaskellOverlay-8107 hfinal hprev) // (
let doJailbreak = prev.haskell.lib.doJailbreak;
overrideSrc = prev.haskell.lib.overrideSrc;
dontHaddock = prev.haskell.lib.dontHaddock;
dontCheck = prev.haskell.lib.dontCheck;
in {
# Temporary fixes for breakage with ghc-9.2.1
attoparsec = dontCheck hprev.attoparsec;
base-compat-batteries = dontCheck hprev.base-compat-batteries;
basement = dontHaddock hprev.basement;
blaze-builder = dontCheck hprev.blaze-builder;
blaze-markup = dontCheck hprev.blaze-markup;
case-insensitive = dontCheck hprev.case-insensitive;
cassava = dontCheck hprev.cassava;
conduit-extra = dontCheck hprev.conduit-extra;
criterion = dontCheck hprev.criterion;
cryptonite = dontHaddock hprev.cryptonite;
fast-logger = dontCheck hprev.fast-logger;
htoml = dontCheck hprev.htoml;
lens-family-core = dontHaddock hprev.lens-family-core;
ListLike = dontCheck hprev.ListLike;
microlens = dontHaddock hprev.microlens;
microstache = dontCheck hprev.microstache;
readable = dontHaddock (doJailbreak hprev.readable);
QuickCheck = dontCheck hprev.QuickCheck;
operational = dontHaddock hprev.operational;
optparse-applicative = dontCheck hprev.optparse-applicative;
generic-deriving = dontHaddock hprev.generic-deriving;
streaming-commons = dontCheck hprev.streaming-commons;
utf8-string = dontCheck hprev.utf8-string;
word8 = dontCheck hprev.word8;
}));

frameHaskellOverlay-8107 = hfinal: hprev:
let doJailbreak = prev.haskell.lib.doJailbreak;
overrideSrc = prev.haskell.lib.overrideSrc;
dontHaddock = prev.haskell.lib.dontHaddock;
dontCheck = prev.haskell.lib.dontCheck;
in {
statestack = doJailbreak hprev.statestack;
svg-builder = doJailbreak hprev.svg-builder;
# see https://github.com/JonasDuregard/sized-functors/pull/10
# https://github.com/
size-based = doJailbreak (overrideSrc hprev.size-based {
version = "unstable-2022-01-20";
src = final.fetchzip {
url = "https://github.com/byorgey/sized-functors/archive/master.tar.gz";
sha256 = "sha256-pVJbEGF4/lvXmWIypwkMQBYygOx3TQwLJbMpfdYovdY=";
};
});
monoid-extras = doJailbreak hprev.monoid-extras;
active = doJailbreak hprev.active;
dual-tree = doJailbreak hprev.dual-tree;
diagrams-core = doJailbreak hprev.diagrams-core;
diagrams-lib = doJailbreak hprev.diagrams-lib;
diagrams-postscript = doJailbreak hprev.diagrams-postscript;
SVGFonts = doJailbreak hprev.SVGFonts;
diagrams-svg = doJailbreak hprev.diagrams-svg;
diagrams-rasterific = doJailbreak hprev.diagrams-rasterific;
Chart = doJailbreak hprev.Chart;
linear = hprev.callHackage "linear" "1.21.8" {};
vinyl = overrideSrc hprev.vinyl {
version = "0.14.1";
src = prev.fetchFromGitHub {
owner = "VinylRecords";
repo = "Vinyl";
rev = "892d597f9dd8e96c0853269ab78141ae2e03aa2c";
hash = "sha256-ONw+8D1r4xX9+KgYOFpTNhk+pCsNZW8DbbAzOheSkS0=";
haskell = pkgs.haskellPackages;
haskell-overlay = final: prev:
let overrideSrc = pkgs.haskell.lib.overrideSrc;
in {
${pkg-name} = hspkgs.callCabal2nix pkg-name ./. {};
# Add here any package overrides you may need
};
hspkgs = haskell.override {
overrides = haskell-overlay;
};
readable = doJailbreak hprev.readable;
in {
packages = pkgs;
apps.init = pkgs.writeShellApplication {
name = "cabal-init";
runtimeInputs = [hspkgs.ghc hspkgs.cabal-install];
text = ''
cabal init -p ${pkg-name}
'';
};
};
};
inherit haskell-overlay;
defaultPackage = hspkgs.${pkg-name};
devShell = hspkgs.shellFor {
packages = p: [p.${pkg-name}];
root = ./.;
withHoogle = true;
buildInputs = with hspkgs; [
haskell-language-server
cabal-install
];
};
}
);
}
98 changes: 50 additions & 48 deletions test/DataCSV.hs
Original file line number Diff line number Diff line change
@@ -1,56 +1,58 @@
{-# LANGUAGE DeriveLift, OverloadedStrings, TemplateHaskell #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}

module DataCSV where
import Control.Monad ((>=>))

import Data.Bifunctor (first)
import qualified Data.ByteString as BS
import qualified Data.HashMap.Lazy as H
import Data.Maybe (catMaybes)
import qualified Data.Foldable as F
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Data.Text.Encoding (decodeUtf8)
import Language.Haskell.TH.Syntax (Lift(..))
import Text.Toml
import Text.Toml.Types (Node (VTable, VString), Table)
import Language.Haskell.TH.Syntax (Lift (..))
import qualified Toml
import Validation

data CsvExample = CsvExample {
name :: String
, csv :: String
, generated :: String }
deriving Lift
data CsvExample = CsvExample
{ name :: String
, csv :: String
, generated :: String
}
deriving (Lift, Show)

-- instance Lift CsvExample where
-- lift (CsvExample n c g) = [e| CsvExample n c g |]
keyText :: Toml.Key -> T.Text
keyText = F.fold . cleanup . map Toml.unPiece . F.toList . Toml.unKey
where
-- Top-level table names are parsed as @"name" :|
-- ["name"]@. Remove that duplication here.
cleanup [x, y] | x == y = [x]
cleanup x = x

examplesFrom :: FilePath -> IO [CsvExample]
examplesFrom fp =
(either error id
. ((first show . parseTomlDoc "examples") >=> go)
. decodeUtf8)
<$> BS.readFile fp
where go :: Table -> Either String [CsvExample]
go = fmap catMaybes . mapM (uncurry ex . first T.unpack) . H.toList
ex :: String -> Node -> Either String (Maybe CsvExample)
ex k (VTable v) =
do c <- case H.lookup "csv" v of
Nothing -> Right Nothing -- ("No csv key in "++k)
Just (VString c) -> Right (Just (T.unpack c))
Just _ -> Left ("csv key not a string in " ++ k)
g <- case H.lookup "generated" v of
Nothing -> Left ("No generated key in " ++ k)
Just (VString g) -> Right (Just (T.unpack g))
Just _ -> Left ("generated key not a string in " ++ k)
return (CsvExample k <$> c <*> g)
ex k _ = Left (k ++ " is not a table")
-- | Parse a TOML file that is a top-level table whose values are all
-- the same type. The @tomland@ codec API is centered around starting
-- with a key, but a top-level table does not have a key, so we must
-- use the lower level 'Toml.parse' and 'Toml.tomlTables' before
-- repeatedly applying the provided 'Toml.TomlCodec'.
parseFileOf :: forall a. Toml.TomlCodec a -> T.Text -> Either [T.Text] [(T.Text, a)]
parseFileOf codec =
first (map Toml.prettyTomlDecodeError)
. validationToEither
. traverse (uncurry go)
. Toml.toList
. Toml.tomlTables
. either (error . show) id
. Toml.parse
where
go :: Toml.Key -> Toml.TOML -> Validation [Toml.TomlDecodeError] (T.Text, a)
go k v = (keyText k,) <$> Toml.runTomlCodec codec v

generatedFrom :: FilePath -> String -> IO String
generatedFrom fp key = (either error id . (>>= go)
. first show . parseTomlDoc "examples")
<$> T.readFile fp
where go :: Table -> Either String String
go toml = do tbl <- case H.lookup (T.pack key) toml of
Just (VTable t) -> Right t
_ -> Left (key ++ " is not a table")
case H.lookup "generated" tbl of
Just (VString g) -> Right (T.unpack g)
Just _ -> Left ("generated key not a string in " ++ key)
Nothing -> Left ("No generated key in " ++ key)
parseExamples :: FilePath -> IO (Either [T.Text] [CsvExample])
parseExamples = fmap (fmap (map mkExample) . parseFileOf exampleCodec) . T.readFile
where
exampleCodec = Toml.pair (Toml.string "csv") (Toml.string "generated")
mkExample (name', (csv', generated')) =
CsvExample (T.unpack name') csv' generated'

-- | Wraps 'parseExamples' to call 'error' on any parse errors.
examplesFrom :: FilePath -> IO [CsvExample]
examplesFrom = fmap (either (error . show) id) . parseExamples
132 changes: 66 additions & 66 deletions test/examples.toml
Original file line number Diff line number Diff line change
@@ -102,72 +102,72 @@ managerId' :: forall f_22 g_23 rs_24 . (Functor f_22,
Rec g_23 rs_24 -> f_22 (Rec g_23 rs_24)
managerId' = rlens' @ManagerId"""

[managers_employees]
generated = """
type ManagerRec = Record [Id, Manager, Age, Pay]
managerRecParser :: ParserOptions
managerRecParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')
type Id = "id" :-> Int
id :: forall f_0 rs_1 . (Functor f_0, Id ∈ rs_1) =>
(Int -> f_0 Bool) -> Record rs_1 -> f_0 (Record rs_1)
id = rlens @Id . rfield
id' :: forall f_2 g_3 rs_4 . (Functor f_2, Functor g_3, Id ∈ rs_4) =>
(g_3 Id -> f_2 (g_3 Id)) -> Rec g_3 rs_4 -> f_2 (Rec g_3 rs_4)
id' = rlens' @Id
type Manager = "manager" :-> Text
manager :: forall f_5 rs_6 . (Functor f_5, Manager ∈ rs_6) =>
(Text -> f_5 Text) -> Record rs_6 -> f_5 (Record rs_6)
manager = rlens @Manager . rfield
manager' :: forall f_7 g_8 rs_9 . (Functor f_7,
Manager ∈ rs_9) =>
(g_8 Manager -> f_7 (g_8 Manager)) -> Rec g_8 rs_9 -> f_7 (Rec g_8 rs_9)
manager' = rlens' @Manager
type Age = "age" :-> Int
age :: forall f_10 rs_11 . (Functor f_10, Age ∈ rs_11) =>
(Int -> f_10 Int) -> Record rs_11 -> f_10 (Record rs_11)
age = rlens @Age . rfield
age' :: forall f_12 g_13 rs_14 . (Functor f_12,
Age ∈ rs_14) =>
(g_13 Age -> f_12 (g_13 Age)) -> Rec g_13 rs_14 -> f_12 (Rec g_13 rs_14)
age' = rlens' @Age
type Pay = "pay" :-> Double
pay :: forall f_15 rs_16 . (Functor f_15, Pay ∈ rs_16) =>
(Double -> f_15 Double) -> Record rs_16 -> f_15 (Record rs_16)
pay = rlens @Pay . rfield
pay' :: forall f_17 g_18 rs_19 . (Functor f_17,
Pay ∈ rs_19) =>
(g_18 Pay -> f_17 (g_18 Pay)) -> Rec g_18 rs_19 -> f_17 (Rec g_18 rs_19)
pay' = rlens' @Pay
type EmployeeRec = Record ["id" :-> Int, "employee" :-> Text, "age" :-> Int, "pay" :-> Double, "manager_id" :-> Int]
employeeRecParser :: ParserOptions
employeeRecParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')
type Employee = "employee" :-> Text
employee :: forall f_5 rs_6 . (Functor f_5, Employee ∈ rs_6) =>
(Text -> f_5 Text) -> Record rs_6 -> f_5 (Record rs_6)
employee = rlens @Employee . rfield
employee' :: forall f_7 g_8 rs_9 . (Functor f_7,
Employee ∈ rs_9) =>
(g_8 Employee -> f_7 (g_8 Employee)) -> Rec g_8 rs_9 -> f_7 (Rec g_8 rs_9)
employee' = rlens' @Employee
type Age = "age" :-> Int
type ManagerId = "manager_id" :-> Int
managerId :: forall f_20 rs_21 . (Functor f_20, ManagerId ∈ rs_21) =>
(Int -> f_20 Int) -> Record rs_21 -> f_20 (Record rs_21)
managerId = rlens @ManagerId . rfield
managerId' :: forall f_22 g_23 rs_24 . (Functor f_22,
ManagerId ∈ rs_24) =>
(g_23 ManagerId -> f_22 (g_23 ManagerId)) ->
Rec g_23 rs_24 -> f_22 (Rec g_23 rs_24)
managerId' = rlens' @ManagerId
"""
# [managers_employees]
# generated = """
# type ManagerRec = Record [Id, Manager, Age, Pay]
# managerRecParser :: ParserOptions
# managerRecParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')

# type Id = "id" :-> Int
# id :: forall f_0 rs_1 . (Functor f_0, Id ∈ rs_1) =>
# (Int -> f_0 Bool) -> Record rs_1 -> f_0 (Record rs_1)
# id = rlens @Id . rfield
# id' :: forall f_2 g_3 rs_4 . (Functor f_2, Functor g_3, Id ∈ rs_4) =>
# (g_3 Id -> f_2 (g_3 Id)) -> Rec g_3 rs_4 -> f_2 (Rec g_3 rs_4)
# id' = rlens' @Id

# type Manager = "manager" :-> Text
# manager :: forall f_5 rs_6 . (Functor f_5, Manager ∈ rs_6) =>
# (Text -> f_5 Text) -> Record rs_6 -> f_5 (Record rs_6)
# manager = rlens @Manager . rfield
# manager' :: forall f_7 g_8 rs_9 . (Functor f_7,
# Manager ∈ rs_9) =>
# (g_8 Manager -> f_7 (g_8 Manager)) -> Rec g_8 rs_9 -> f_7 (Rec g_8 rs_9)
# manager' = rlens' @Manager

# type Age = "age" :-> Int
# age :: forall f_10 rs_11 . (Functor f_10, Age ∈ rs_11) =>
# (Int -> f_10 Int) -> Record rs_11 -> f_10 (Record rs_11)
# age = rlens @Age . rfield
# age' :: forall f_12 g_13 rs_14 . (Functor f_12,
# Age ∈ rs_14) =>
# (g_13 Age -> f_12 (g_13 Age)) -> Rec g_13 rs_14 -> f_12 (Rec g_13 rs_14)
# age' = rlens' @Age

# type Pay = "pay" :-> Double
# pay :: forall f_15 rs_16 . (Functor f_15, Pay ∈ rs_16) =>
# (Double -> f_15 Double) -> Record rs_16 -> f_15 (Record rs_16)
# pay = rlens @Pay . rfield
# pay' :: forall f_17 g_18 rs_19 . (Functor f_17,
# Pay ∈ rs_19) =>
# (g_18 Pay -> f_17 (g_18 Pay)) -> Rec g_18 rs_19 -> f_17 (Rec g_18 rs_19)
# pay' = rlens' @Pay

# type EmployeeRec = Record ["id" :-> Int, "employee" :-> Text, "age" :-> Int, "pay" :-> Double, "manager_id" :-> Int]
# employeeRecParser :: ParserOptions
# employeeRecParser = ParserOptions Nothing (T.pack ",") (Frames.CSV.RFC4180Quoting '"')

# type Employee = "employee" :-> Text
# employee :: forall f_5 rs_6 . (Functor f_5, Employee ∈ rs_6) =>
# (Text -> f_5 Text) -> Record rs_6 -> f_5 (Record rs_6)
# employee = rlens @Employee . rfield
# employee' :: forall f_7 g_8 rs_9 . (Functor f_7,
# Employee ∈ rs_9) =>
# (g_8 Employee -> f_7 (g_8 Employee)) -> Rec g_8 rs_9 -> f_7 (Rec g_8 rs_9)
# employee' = rlens' @Employee

# type Age = "age" :-> Int

# type ManagerId = "manager_id" :-> Int
# managerId :: forall f_20 rs_21 . (Functor f_20, ManagerId ∈ rs_21) =>
# (Int -> f_20 Int) -> Record rs_21 -> f_20 (Record rs_21)
# managerId = rlens @ManagerId . rfield
# managerId' :: forall f_22 g_23 rs_24 . (Functor f_22,
# ManagerId ∈ rs_24) =>
# (g_23 ManagerId -> f_22 (g_23 ManagerId)) ->
# Rec g_23 rs_24 -> f_22 (Rec g_23 rs_24)
# managerId' = rlens' @ManagerId
# """

[double_gt_bool]
csv = """