Add more mini-Haskell projects (plus related note files)
This commit is contained in:
parent
d7dc17eac0
commit
4177cdb552
25
11-haskell-typeclasses/README.md
Normal file
25
11-haskell-typeclasses/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# 11-haskell-typeclasses
|
||||
|
||||
This example shows intermediate Haskell abstraction with a custom type class and several instances.
|
||||
|
||||
It includes:
|
||||
|
||||
- a custom `Renderable` type class,
|
||||
- instances for domain types and a report wrapper,
|
||||
- a small CLI that uses one shared rendering interface, and
|
||||
- a test suite run by `nix flake check`.
|
||||
|
||||
Useful commands:
|
||||
|
||||
```bash
|
||||
nix develop
|
||||
cabal run
|
||||
cabal run -- production failed 2
|
||||
cabal test
|
||||
|
||||
nix build
|
||||
./result/bin/mini-render production failed 2
|
||||
|
||||
nix run . -- production failed 2
|
||||
nix flake check
|
||||
```
|
||||
16
11-haskell-typeclasses/app/Main.hs
Normal file
16
11-haskell-typeclasses/app/Main.hs
Normal file
@ -0,0 +1,16 @@
|
||||
module Main where
|
||||
|
||||
import MiniRender.Report (parseDeployment, render, sampleReport)
|
||||
import System.Environment (getArgs)
|
||||
import System.Exit (die)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
args <- getArgs
|
||||
|
||||
case args of
|
||||
[] -> putStr (render sampleReport)
|
||||
_ ->
|
||||
case parseDeployment args of
|
||||
Left err -> die err
|
||||
Right deployment -> putStrLn (render deployment)
|
||||
27
11-haskell-typeclasses/flake.lock
generated
Normal file
27
11-haskell-typeclasses/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
|
||||
}
|
||||
38
11-haskell-typeclasses/flake.nix
Normal file
38
11-haskell-typeclasses/flake.nix
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
# Builds a small Haskell project that focuses on a custom type class and
|
||||
# several instances that share one rendering interface.
|
||||
description = "A Haskell project for type classes and custom instances";
|
||||
|
||||
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-render" ./. { };
|
||||
checkedProject = pkgs.haskell.lib.doCheck project;
|
||||
in
|
||||
{
|
||||
packages.${system}.default = project;
|
||||
|
||||
apps.${system}.default = {
|
||||
type = "app";
|
||||
program = "${self.packages.${system}.default}/bin/mini-render";
|
||||
meta.description = "Run the type class and custom instance example.";
|
||||
};
|
||||
|
||||
devShells.${system}.default = pkgs.mkShell {
|
||||
packages = [
|
||||
haskellPackages.ghc
|
||||
pkgs.cabal-install
|
||||
pkgs.haskell-language-server
|
||||
];
|
||||
};
|
||||
|
||||
checks.${system}.test-suite = checkedProject;
|
||||
};
|
||||
}
|
||||
27
11-haskell-typeclasses/mini-render.cabal
Normal file
27
11-haskell-typeclasses/mini-render.cabal
Normal file
@ -0,0 +1,27 @@
|
||||
cabal-version: 2.4
|
||||
name: mini-render
|
||||
version: 0.1.0.0
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: MiniRender.Report
|
||||
hs-source-dirs: src
|
||||
build-depends: base >=4.14 && <5
|
||||
default-language: Haskell2010
|
||||
|
||||
executable mini-render
|
||||
main-is: Main.hs
|
||||
hs-source-dirs: app
|
||||
build-depends:
|
||||
base >=4.14 && <5,
|
||||
mini-render
|
||||
default-language: Haskell2010
|
||||
|
||||
test-suite mini-render-test
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Main.hs
|
||||
hs-source-dirs: test
|
||||
build-depends:
|
||||
base >=4.14 && <5,
|
||||
mini-render
|
||||
default-language: Haskell2010
|
||||
73
11-haskell-typeclasses/src/MiniRender/Report.hs
Normal file
73
11-haskell-typeclasses/src/MiniRender/Report.hs
Normal file
@ -0,0 +1,73 @@
|
||||
module MiniRender.Report where
|
||||
|
||||
data Environment
|
||||
= Staging
|
||||
| Production
|
||||
deriving (Eq, Show)
|
||||
|
||||
data BuildState
|
||||
= Queued
|
||||
| Running
|
||||
| Passed
|
||||
| Failed Int
|
||||
deriving (Eq, Show)
|
||||
|
||||
data Deployment = Deployment
|
||||
{ environment :: Environment
|
||||
, buildState :: BuildState
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
newtype DeploymentReport = DeploymentReport
|
||||
{ deployments :: [Deployment]
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
class Renderable a where
|
||||
render :: a -> String
|
||||
|
||||
instance Renderable Environment where
|
||||
render Staging = "staging"
|
||||
render Production = "production"
|
||||
|
||||
instance Renderable BuildState where
|
||||
render Queued = "queued"
|
||||
render Running = "running"
|
||||
render Passed = "passed"
|
||||
render (Failed retryCount) = "failed after " ++ show retryCount ++ " retries"
|
||||
|
||||
instance Renderable Deployment where
|
||||
render deployment =
|
||||
render (environment deployment) ++ ": " ++ render (buildState deployment)
|
||||
|
||||
instance Renderable DeploymentReport where
|
||||
render report = unlines (map render (deployments report))
|
||||
|
||||
sampleReport :: DeploymentReport
|
||||
sampleReport =
|
||||
DeploymentReport
|
||||
[ Deployment Staging Passed
|
||||
, Deployment Production (Failed 2)
|
||||
]
|
||||
|
||||
parseEnvironment :: String -> Either String Environment
|
||||
parseEnvironment "staging" = Right Staging
|
||||
parseEnvironment "production" = Right Production
|
||||
parseEnvironment other = Left ("unknown environment: " ++ other)
|
||||
|
||||
parseBuildState :: [String] -> Either String BuildState
|
||||
parseBuildState ["queued"] = Right Queued
|
||||
parseBuildState ["running"] = Right Running
|
||||
parseBuildState ["passed"] = Right Passed
|
||||
parseBuildState ["failed", retryCount] =
|
||||
case reads retryCount of
|
||||
[(parsedRetryCount, "")] -> Right (Failed parsedRetryCount)
|
||||
_ -> Left ("invalid retry count: " ++ retryCount)
|
||||
parseBuildState _ =
|
||||
Left "expected one of: queued | running | passed | failed <retry-count>"
|
||||
|
||||
parseDeployment :: [String] -> Either String Deployment
|
||||
parseDeployment [] =
|
||||
Left "expected: <staging|production> <queued|running|passed|failed> [retry-count]"
|
||||
parseDeployment (environmentArg : buildStateArgs) =
|
||||
Deployment <$> parseEnvironment environmentArg <*> parseBuildState buildStateArgs
|
||||
25
11-haskell-typeclasses/test/Main.hs
Normal file
25
11-haskell-typeclasses/test/Main.hs
Normal file
@ -0,0 +1,25 @@
|
||||
module Main where
|
||||
|
||||
import MiniRender.Report
|
||||
( BuildState (Failed, Passed)
|
||||
, Deployment (Deployment)
|
||||
, DeploymentReport (DeploymentReport)
|
||||
, Environment (Production, Staging)
|
||||
, parseDeployment
|
||||
, render
|
||||
, sampleReport
|
||||
)
|
||||
import System.Exit (die)
|
||||
|
||||
main :: IO ()
|
||||
main =
|
||||
case
|
||||
( render (Deployment Production (Failed 2))
|
||||
, parseDeployment ["staging", "passed"]
|
||||
, lines (render sampleReport)
|
||||
) of
|
||||
( "production: failed after 2 retries"
|
||||
, Right (Deployment Staging Passed)
|
||||
, ["staging: passed", "production: failed after 2 retries"]
|
||||
) -> putStrLn "test passed"
|
||||
_ -> die "unexpected rendering result"
|
||||
25
12-haskell-parser-combinators/README.md
Normal file
25
12-haskell-parser-combinators/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# 12-haskell-parser-combinators
|
||||
|
||||
This example shows intermediate Haskell parsing with Megaparsec and parser combinators.
|
||||
|
||||
It includes:
|
||||
|
||||
- a small command language for deploy instructions,
|
||||
- parser combinators for sequencing, choice, repetition, and end-of-input,
|
||||
- a CLI that parses and renders the parsed command, and
|
||||
- a test suite run by `nix flake check`.
|
||||
|
||||
Useful commands:
|
||||
|
||||
```bash
|
||||
nix develop
|
||||
cabal run
|
||||
cabal run -- deploy api production tags=blue,stable
|
||||
cabal test
|
||||
|
||||
nix build
|
||||
./result/bin/mini-parser deploy api production tags=blue,stable
|
||||
|
||||
nix run . -- deploy api production tags=blue,stable
|
||||
nix flake check
|
||||
```
|
||||
17
12-haskell-parser-combinators/app/Main.hs
Normal file
17
12-haskell-parser-combinators/app/Main.hs
Normal file
@ -0,0 +1,17 @@
|
||||
module Main where
|
||||
|
||||
import MiniParser.Deploy (parseDeployCommand, renderCommand)
|
||||
import System.Environment (getArgs)
|
||||
import System.Exit (die)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
args <- getArgs
|
||||
let input =
|
||||
case args of
|
||||
[] -> "deploy api staging tags=learning,flakes"
|
||||
_ -> unwords args
|
||||
|
||||
case parseDeployCommand input of
|
||||
Left err -> die err
|
||||
Right command -> putStrLn (renderCommand command)
|
||||
27
12-haskell-parser-combinators/flake.lock
generated
Normal file
27
12-haskell-parser-combinators/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
|
||||
}
|
||||
38
12-haskell-parser-combinators/flake.nix
Normal file
38
12-haskell-parser-combinators/flake.nix
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
# Builds a small Haskell project that focuses on parser combinators with
|
||||
# Megaparsec and a tiny command language.
|
||||
description = "A Haskell project for parser combinators";
|
||||
|
||||
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-parser" ./. { };
|
||||
checkedProject = pkgs.haskell.lib.doCheck project;
|
||||
in
|
||||
{
|
||||
packages.${system}.default = project;
|
||||
|
||||
apps.${system}.default = {
|
||||
type = "app";
|
||||
program = "${self.packages.${system}.default}/bin/mini-parser";
|
||||
meta.description = "Run the parser combinator example.";
|
||||
};
|
||||
|
||||
devShells.${system}.default = pkgs.mkShell {
|
||||
packages = [
|
||||
haskellPackages.ghc
|
||||
pkgs.cabal-install
|
||||
pkgs.haskell-language-server
|
||||
];
|
||||
};
|
||||
|
||||
checks.${system}.test-suite = checkedProject;
|
||||
};
|
||||
}
|
||||
30
12-haskell-parser-combinators/mini-parser.cabal
Normal file
30
12-haskell-parser-combinators/mini-parser.cabal
Normal file
@ -0,0 +1,30 @@
|
||||
cabal-version: 2.4
|
||||
name: mini-parser
|
||||
version: 0.1.0.0
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: MiniParser.Deploy
|
||||
hs-source-dirs: src
|
||||
build-depends:
|
||||
base >=4.14 && <5,
|
||||
megaparsec,
|
||||
text
|
||||
default-language: Haskell2010
|
||||
|
||||
executable mini-parser
|
||||
main-is: Main.hs
|
||||
hs-source-dirs: app
|
||||
build-depends:
|
||||
base >=4.14 && <5,
|
||||
mini-parser
|
||||
default-language: Haskell2010
|
||||
|
||||
test-suite mini-parser-test
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Main.hs
|
||||
hs-source-dirs: test
|
||||
build-depends:
|
||||
base >=4.14 && <5,
|
||||
mini-parser
|
||||
default-language: Haskell2010
|
||||
86
12-haskell-parser-combinators/src/MiniParser/Deploy.hs
Normal file
86
12-haskell-parser-combinators/src/MiniParser/Deploy.hs
Normal file
@ -0,0 +1,86 @@
|
||||
module MiniParser.Deploy where
|
||||
|
||||
import Control.Monad (void)
|
||||
import Data.Void (Void)
|
||||
import Text.Megaparsec
|
||||
( Parsec
|
||||
, choice
|
||||
, eof
|
||||
, errorBundlePretty
|
||||
, many
|
||||
, parse
|
||||
, sepBy1
|
||||
, some
|
||||
, (<|>)
|
||||
)
|
||||
import Text.Megaparsec.Char (alphaNumChar, char, space1, string)
|
||||
|
||||
data Environment
|
||||
= Staging
|
||||
| Production
|
||||
deriving (Eq, Show)
|
||||
|
||||
data DeployCommand = DeployCommand
|
||||
{ serviceName :: String
|
||||
, environment :: Environment
|
||||
, tags :: [String]
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
type Parser = Parsec Void String
|
||||
|
||||
environmentParser :: Parser Environment
|
||||
environmentParser =
|
||||
choice
|
||||
[ Production <$ string "production"
|
||||
, Staging <$ string "staging"
|
||||
]
|
||||
|
||||
identifierParser :: Parser String
|
||||
identifierParser = some (alphaNumChar <|> char '_' <|> char '-')
|
||||
|
||||
tagParser :: Parser [String]
|
||||
tagParser = string "tags=" *> (identifierParser `sepBy1` char ',')
|
||||
|
||||
deployCommandParser :: Parser DeployCommand
|
||||
deployCommandParser = do
|
||||
void (string "deploy")
|
||||
space1
|
||||
parsedService <- identifierParser
|
||||
space1
|
||||
parsedEnvironment <- environmentParser
|
||||
parsedTags <- many (space1 *> tagParser)
|
||||
eof
|
||||
pure
|
||||
DeployCommand
|
||||
{ serviceName = parsedService
|
||||
, environment = parsedEnvironment
|
||||
, tags = concat parsedTags
|
||||
}
|
||||
|
||||
parseDeployCommand :: String -> Either String DeployCommand
|
||||
parseDeployCommand input =
|
||||
case parse deployCommandParser "deploy-command" input of
|
||||
Left parseError -> Left (errorBundlePretty parseError)
|
||||
Right command -> Right command
|
||||
|
||||
renderCommand :: DeployCommand -> String
|
||||
renderCommand command =
|
||||
"deploy "
|
||||
++ serviceName command
|
||||
++ " to "
|
||||
++ renderEnvironment (environment command)
|
||||
++ renderTags (tags command)
|
||||
|
||||
renderEnvironment :: Environment -> String
|
||||
renderEnvironment Staging = "staging"
|
||||
renderEnvironment Production = "production"
|
||||
|
||||
renderTags :: [String] -> String
|
||||
renderTags [] = " with no tags"
|
||||
renderTags parsedTags = " with tags: " ++ commaSeparated parsedTags
|
||||
|
||||
commaSeparated :: [String] -> String
|
||||
commaSeparated [] = ""
|
||||
commaSeparated [singleItem] = singleItem
|
||||
commaSeparated (firstItem : remainingItems) = firstItem ++ ", " ++ commaSeparated remainingItems
|
||||
23
12-haskell-parser-combinators/test/Main.hs
Normal file
23
12-haskell-parser-combinators/test/Main.hs
Normal file
@ -0,0 +1,23 @@
|
||||
module Main where
|
||||
|
||||
import MiniParser.Deploy
|
||||
( DeployCommand (DeployCommand)
|
||||
, Environment (Production, Staging)
|
||||
, parseDeployCommand
|
||||
, renderCommand
|
||||
)
|
||||
import System.Exit (die)
|
||||
|
||||
main :: IO ()
|
||||
main =
|
||||
case
|
||||
( parseDeployCommand "deploy api production tags=blue,stable"
|
||||
, parseDeployCommand "deploy worker staging"
|
||||
, parseDeployCommand "bad input"
|
||||
) of
|
||||
( Right (DeployCommand "api" Production ["blue", "stable"])
|
||||
, Right parsedWorker
|
||||
, Left _
|
||||
) | renderCommand parsedWorker == "deploy worker to staging with no tags" ->
|
||||
putStrLn "test passed"
|
||||
_ -> die "unexpected parser result"
|
||||
51
notes/011-haskell-newtypes.md
Normal file
51
notes/011-haskell-newtypes.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Haskell Newtypes and Smart Constructors
|
||||
|
||||
This note covers `09-haskell-newtype/`, which models validated user input with `newtype`, record fields, and smart constructors.
|
||||
|
||||
---
|
||||
|
||||
## 1. Why `newtype` Matters
|
||||
|
||||
Plain `Text` values do not tell you what they represent. A user name and an email address could both be `Text`, even though they mean different
|
||||
things.
|
||||
|
||||
This example wraps those concepts explicitly:
|
||||
|
||||
- `UserName`,
|
||||
- `Email`, and
|
||||
- `Registration`.
|
||||
|
||||
That lets the rest of the code depend on validated domain types instead of raw input.
|
||||
|
||||
---
|
||||
|
||||
## 2. Smart Constructors
|
||||
|
||||
The module exposes constructor functions like `mkUserName` and `mkEmail`, which return `Either String ...`.
|
||||
|
||||
That is the main idea:
|
||||
|
||||
- invalid input is rejected at the boundary,
|
||||
- successful validation returns a domain type, and
|
||||
- the rest of the program works with trusted values.
|
||||
|
||||
This is a common intermediate Haskell pattern because it pushes validation close to the edge of the program.
|
||||
|
||||
---
|
||||
|
||||
## 3. Commands to Try
|
||||
|
||||
```bash
|
||||
cd 09-haskell-newtype
|
||||
|
||||
nix develop
|
||||
cabal run
|
||||
cabal run -- learner learner@example.com
|
||||
cabal test
|
||||
|
||||
nix build
|
||||
./result/bin/mini-registration learner learner@example.com
|
||||
|
||||
nix run . -- learner learner@example.com
|
||||
nix flake check
|
||||
```
|
||||
53
notes/012-haskell-effects.md
Normal file
53
notes/012-haskell-effects.md
Normal file
@ -0,0 +1,53 @@
|
||||
# Haskell Effects with ReaderT and Except
|
||||
|
||||
This note covers `10-haskell-effects/`, which models application logic with an environment, explicit errors, and a small effect stack.
|
||||
|
||||
---
|
||||
|
||||
## 1. What the Stack Represents
|
||||
|
||||
The example uses:
|
||||
|
||||
- `ReaderT Env` for read-only configuration, and
|
||||
- `Except AppError` for failures that belong to the domain.
|
||||
|
||||
That is an important intermediate step because it separates three things cleanly:
|
||||
|
||||
- configuration,
|
||||
- business logic, and
|
||||
- error handling.
|
||||
|
||||
---
|
||||
|
||||
## 2. Why the Functions Use Constraints
|
||||
|
||||
The library functions are written against `MonadReader Env` and `MonadError AppError` constraints rather than a concrete stack type.
|
||||
|
||||
That keeps the functions reusable. They say what capabilities they need, not exactly which monad stack must provide them.
|
||||
|
||||
The concrete stack still exists:
|
||||
|
||||
```haskell
|
||||
type App = ReaderT Env (Except AppError)
|
||||
```
|
||||
|
||||
But the function signatures stay more flexible and easier to test.
|
||||
|
||||
---
|
||||
|
||||
## 3. Commands to Try
|
||||
|
||||
```bash
|
||||
cd 10-haskell-effects
|
||||
|
||||
nix develop
|
||||
cabal run
|
||||
cabal run -- haskell
|
||||
cabal test
|
||||
|
||||
nix build
|
||||
./result/bin/mini-effects haskell
|
||||
|
||||
nix run . -- haskell
|
||||
nix flake check
|
||||
```
|
||||
48
notes/013-haskell-typeclasses.md
Normal file
48
notes/013-haskell-typeclasses.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Haskell Type Classes and Custom Instances
|
||||
|
||||
This note covers `11-haskell-typeclasses/`, which defines a custom type class and several instances that share one rendering interface.
|
||||
|
||||
---
|
||||
|
||||
## 1. Why Type Classes Matter
|
||||
|
||||
Type classes let you describe a capability independently from any one data type.
|
||||
|
||||
This example introduces:
|
||||
|
||||
- `Renderable` as a capability,
|
||||
- `Environment`, `BuildState`, and `Deployment` as domain types, and
|
||||
- `DeploymentReport` as a wrapper type for a whole collection.
|
||||
|
||||
Each type gets its own `Renderable` instance, but all of them can be used through the same `render` function.
|
||||
|
||||
---
|
||||
|
||||
## 2. Why the Instances Are Separate
|
||||
|
||||
Each instance decides how that type should appear:
|
||||
|
||||
- `Environment` renders short environment names,
|
||||
- `BuildState` renders status text, and
|
||||
- `Deployment` combines those smaller renderings into one message.
|
||||
|
||||
That shows the core value of type classes: behavior is attached per type, while the calling code can stay generic.
|
||||
|
||||
---
|
||||
|
||||
## 3. Commands to Try
|
||||
|
||||
```bash
|
||||
cd 11-haskell-typeclasses
|
||||
|
||||
nix develop
|
||||
cabal run
|
||||
cabal run -- production failed 2
|
||||
cabal test
|
||||
|
||||
nix build
|
||||
./result/bin/mini-render production failed 2
|
||||
|
||||
nix run . -- production failed 2
|
||||
nix flake check
|
||||
```
|
||||
42
notes/014-haskell-learning-path.md
Normal file
42
notes/014-haskell-learning-path.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Haskell Learning Path
|
||||
|
||||
This note links the Haskell examples in a suggested order from first project structure through intermediate language and application patterns.
|
||||
|
||||
---
|
||||
|
||||
## 1. Suggested Order
|
||||
|
||||
1. `05-haskell/`: a small Cabal library, executable, test suite, and dev shell
|
||||
2. `06-haskell-shellfor/`: a Haskell package set override and a `shellFor`-based dev shell
|
||||
3. `07-haskell-deps/`: external Haskell libraries through Cabal `build-depends`
|
||||
4. `08-haskell-adt/`: algebraic data types, records, and pattern matching
|
||||
5. `09-haskell-newtype/`: `newtype`, smart constructors, and validation
|
||||
6. `10-haskell-effects/`: `ReaderT`, `Except`, and constrained application logic
|
||||
7. `11-haskell-typeclasses/`: custom type classes and per-type instances
|
||||
8. `12-haskell-parser-combinators/`: parser combinators with Megaparsec
|
||||
|
||||
---
|
||||
|
||||
## 2. What to Focus on at Each Step
|
||||
|
||||
- `05-haskell/`: how a Cabal package becomes a flake package, app, dev shell, and check
|
||||
- `06-haskell-shellfor/`: when a Haskell-specific dev shell is more useful than a generic shell
|
||||
- `07-haskell-deps/`: why Cabal stays the source of truth for package dependencies
|
||||
- `08-haskell-adt/`: how to model a problem with constructors before writing behavior
|
||||
- `09-haskell-newtype/`: how to move validation to the boundary and protect the domain model
|
||||
- `10-haskell-effects/`: how to separate configuration, logic, and failures
|
||||
- `11-haskell-typeclasses/`: how to abstract shared behavior across several types
|
||||
- `12-haskell-parser-combinators/`: how to build a small language from reusable parser pieces
|
||||
|
||||
---
|
||||
|
||||
## 3. Related Notes
|
||||
|
||||
- `notes/007-haskell.md`
|
||||
- `notes/008-haskell-shellfor.md`
|
||||
- `notes/009-haskell-dependencies.md`
|
||||
- `notes/010-haskell-adts.md`
|
||||
- `notes/011-haskell-newtypes.md`
|
||||
- `notes/012-haskell-effects.md`
|
||||
- `notes/013-haskell-typeclasses.md`
|
||||
- `notes/015-haskell-parser-combinators.md`
|
||||
50
notes/015-haskell-parser-combinators.md
Normal file
50
notes/015-haskell-parser-combinators.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Haskell Parser Combinators
|
||||
|
||||
This note covers `12-haskell-parser-combinators/`, which parses a tiny deploy-command language with Megaparsec.
|
||||
|
||||
---
|
||||
|
||||
## 1. Why Parser Combinators Matter
|
||||
|
||||
Parser combinators let you build a parser out of smaller parsers:
|
||||
|
||||
- parse one token,
|
||||
- combine it with another parser,
|
||||
- choose between alternatives, and
|
||||
- repeat parts that can appear many times.
|
||||
|
||||
That makes them a very Haskell-shaped tool: you write small functions, compose them, and end up with a parser for a whole language.
|
||||
|
||||
---
|
||||
|
||||
## 2. What This Example Shows
|
||||
|
||||
The example defines parsers for:
|
||||
|
||||
- the environment,
|
||||
- identifiers,
|
||||
- tag lists, and
|
||||
- the full deploy command.
|
||||
|
||||
The full parser then composes those pieces with sequencing, `choice`, `many`, and `sepBy1`.
|
||||
|
||||
That is the main intermediate idea: treat a parser as a reusable value, not a one-off block of string handling code.
|
||||
|
||||
---
|
||||
|
||||
## 3. Commands to Try
|
||||
|
||||
```bash
|
||||
cd 12-haskell-parser-combinators
|
||||
|
||||
nix develop
|
||||
cabal run
|
||||
cabal run -- deploy api production tags=blue,stable
|
||||
cabal test
|
||||
|
||||
nix build
|
||||
./result/bin/mini-parser deploy api production tags=blue,stable
|
||||
|
||||
nix run . -- deploy api production tags=blue,stable
|
||||
nix flake check
|
||||
```
|
||||
Loading…
x
Reference in New Issue
Block a user