content: dead-simple-haskell

This commit is contained in:
Maciej Jur 2024-02-04 01:00:25 +01:00
parent 588a0d1480
commit 2ff3f15388
Signed by: kamov
GPG key ID: 191CBFF5F72ECAFD
8 changed files with 1340 additions and 9 deletions

3
hie.yaml Normal file
View file

@ -0,0 +1,3 @@
cradle:
direct:
arguments: ["-pgmL", "markdown-unlit"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 KiB

View file

@ -26,7 +26,7 @@ const ESCAPED_CHARS: {[key: string]: string} = {
const REGEX_HL_LINES = /\[([\s\d,|-]*)\]/;
function transformCode(node: CodeNode, index: number, parent: Parent) {
if (!node.meta || !REGEX_HL_LINES.test(node.meta)) return;
const langtag = node.lang ? ` class="${node.lang}" ` : ''
const numbers = node.meta.match(REGEX_HL_LINES)![1];
const escaped = node.value.replace(/[&<>"']/g, match => ESCAPED_CHARS[match] || '');
@ -55,16 +55,21 @@ const SPLIT_H = /\n-----\n/;
const SPLIT_V = /\n---\n/;
function wrapSection(content: string): string {
return `<section>${content}</section>`;
function wrapSection(animate: boolean, id: number) {
return (content: string): string => {
return animate
? `<section data-auto-animate data-auto-animate-id="${id}">${content}</section>`
: `<section>${content}</section>`;
}
}
export function render(text: string): string {
export function render(text: string, animate = false): string {
const wrapOuter = wrapSection(false, 0);
return text
.split(SPLIT_H)
.map(stacks => stacks.split(SPLIT_V).map(slide => String(renderer.processSync(slide))))
.map(stack => (stack.length > 1)
? wrapSection(stack.map(wrapSection).join(''))
: wrapSection(stack[0]))
.map((stack, i) => (stack.length > 1)
? wrapOuter(stack.map(wrapSection(animate, i)).join(''))
: wrapOuter(stack[0]))
.join('');
}

View file

@ -15,6 +15,7 @@ export const collections = {
title: z.string(),
date: z.date(),
tags: z.array(z.string()).optional(),
animate: z.boolean().optional(),
})
}),
wiki: defineCollection({

View file

@ -0,0 +1,658 @@
---
title: Dead simple Haskell
date: 2024-01-11T21:21:41.263Z
slug: dead-simple-haskell
animate: true
---
# Dead simple Haskell
![Haskell logo](/static/content/slides/haskell-molecules/haskell.png)
-----
## Haskell molecules
-----
### Atoms
![atoms](/static/content/slides/haskell-molecules/atoms.png)
---
![atoms](/static/content/slides/haskell-molecules/atoms.png)
```haskell
data H = H -- hydrogen
data O = O -- oxygen
data C = C -- carbon
```
-----
### Molecules
![Atoms](/static/content/slides/haskell-molecules/molecules.png)
---
![Atoms](/static/content/slides/haskell-molecules/molecules.png)
```haskell
type H₂O = (H, O, H) -- water
type O₂ = (O, O) -- oxygen (gas)
type CO₂ = (O, C, O) -- carbon dioxide
```
-----
### Reactions
![Magic](/static/content/slides/haskell-molecules/magic.png)
---
![More magic](/static/content/slides/haskell-molecules/more-magic.png)
---
![More magic](/static/content/slides/haskell-molecules/more-magic.png)
```haskell ignore
makeWater :: H -> H -> O -> H₂O
```
```haskell ignore
makeOxygen :: O -> O -> O₂
```
```haskell ignore
burnOxygen :: C -> O₂ -> CO₂
```
---
```haskell
makeWater :: H -> H -> O -> H₂O
```
```haskell
makeWater h1 h2 o = (h1, o, h2)
```
~
```haskell
makeOxygen :: O -> O -> O₂
```
```haskell
makeOxygen o1 o2 = (o1, o2)
```
~
```haskell
burnOxygen :: C -> O₂ -> CO₂
```
```haskell
burnOxygen c (o1, o2) = (o1, c, o2)
```
-----
### `.` `$`
![Plumbing](/static/content/slides/haskell-molecules/plumbing.png)
---
![Partial application](/static/content/slides/haskell-molecules/partial.png)
```haskell ignore
λ> :type makeOxygen
makeOxygen :: O -> O -> O₂
```
```haskell ignore
λ> :type makeOxygen O
makeOxygen O :: O -> O₂
```
```haskell ignore
λ> :type makeOxygen O O
makeOxygen O O :: O₂
```
---
![Combustion](/static/content/slides/haskell-molecules/combustion.png)
```haskell ignore
λ> :type burnOxygen
burnOxygen :: C -> O₂ -> CO₂
```
```haskell ignore
λ> :type burnOxygen C
burnOxygen C :: O₂ -> CO₂
```
```haskell ignore
λ> :type burnOxygen C (O, O)
burnOxygen C O₂ :: CO₂
```
---
![Two functions](/static/content/slides/haskell-molecules/two-fs.png)
```haskell
f1 :: O -> O₂
f1 = makeOxygen O
f2 :: O₂ -> CO₂
f2 = burnOxygen C
```
---
![Composition](/static/content/slides/haskell-molecules/composition.png)
```haskell
f3 = f2 . f1
```
---
![Composition2](/static/content/slides/haskell-molecules/composition2.png)
```haskell
f3 :: O -> CO₂
```
```haskell ignore
f1 :: { O -> O₂ }
f2 :: [ O₂ -> CO₂ ]
f3 :: {O -> [ O₂ } -> CO₂ ]
f3 :: O -> CO₂
```
---
```haskell ignore
f3' o = f2 . f1 o
```
```scala
Diagnostics:
1. • Couldn't match type: (O, O)
with: a -> O₂
Expected: a -> O₂
Actual: O₂
• Possible cause: f1 is applied to too many arguments
In the second argument of (.), namely f1 o
In the expression: f2 . f1 o
In an equation for f3': f3' o = f2 . f1 o
• Relevant bindings include
f3' :: O -> a -> CO₂
(bound at Script.hs:112:1) [-Wdeferred-type-errors]
```
---
![Composition3](/static/content/slides/haskell-molecules/composition3.png)
```haskell ignore
f3' o = f2 . f1 o
```
---
![Funnel](/static/content/slides/haskell-molecules/funnel.png)
```haskell ignore
f3' o = f2 . f1 $ o
-- OR
f3' o = f2 $ f1 o
```
---
```haskell ignore
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)
infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x
```
-----
## Isotopes
![Heavy water](/static/content/slides/haskell-molecules/heavy-water.gif)
---
![Isotopes](/static/content/slides/haskell-molecules/isotopes.png)
---
```haskell
data H'
= H¹-- protium
| H²-- deuterium
| H³-- tritium
type H2O' = (H', O, H') -- water
```
```haskell
makeWater' :: H' -> H' -> O -> H2O'
makeWater' h1 h2 o = (h1, o, h2)
```
---
### Algebra
How many values inhibit each of these?
Type | Possibilities
---------- | -------------
`()` | ?
`O` | ?
`H'` | ?
`(H', H')` | ?
---
`()`
---
`()`
1
`()`
---
`O`
---
`O`
1
`O`
---
`H'`
---
`H'`
3
`H¹` `H²` `H³`
---
`(H', H')`
---
`(H', H')`
9
`(H¹, H¹)` `(H¹, H²)` `(H¹, H³)`
`(H², H¹)` `(H², H²)` `(H², H³)`
`(H³, H¹)` `(H³, H²)` `(H³, H³)`
---
`(H', Bool)`
---
`(H', Bool)`
6
`(H¹, True)` `(H¹, False)`
`(H², True)` `(H², False)`
`(H³, True)` `(H³, False)`
---
Type | Inhibitants
---------- | -----------
`()` | 1
`Bool` | 2
`H'` | 3
`A` | a
`B` | b
`(A, B)` | a × b
`A` \| `B` | a + b
---
Product types Π
Type | Inhibitants
---------- | -----------
() | 1
Bool | 2
((), Bool) | 1 × 2 = 2
(H', Bool) | 3 × 2 = 6
```haskell ignore
data Pair a b = Pair a b
```
---
```haskell ignore
type Pair a b = (a, b)
```
```haskell ignore
data Pair a b = Pair a b
```
```haskell ignore
data Pair a b = Pair
{ fieldA :: a
, fieldB :: b
}
```
---
Sum types Σ
Type | Inhibitants
---------- | -----------
() | 1
Bool | 2
() | Bool | 1 + 2 = 3
H' | Bool | 3 + 2 = 5
```haskell ignore
data Either a b = Left a | Right b
```
---
Why not both?
---
Why not both?
```haskell ignore
data Crazy a b
= CrazyA a
| CrazyB b
| Both a b
| Neither
```
---
```haskell ignore
data Crazy a b
= CrazyA a
| CrazyB b
| Both a b
| Neither
```
Member | Inhibitants
---------- | -----------
`CrazyA a` | a
`CrazyB b` | b
`Both a b` | a × b
`Neither` | 1
Σ | a + b + a × b + 1
---
Can there be a "0"?
---
Can there be a "0"?
Yes
```haskell ignore
Crazy Void ()
= CrazyA Void
| CrazyB ()
| Both Void ()
| Neither
```
Σ = 0 + 1 + 0 × 1 + 1 = 2
-----
### Traits
![Neon colors](/static/content/slides/haskell-molecules/neon.png)
---
Element name | Color
------------ | ------
Helium | orange
Neon | red
Argon | lavender
Krypton | white
Xenon | blue
Radon | red
---
```haskell
data He = He -- Helium
data Ne = Ne -- Neon
data Ar = Ar -- Argon
data Kr = Kr -- Krypton
data Xe = Xe -- Xenon
data Rn = Rn -- Radon
```
---
How to convert noble gases to color?
```haskell ignore
toColor :: ? -> String
```
---
What about allowing everything in?
```haskell ignore
toColor :: a -> String
toColor a
| a == He = "orange"
| a == Ne = "red"
| otherwise = undefined
-- ???
_ = toColor "anything in"
_ = toColor 1234
```
---
What about treating noble gases as a sum type?
```haskell ignore
data Noble
= He
| Ne
| Ar
| Kr
| Xe
| Rn
```
---
`a -> String`
^
???
^
`Noble -> String`
---
```haskell
class Noble a where
toColor :: a -> String
```
~
```haskell
instance Noble He where
toColor _ = "orange"
instance Noble Ne where
toColor _ = "red"
instance Noble Ar where
toColor _ = "lavender"
```
---
Works:
```haskell ignore
λ> toColor He
"orange"
λ> toColor Ne
"red"
```
Doesn't work:
```haskell ignore
λ> toColor C
<interactive>:6:1: error:
• No instance for (Noble C) arising from a use of toColor
• In the expression: toColor C
In an equation for it: it = toColor C
```
---
```haskell
mixNoble :: (Noble n1, Noble n2) => n1 -> n2 -> String
mixNoble n1 n2 = toColor n1 <> "-" <> toColor n2
```
```haskell ignore
λ> mixNoble He Ne
"orange-red"
```
-----
## Shrodinger's cat
(A change of topic)
---
```haskell
data Box a
= Has a
| Empty
```
---
```haskell
class Mappable box where
map' :: box a -> box b
```
---
```haskell
class Appliable box where
wrap :: a -> box a
apply' :: box (a -> b) -> box a -> box b
```
---
```haskell
class Chainable box where
chain :: box a -> (a -> box b) -> box b
```
-----
In fact it all already exists.
![This was all dream](/static/content/slides/haskell-molecules/_cave.jpg)
---
What's a `Mappable`?
---
![It's a Functor](/static/content/slides/haskell-molecules/scoobydoo.jpg)
---
What's a `Appliable`?
---
![It's a Functor](/static/content/slides/haskell-molecules/scoobydoo.jpg)
---
What's a `Chainable`?
---
![It's a Functor](/static/content/slides/haskell-molecules/scoobydoo.jpg)
---
## Fun with monadic parsing
---
## More here:
- **Learn You a Haskell for Great Good!** by Miran Lipovaca
- **Programming in Haskell - 2nd Edition** by Graham Hutton
- **Effective Haskell** by Rebecca Skinner

View file

@ -0,0 +1,658 @@
---
title: Dead simple Haskell
date: 2024-01-11T21:21:41.263Z
slug: dead-simple-haskell
animate: true
---
# Dead simple Haskell
![Haskell logo](/static/content/slides/haskell-molecules/haskell.png)
-----
## Haskell molecules
-----
### Atoms
![atoms](/static/content/slides/haskell-molecules/atoms.png)
---
![atoms](/static/content/slides/haskell-molecules/atoms.png)
```haskell
data H = H -- hydrogen
data O = O -- oxygen
data C = C -- carbon
```
-----
### Molecules
![Atoms](/static/content/slides/haskell-molecules/molecules.png)
---
![Atoms](/static/content/slides/haskell-molecules/molecules.png)
```haskell
type H₂O = (H, O, H) -- water
type O₂ = (O, O) -- oxygen (gas)
type CO₂ = (O, C, O) -- carbon dioxide
```
-----
### Reactions
![Magic](/static/content/slides/haskell-molecules/magic.png)
---
![More magic](/static/content/slides/haskell-molecules/more-magic.png)
---
![More magic](/static/content/slides/haskell-molecules/more-magic.png)
```haskell ignore
makeWater :: H -> H -> O -> H₂O
```
```haskell ignore
makeOxygen :: O -> O -> O₂
```
```haskell ignore
burnOxygen :: C -> O₂ -> CO₂
```
---
```haskell
makeWater :: H -> H -> O -> H₂O
```
```haskell
makeWater h1 h2 o = (h1, o, h2)
```
~
```haskell
makeOxygen :: O -> O -> O₂
```
```haskell
makeOxygen o1 o2 = (o1, o2)
```
~
```haskell
burnOxygen :: C -> O₂ -> CO₂
```
```haskell
burnOxygen c (o1, o2) = (o1, c, o2)
```
-----
### `.` `$`
![Plumbing](/static/content/slides/haskell-molecules/plumbing.png)
---
![Partial application](/static/content/slides/haskell-molecules/partial.png)
```haskell ignore
λ> :type makeOxygen
makeOxygen :: O -> O -> O₂
```
```haskell ignore
λ> :type makeOxygen O
makeOxygen O :: O -> O₂
```
```haskell ignore
λ> :type makeOxygen O O
makeOxygen O O :: O₂
```
---
![Combustion](/static/content/slides/haskell-molecules/combustion.png)
```haskell ignore
λ> :type burnOxygen
burnOxygen :: C -> O₂ -> CO₂
```
```haskell ignore
λ> :type burnOxygen C
burnOxygen C :: O₂ -> CO₂
```
```haskell ignore
λ> :type burnOxygen C (O, O)
burnOxygen C O₂ :: CO₂
```
---
![Two functions](/static/content/slides/haskell-molecules/two-fs.png)
```haskell
f1 :: O -> O₂
f1 = makeOxygen O
f2 :: O₂ -> CO₂
f2 = burnOxygen C
```
---
![Composition](/static/content/slides/haskell-molecules/composition.png)
```haskell
f3 = f2 . f1
```
---
![Composition2](/static/content/slides/haskell-molecules/composition2.png)
```haskell
f3 :: O -> CO₂
```
```haskell ignore
f1 :: { O -> O₂ }
f2 :: [ O₂ -> CO₂ ]
f3 :: {O -> [ O₂ } -> CO₂ ]
f3 :: O -> CO₂
```
---
```haskell ignore
f3' o = f2 . f1 o
```
```scala
Diagnostics:
1. • Couldn't match type: (O, O)
with: a -> O₂
Expected: a -> O₂
Actual: O₂
• Possible cause: f1 is applied to too many arguments
In the second argument of (.), namely f1 o
In the expression: f2 . f1 o
In an equation for f3': f3' o = f2 . f1 o
• Relevant bindings include
f3' :: O -> a -> CO₂
(bound at Script.hs:112:1) [-Wdeferred-type-errors]
```
---
![Composition3](/static/content/slides/haskell-molecules/composition3.png)
```haskell ignore
f3' o = f2 . f1 o
```
---
![Funnel](/static/content/slides/haskell-molecules/funnel.png)
```haskell ignore
f3' o = f2 . f1 $ o
-- OR
f3' o = f2 $ f1 o
```
---
```haskell ignore
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)
infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x
```
-----
## Isotopes
![Heavy water](/static/content/slides/haskell-molecules/heavy-water.gif)
---
![Isotopes](/static/content/slides/haskell-molecules/isotopes.png)
---
```haskell
data H'
= H¹-- protium
| H²-- deuterium
| H³-- tritium
type H2O' = (H', O, H') -- water
```
```haskell
makeWater' :: H' -> H' -> O -> H2O'
makeWater' h1 h2 o = (h1, o, h2)
```
---
### Algebra
How many values inhibit each of these?
Type | Possibilities
---------- | -------------
`()` | ?
`O` | ?
`H'` | ?
`(H', H')` | ?
---
`()`
---
`()`
1
`()`
---
`O`
---
`O`
1
`O`
---
`H'`
---
`H'`
3
`H¹` `H²` `H³`
---
`(H', H')`
---
`(H', H')`
9
`(H¹, H¹)` `(H¹, H²)` `(H¹, H³)`
`(H², H¹)` `(H², H²)` `(H², H³)`
`(H³, H¹)` `(H³, H²)` `(H³, H³)`
---
`(H', Bool)`
---
`(H', Bool)`
6
`(H¹, True)` `(H¹, False)`
`(H², True)` `(H², False)`
`(H³, True)` `(H³, False)`
---
Type | Inhibitants
---------- | -----------
`()` | 1
`Bool` | 2
`H'` | 3
`A` | a
`B` | b
`(A, B)` | a × b
`A` \| `B` | a + b
---
Product types Π
Type | Inhibitants
---------- | -----------
() | 1
Bool | 2
((), Bool) | 1 × 2 = 2
(H', Bool) | 3 × 2 = 6
```haskell ignore
data Pair a b = Pair a b
```
---
```haskell ignore
type Pair a b = (a, b)
```
```haskell ignore
data Pair a b = Pair a b
```
```haskell ignore
data Pair a b = Pair
{ fieldA :: a
, fieldB :: b
}
```
---
Sum types Σ
Type | Inhibitants
---------- | -----------
() | 1
Bool | 2
() | Bool | 1 + 2 = 3
H' | Bool | 3 + 2 = 5
```haskell ignore
data Either a b = Left a | Right b
```
---
Why not both?
---
Why not both?
```haskell ignore
data Crazy a b
= CrazyA a
| CrazyB b
| Both a b
| Neither
```
---
```haskell ignore
data Crazy a b
= CrazyA a
| CrazyB b
| Both a b
| Neither
```
Member | Inhibitants
---------- | -----------
`CrazyA a` | a
`CrazyB b` | b
`Both a b` | a × b
`Neither` | 1
Σ | a + b + a × b + 1
---
Can there be a "0"?
---
Can there be a "0"?
Yes
```haskell ignore
Crazy Void ()
= CrazyA Void
| CrazyB ()
| Both Void ()
| Neither
```
Σ = 0 + 1 + 0 × 1 + 1 = 2
-----
### Traits
![Neon colors](/static/content/slides/haskell-molecules/neon.png)
---
Element name | Color
------------ | ------
Helium | orange
Neon | red
Argon | lavender
Krypton | white
Xenon | blue
Radon | red
---
```haskell
data He = He -- Helium
data Ne = Ne -- Neon
data Ar = Ar -- Argon
data Kr = Kr -- Krypton
data Xe = Xe -- Xenon
data Rn = Rn -- Radon
```
---
How to convert noble gases to color?
```haskell ignore
toColor :: ? -> String
```
---
What about allowing everything in?
```haskell ignore
toColor :: a -> String
toColor a
| a == He = "orange"
| a == Ne = "red"
| otherwise = undefined
-- ???
_ = toColor "anything in"
_ = toColor 1234
```
---
What about treating noble gases as a sum type?
```haskell ignore
data Noble
= He
| Ne
| Ar
| Kr
| Xe
| Rn
```
---
`a -> String`
^
???
^
`Noble -> String`
---
```haskell
class Noble a where
toColor :: a -> String
```
~
```haskell
instance Noble He where
toColor _ = "orange"
instance Noble Ne where
toColor _ = "red"
instance Noble Ar where
toColor _ = "lavender"
```
---
Works:
```haskell ignore
λ> toColor He
"orange"
λ> toColor Ne
"red"
```
Doesn't work:
```haskell ignore
λ> toColor C
<interactive>:6:1: error:
• No instance for (Noble C) arising from a use of toColor
• In the expression: toColor C
In an equation for it: it = toColor C
```
---
```haskell
mixNoble :: (Noble n1, Noble n2) => n1 -> n2 -> String
mixNoble n1 n2 = toColor n1 <> "-" <> toColor n2
```
```haskell ignore
λ> mixNoble He Ne
"orange-red"
```
-----
## Shrodinger's cat
(A change of topic)
---
```haskell
data Box a
= Has a
| Empty
```
---
```haskell
class Mappable box where
map' :: box a -> box b
```
---
```haskell
class Appliable box where
wrap :: a -> box a
apply' :: box (a -> b) -> box a -> box b
```
---
```haskell
class Chainable box where
chain :: box a -> (a -> box b) -> box b
```
-----
In fact it all already exists.
![This was all dream](/static/content/slides/haskell-molecules/_cave.jpg)
---
What's a `Mappable`?
---
![It's a Functor](/static/content/slides/haskell-molecules/scoobydoo.jpg)
---
What's a `Appliable`?
---
![It's a Functor](/static/content/slides/haskell-molecules/scoobydoo.jpg)
---
What's a `Chainable`?
---
![It's a Functor](/static/content/slides/haskell-molecules/scoobydoo.jpg)
---
## Fun with monadic parsing
---
## More here:
- **Learn You a Haskell for Great Good!** by Miran Lipovaca
- **Programming in Haskell - 2nd Edition** by Graham Hutton
- **Effective Haskell** by Rebecca Skinner

View file

@ -9,11 +9,16 @@ import { render } from '../../assets/reveal-ssr/markdown';
export async function getStaticPaths () {
return (await getCollection('slides'))
.map(entry => ({params: {slug: entry.slug}, props: {entry}}));
.map(entry => ({
params: { slug: entry.slug },
props: { entry }
}));
}
const { entry } = Astro.props;
const slides = render(entry.body);
const animate = entry.data.animate;
const slides = render(entry.body, animate);
---
<Base>
@ -26,6 +31,7 @@ const slides = render(entry.body);
Reveal.initialize({
hash: true,
slideNumber: true,
plugins: [
Highlight,
]