new nix setup and fourmolu

This commit is contained in:
Jack Wines 2024-04-15 23:11:46 -07:00
parent b7b2e60331
commit cfa75d1d38
No known key found for this signature in database
GPG key ID: 25B20640600571E6
6 changed files with 229 additions and 95 deletions

87
flake.lock generated
View file

@ -1,42 +1,79 @@
{
"nodes": {
"flake-utils": {
"flake-parts": {
"inputs": {
"systems": "systems"
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"haskell-flake": {
"locked": {
"lastModified": 1713084600,
"narHash": "sha256-qL7LV2MtwJ+1Xasg1TjSUmoE7yrRuXPqxpPlKjLE0SE=",
"owner": "srid",
"repo": "haskell-flake",
"rev": "847292fc793a5c15c873e52e7751ee4267ef32a0",
"type": "github"
},
"original": {
"owner": "srid",
"repo": "haskell-flake",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1692210102,
"narHash": "sha256-Th/csUBviIMpalVmRCbr2RUBT7OId6xEK9D0D7A589I=",
"owner": "NixOS",
"lastModified": 1712963716,
"narHash": "sha256-WKm9CvgCldeIVvRz87iOMi8CFVB1apJlkUT4GGvA0iM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "3514001703c39f37c854745d787911b48a48bc8e",
"rev": "cfd6b5fc90b15709b780a5a1619695a88505a176",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"dir": "lib",
"lastModified": 1711703276,
"narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d8fe5e6c92d0d190646fb9f1056741a229980089",
"type": "github"
},
"original": {
"dir": "lib",
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
"flake-parts": "flake-parts",
"haskell-flake": "haskell-flake",
"nixpkgs": "nixpkgs",
"systems": "systems",
"treefmt-nix": "treefmt-nix"
}
},
"systems": {
@ -53,6 +90,26 @@
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1711963903,
"narHash": "sha256-N3QDhoaX+paWXHbEXZapqd1r95mdshxToGowtjtYkGI=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "49dc4a92b02b8e68798abd99184f228243b6e3ac",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",

107
flake.nix
View file

@ -1,43 +1,84 @@
# SPDX-FileCopyrightText: 2021 Serokell <https://serokell.io/>
#
# SPDX-License-Identifier: CC0-1.0
{
description = "My haskell application";
description = "srid/haskell-template: Nix template for Haskell projects";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
systems.url = "github:nix-systems/default";
flake-parts.url = "github:hercules-ci/flake-parts";
haskell-flake.url = "github:srid/haskell-flake";
treefmt-nix.url = "github:numtide/treefmt-nix";
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
outputs = inputs:
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
systems = import inputs.systems;
imports = [
inputs.haskell-flake.flakeModule
inputs.treefmt-nix.flakeModule
];
perSystem = { self', system, lib, config, pkgs, ... }: {
# Our only Haskell project. You can have multiple projects, but this template
# has only one.
# See https://github.com/srid/haskell-flake/blob/master/example/flake.nix
haskellProjects.default = {
# The base package set (this value is the default)
# basePackages = pkgs.haskellPackages;
haskellPackages = pkgs.haskellPackages;
jailbreakUnbreak = pkg:
pkgs.haskell.lib.doJailbreak (pkg.overrideAttrs (_: { meta = { }; }));
# DON'T FORGET TO PUT YOUR PACKAGE NAME HERE, REMOVING `throw`
packageName = "spell-checker";
in {
packages.${packageName} =
haskellPackages.callCabal2nix packageName self rec {
# Dependency overrides go here
# Packages to add on top of `basePackages`
packages = {
# Add source or Hackage overrides here
# (Local packages are added automatically)
# https://github.com/lehins/hip.git
# hip.source = inputs.hip + "/hip";
};
packages.default = self.packages.${system}.${packageName};
defaultPackage = self.packages.${system}.default;
# Add your package overrides here
settings = {
# barbies-th = {
# broken = false;
# jailbreak = true;
# };
};
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
haskellPackages.haskell-language-server # you must build it with your ghc to work
cabal-install
];
inputsFrom = map (__getAttr "env") (__attrValues self.packages.${system});
# Development shell configuration
devShell = {
hlsCheck.enable = true;
};
# What should haskell-flake add to flake outputs?
autoWire = [ "packages" "apps" "checks" ]; # Wire all but the devShell
};
devShell = self.devShells.${system}.default;
});
# Auto formatters. This also adds a flake check to ensure that the
# source tree was auto formatted.
treefmt.config = {
projectRootFile = "flake.nix";
programs.ormolu.enable = true;
programs.nixpkgs-fmt.enable = true;
programs.cabal-fmt.enable = true;
programs.hlint.enable = true;
# We use fourmolu
programs.ormolu.package = pkgs.haskellPackages.fourmolu;
};
# Default package & app.
packages.default = self'.packages.spell-checker;
apps.default = self'.apps.spell-checker;
# Default shell.
devShells.default = pkgs.mkShell {
name = "haskell-template";
meta.description = "Haskell development environment";
# See https://zero-to-flakes.com/haskell-flake/devshell#composing-devshells
inputsFrom = [
config.haskellProjects.default.outputs.devShell
config.treefmt.build.devShell
];
nativeBuildInputs = with pkgs; [
];
};
};
};
}

View file

@ -1,24 +1,17 @@
cabal-version: 3.0
name: spell-checker
version: 0.1.0.0
-- synopsis:
-- description:
license: MIT
license-file: LICENSE
author: Jack Wines
maintainer: jack@winesj.com
-- copyright:
category: Text
build-type: Simple
extra-doc-files: CHANGELOG.md
-- extra-source-files:
common warnings
ghc-options: -Wall
executable spell-checker
import: warnings
main-is: Main.hs
other-modules: PrefixTree
default-extensions: ScopedTypeVariables OverloadedStrings
OverloadedLists TemplateHaskell DataKinds FlexibleContexts
FlexibleInstances MultiParamTypeClasses OverloadedLabels
@ -30,5 +23,5 @@ executable spell-checker
text,
MemoTrie
hs-source-dirs: src-exe
hs-source-dirs: src
default-language: GHC2021

View file

@ -1,38 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.Text as T
import qualified Data.Map.Lazy as M
import qualified Data.List as L
import qualified Data.Text.IO as TIO
import qualified Data.Ord as O
editDistance :: T.Text -> T.Text -> Word
editDistance txt0' txt1' = memoMap M.! (txt0', txt1')
where
memoMap :: M.Map (T.Text, T.Text) Word
memoMap = M.fromList . map (\txts -> (txts, uncurry editDistance' txts)) $ allPairs
allPairs = [(txt0'', txt1'') | txt0'' <- T.tails txt0', txt1'' <- T.tails txt1']
editDistance' txt0 txt1 = case (T.uncons txt0, T.uncons txt1) of
(Nothing, Nothing) -> 0
(Nothing, Just (_, txt)) -> succ . fromIntegral . T.length $ txt
(Just (_, txt), Nothing) -> succ . fromIntegral . T.length $ txt
(Just (x0, xs0), Just (x1, xs1)) -> (+) (if x0 == x1 then 0 else 1) $
minimum . map (memoMap M.!) $ [
(xs0, xs1),
(txt0, xs1),
(xs0, txt1)
]
readDict :: IO [T.Text]
readDict = T.lines <$> TIO.readFile "/usr/share/dict/words"
main :: IO ()
main = do
allWords <- readDict
let word = "asoeunht"
let vals = map (\x -> (x, editDistance word x)) allWords
print $ L.minimumBy (O.comparing snd) vals

39
src/Main.hs Normal file
View file

@ -0,0 +1,39 @@
module Main where
import Data.List qualified as L
import Data.Map.Lazy qualified as M
import Data.Ord qualified as O
import Data.Text qualified as T
import Data.Text.IO qualified as TIO
import PrefixTree qualified as PT
editDistance :: T.Text -> T.Text -> Word
editDistance txt0' txt1' = memoMap M.! (txt0', txt1')
where
memoMap :: M.Map (T.Text, T.Text) Word
memoMap = M.fromList . map (\txts -> (txts, uncurry editDistance' txts)) $ allPairs
allPairs = [(txt0'', txt1'') | txt0'' <- T.tails txt0', txt1'' <- T.tails txt1']
editDistance' txt0 txt1 = case (T.uncons txt0, T.uncons txt1) of
(Nothing, Nothing) -> 0
(Nothing, Just (_, txt)) -> succ . fromIntegral . T.length $ txt
(Just (_, txt), Nothing) -> succ . fromIntegral . T.length $ txt
(Just (x0, xs0), Just (x1, xs1)) ->
(+) (if x0 == x1 then 0 else 1)
$ minimum
. map (memoMap M.!)
$ [ (xs0, xs1)
, (txt0, xs1)
, (xs0, txt1)
]
readDict :: IO [T.Text]
readDict = T.lines <$> TIO.readFile "/usr/share/dict/words"
main :: IO ()
main = do
allWords <- readDict
let word = "supedpower"
let vals = PT.fromList . map T.unpack $ allWords
print $ PT.lookupClosest word vals

42
src/PrefixTree.hs Normal file
View file

@ -0,0 +1,42 @@
module PrefixTree where
import Control.Applicative qualified as A
import Data.Bifunctor qualified as Bi
import Data.Foldable qualified as F
import Data.List.NonEmpty qualified as NE
import Data.Map.Strict qualified as M
import Data.Maybe qualified as My
import Data.Ord qualified as O
data Tree a = (Ord a) =>
Tree
{ isTermination :: Bool
, children :: M.Map a (Tree a)
}
empty :: (Ord a) => Tree a
empty = Tree False M.empty
-- singleton :: (Ord a) => a -> Tree a
-- singleton = Tree . flip M.singleton empty
fromList :: (Ord a, F.Foldable t) => t [a] -> Tree a
fromList = F.foldl' insert empty
insert :: Tree a -> [a] -> Tree a
insert (Tree{..}) [] = Tree{isTermination = True, ..}
insert (Tree{..}) (x : xs) = Tree isTermination . flip (M.insert x) children . flip insert xs . My.fromMaybe empty . M.lookup x $ children
lookup :: (Ord a) => [a] -> Tree a -> Bool
lookup [] = const True
lookup (x : xs) = maybe False (PrefixTree.lookup xs) . M.lookup x . children
lookupClosest :: forall a. (Ord a) => [a] -> Tree a -> (Word, [a])
lookupClosest [] (Tree True _) = (0, [])
lookupClosest [] (Tree False _) = (0, [])
lookupClosest (x : xs) (Tree{..}) = maybe rest (Bi.second (x :) . lookupClosest xs) $ M.lookup x children
where
rest :: (Word, [a])
rest = Bi.first (+ 1) . F.maximumBy (O.comparing fst) . map charSkippedResult . M.assocs . M.delete x $ children
charSkippedResult (skippedChar, tree) = Bi.second (skippedChar :) . lookupClosest xs $ tree