Add a more advanced haskell project
This commit is contained in:
parent
d53b887029
commit
72f23438b1
23
06-haskell-shellfor/README.md
Normal file
23
06-haskell-shellfor/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# 06-haskell-shellfor
|
||||
|
||||
This example shows a Haskell dev shell built with `shellFor`.
|
||||
|
||||
It includes:
|
||||
|
||||
- a local package added to the Haskell package set,
|
||||
- a dev shell derived from that package with `shellFor`, and
|
||||
- a small test suite run by `nix flake check`.
|
||||
|
||||
Useful commands:
|
||||
|
||||
```bash
|
||||
nix develop
|
||||
cabal run
|
||||
cabal test
|
||||
|
||||
nix build
|
||||
./result/bin/mini-shellfor
|
||||
|
||||
nix run
|
||||
nix flake check
|
||||
```
|
||||
25
07-haskell-deps/README.md
Normal file
25
07-haskell-deps/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# 07-haskell-deps
|
||||
|
||||
This example shows a small Haskell project that uses external libraries from the package set.
|
||||
|
||||
It includes:
|
||||
|
||||
- a library that parses JSON with `aeson`,
|
||||
- `text` and `bytestring` usage in the library and CLI,
|
||||
- an executable under `app/`, and
|
||||
- a test suite run by `nix flake check`.
|
||||
|
||||
Useful commands:
|
||||
|
||||
```bash
|
||||
nix develop
|
||||
cabal run
|
||||
cabal run -- '{"name":"flakes"}'
|
||||
cabal test
|
||||
|
||||
nix build
|
||||
./result/bin/mini-json '{"name":"flakes"}'
|
||||
|
||||
nix run . -- '{"name":"flakes"}'
|
||||
nix flake check
|
||||
```
|
||||
18
07-haskell-deps/app/Main.hs
Normal file
18
07-haskell-deps/app/Main.hs
Normal file
@ -0,0 +1,18 @@
|
||||
module Main where
|
||||
|
||||
import qualified Data.ByteString.Char8 as ByteString
|
||||
import MiniJson.Greeting (greetFromJson)
|
||||
import System.Environment (getArgs)
|
||||
import System.Exit (die)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
args <- getArgs
|
||||
let input =
|
||||
case args of
|
||||
[] -> "{\"name\":\"learner\"}"
|
||||
firstArg : _ -> firstArg
|
||||
|
||||
case greetFromJson (ByteString.pack input) of
|
||||
Left err -> die err
|
||||
Right message -> putStrLn message
|
||||
27
07-haskell-deps/flake.lock
generated
Normal file
27
07-haskell-deps/flake.lock
generated
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1776548001,
|
||||
"narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
40
07-haskell-deps/flake.nix
Normal file
40
07-haskell-deps/flake.nix
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
# Builds a small Haskell program that uses external libraries from the
|
||||
# package set, so the example shows how Cabal dependencies flow through Nix.
|
||||
description = "A Haskell project with external dependencies";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{ self, nixpkgs, ... }:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
inherit (pkgs) haskellPackages;
|
||||
project = haskellPackages.callCabal2nix "mini-json" ./. { };
|
||||
checkedProject = pkgs.haskell.lib.doCheck project;
|
||||
in
|
||||
{
|
||||
packages.${system}.default = project;
|
||||
|
||||
apps.${system}.default = {
|
||||
type = "app";
|
||||
program = "${self.packages.${system}.default}/bin/mini-json";
|
||||
meta.description = "Run the Haskell JSON parsing example.";
|
||||
};
|
||||
|
||||
devShells.${system}.default = pkgs.mkShell {
|
||||
packages = [
|
||||
haskellPackages.ghc
|
||||
pkgs.cabal-install
|
||||
pkgs.haskell-language-server
|
||||
];
|
||||
};
|
||||
|
||||
# `doCheck` runs the Cabal test suite, which exercises both valid and
|
||||
# invalid JSON input through the library function.
|
||||
checks.${system}.test-suite = checkedProject;
|
||||
};
|
||||
}
|
||||
33
07-haskell-deps/mini-json.cabal
Normal file
33
07-haskell-deps/mini-json.cabal
Normal file
@ -0,0 +1,33 @@
|
||||
cabal-version: 2.4
|
||||
name: mini-json
|
||||
version: 0.1.0.0
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: MiniJson.Greeting
|
||||
hs-source-dirs: src
|
||||
build-depends:
|
||||
aeson,
|
||||
base >=4.14 && <5,
|
||||
bytestring,
|
||||
text
|
||||
default-language: Haskell2010
|
||||
|
||||
executable mini-json
|
||||
main-is: Main.hs
|
||||
hs-source-dirs: app
|
||||
build-depends:
|
||||
base >=4.14 && <5,
|
||||
bytestring,
|
||||
mini-json
|
||||
default-language: Haskell2010
|
||||
|
||||
test-suite mini-json-test
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Main.hs
|
||||
hs-source-dirs: test
|
||||
build-depends:
|
||||
base >=4.14 && <5,
|
||||
bytestring,
|
||||
mini-json
|
||||
default-language: Haskell2010
|
||||
20
07-haskell-deps/src/MiniJson/Greeting.hs
Normal file
20
07-haskell-deps/src/MiniJson/Greeting.hs
Normal file
@ -0,0 +1,20 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module MiniJson.Greeting where
|
||||
|
||||
import Data.Aeson ((.:), FromJSON (parseJSON), eitherDecodeStrict', withObject)
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as Text
|
||||
|
||||
data GreetingRequest = GreetingRequest
|
||||
{ name :: Text
|
||||
}
|
||||
|
||||
instance FromJSON GreetingRequest where
|
||||
parseJSON = withObject "GreetingRequest" (\object -> GreetingRequest <$> object .: "name")
|
||||
|
||||
greetFromJson :: ByteString -> Either String String
|
||||
greetFromJson input = do
|
||||
request <- eitherDecodeStrict' input
|
||||
pure ("hello, " ++ Text.unpack (name request) ++ ", from aeson")
|
||||
14
07-haskell-deps/test/Main.hs
Normal file
14
07-haskell-deps/test/Main.hs
Normal file
@ -0,0 +1,14 @@
|
||||
module Main where
|
||||
|
||||
import qualified Data.ByteString.Char8 as ByteString
|
||||
import MiniJson.Greeting (greetFromJson)
|
||||
import System.Exit (die)
|
||||
|
||||
main :: IO ()
|
||||
main =
|
||||
case
|
||||
( greetFromJson (ByteString.pack "{\"name\":\"flakes\"}")
|
||||
, greetFromJson (ByteString.pack "{\"missing\":\"name\"}")
|
||||
) of
|
||||
(Right "hello, flakes, from aeson", Left _) -> putStrLn "test passed"
|
||||
_ -> die "unexpected parser result"
|
||||
67
notes/009-haskell-dependencies.md
Normal file
67
notes/009-haskell-dependencies.md
Normal file
@ -0,0 +1,67 @@
|
||||
# Haskell Dependencies
|
||||
|
||||
This note covers `07-haskell-deps/`, which builds a small Haskell program that depends on external libraries from the Haskell package set.
|
||||
|
||||
---
|
||||
|
||||
## 1. What This Example Adds
|
||||
|
||||
The earlier Haskell examples stayed close to `base` and local modules.
|
||||
|
||||
This example adds three common Haskell libraries:
|
||||
|
||||
- `aeson` for JSON decoding,
|
||||
- `text` for string data in the parsed value, and
|
||||
- `bytestring` for the raw input passed into the decoder.
|
||||
|
||||
That makes the example useful for two reasons:
|
||||
|
||||
- it shows how Cabal `build-depends` entries become Nix build inputs automatically, and
|
||||
- it demonstrates a more realistic program shape than a pure string concatenation example.
|
||||
|
||||
---
|
||||
|
||||
## 2. The Important Point About Dependencies
|
||||
|
||||
The flake still uses the same Nix expression pattern as `05-haskell/`:
|
||||
|
||||
```nix
|
||||
project = haskellPackages.callCabal2nix "mini-json" ./. { };
|
||||
```
|
||||
|
||||
The external libraries are not listed again in `flake.nix`. They are declared in `mini-json.cabal`, and `callCabal2nix` translates that Cabal package description into a Nix derivation.
|
||||
|
||||
That is the main teaching point: Cabal remains the source of truth for Haskell package dependencies, while the flake decides how the package is exposed as a build, app, dev shell, and check.
|
||||
|
||||
---
|
||||
|
||||
## 3. The Library Function
|
||||
|
||||
The library exposes one function:
|
||||
|
||||
```haskell
|
||||
greetFromJson :: ByteString -> Either String String
|
||||
```
|
||||
|
||||
It decodes a JSON object with a `name` field and returns a greeting string. The executable wraps that function in a small CLI, and the test suite checks both a valid input and an invalid one.
|
||||
|
||||
That keeps the example focused on dependency usage, not CLI design.
|
||||
|
||||
---
|
||||
|
||||
## 4. Commands to Try
|
||||
|
||||
```bash
|
||||
cd 07-haskell-deps
|
||||
|
||||
nix develop
|
||||
cabal run
|
||||
cabal run -- '{"name":"flakes"}'
|
||||
cabal test
|
||||
|
||||
nix build
|
||||
./result/bin/mini-json '{"name":"flakes"}'
|
||||
|
||||
nix run . -- '{"name":"flakes"}'
|
||||
nix flake check
|
||||
```
|
||||
Loading…
x
Reference in New Issue
Block a user