feat(TOC): Table of Contents
post(typoclassopedia): alternative formulations for Applicative
This commit is contained in:
parent
c4d343b3fd
commit
ae6b5b2be7
BIN
Resume.pdf
BIN
Resume.pdf
Binary file not shown.
@ -32,3 +32,6 @@ collections:
|
|||||||
output: true
|
output: true
|
||||||
permalink: /travel/:path/
|
permalink: /travel/:path/
|
||||||
path: /travel
|
path: /travel
|
||||||
|
|
||||||
|
contentsLabel: "Table of Contents"
|
||||||
|
showToggleButton: true
|
||||||
|
@ -10,7 +10,11 @@
|
|||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<h1 class="page-heading"></h1>
|
<h1 class="page-heading"></h1>
|
||||||
|
{% if page.toc %}
|
||||||
|
{{ content | toc_generate }}
|
||||||
|
{% else %}
|
||||||
{{ content }}
|
{{ content }}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
132
_plugins/tocGenerator.rb
Normal file
132
_plugins/tocGenerator.rb
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
require 'nokogiri'
|
||||||
|
|
||||||
|
module Jekyll
|
||||||
|
|
||||||
|
module TOCGenerator
|
||||||
|
|
||||||
|
TOGGLE_HTML = '<h2>%1</h2>'
|
||||||
|
TOC_CONTAINER_HTML = '<div id="toc-container"><table class="toc" id="toc"><tbody><tr><td>%1%2<ul>%3</ul></td></tr></tbody></table></div>'
|
||||||
|
HIDE_HTML = '<input type="checkbox" id="toctogglelink" href="#" checked></input><span></span>'
|
||||||
|
|
||||||
|
def toc_generate(html)
|
||||||
|
# No Toc can be specified on every single page
|
||||||
|
# For example the index page has no table of contents
|
||||||
|
return html if (@context.environments.first["page"]["noToc"] || false)
|
||||||
|
|
||||||
|
config = @context.registers[:site].config
|
||||||
|
|
||||||
|
# Minimum number of items needed to show TOC, default 0 (0 means no minimum)
|
||||||
|
min_items_to_show_toc = config["minItemsToShowToc"] || 0
|
||||||
|
|
||||||
|
anchor_prefix = config["anchorPrefix"] || 'tocAnchor-'
|
||||||
|
|
||||||
|
# better for traditional page seo, commonlly use h1 as title
|
||||||
|
toc_top_tag = config["tocTopTag"] || 'h1'
|
||||||
|
toc_top_tag = toc_top_tag.gsub(/h/, '').to_i
|
||||||
|
|
||||||
|
toc_top_tag = 5 if toc_top_tag > 5
|
||||||
|
|
||||||
|
toc_sec_tag = toc_top_tag + 1
|
||||||
|
toc_top_tag = "h#{toc_top_tag}"
|
||||||
|
toc_sec_tag = "h#{toc_sec_tag}"
|
||||||
|
|
||||||
|
|
||||||
|
# Text labels
|
||||||
|
contents_label = config["contentsLabel"] || 'Contents'
|
||||||
|
hide_label = config["hideLabel"] || 'hide'
|
||||||
|
# show_label = config["showLabel"] || 'show' # unused
|
||||||
|
show_toggle_button = config["showToggleButton"]
|
||||||
|
|
||||||
|
toc_html = ''
|
||||||
|
toc_level = 1
|
||||||
|
toc_section = 1
|
||||||
|
item_number = 1
|
||||||
|
level_html = ''
|
||||||
|
|
||||||
|
doc = Nokogiri::HTML(html)
|
||||||
|
|
||||||
|
# Find H1 tag and all its H2 siblings until next H1
|
||||||
|
doc.css(toc_top_tag).each do |tag|
|
||||||
|
# TODO This XPATH expression can greatly improved
|
||||||
|
ct = tag.xpath("count(following-sibling::#{toc_top_tag})")
|
||||||
|
sects = tag.xpath("following-sibling::#{toc_sec_tag}[count(following-sibling::#{toc_top_tag})=#{ct}]")
|
||||||
|
|
||||||
|
level_html = ''
|
||||||
|
inner_section = 0
|
||||||
|
|
||||||
|
sects.each do |sect|
|
||||||
|
inner_section += 1
|
||||||
|
anchor_id = [
|
||||||
|
anchor_prefix, toc_level, '-', toc_section, '-',
|
||||||
|
inner_section
|
||||||
|
].map(&:to_s).join ''
|
||||||
|
|
||||||
|
sect['id'] = "#{anchor_id}"
|
||||||
|
|
||||||
|
level_html += create_level_html(anchor_id,
|
||||||
|
toc_level + 1,
|
||||||
|
toc_section + inner_section,
|
||||||
|
item_number.to_s + '.' + inner_section.to_s,
|
||||||
|
sect.text,
|
||||||
|
'')
|
||||||
|
end
|
||||||
|
|
||||||
|
level_html = '<ul>' + level_html + '</ul>' if level_html.length > 0
|
||||||
|
|
||||||
|
anchor_id = anchor_prefix + toc_level.to_s + '-' + toc_section.to_s
|
||||||
|
tag['id'] = "#{anchor_id}"
|
||||||
|
|
||||||
|
toc_html += create_level_html(anchor_id,
|
||||||
|
toc_level,
|
||||||
|
toc_section,
|
||||||
|
item_number,
|
||||||
|
tag.text,
|
||||||
|
level_html)
|
||||||
|
|
||||||
|
toc_section += 1 + inner_section
|
||||||
|
item_number += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# for convenience item_number starts from 1
|
||||||
|
# so we decrement it to obtain the index count
|
||||||
|
toc_index_count = item_number - 1
|
||||||
|
|
||||||
|
return html unless toc_html.length > 0
|
||||||
|
|
||||||
|
hide_html = ''
|
||||||
|
hide_html = HIDE_HTML.gsub('%1', hide_label) if (show_toggle_button)
|
||||||
|
|
||||||
|
if min_items_to_show_toc <= toc_index_count
|
||||||
|
replaced_toggle_html = TOGGLE_HTML
|
||||||
|
.gsub('%1', contents_label)
|
||||||
|
|
||||||
|
toc_table = TOC_CONTAINER_HTML
|
||||||
|
.gsub('%1', replaced_toggle_html)
|
||||||
|
.gsub('%2', hide_html)
|
||||||
|
.gsub('%3', toc_html)
|
||||||
|
|
||||||
|
doc.css('.post-header').after(toc_table)
|
||||||
|
end
|
||||||
|
|
||||||
|
doc.css('body').children.to_xhtml
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_level_html(anchor_id, toc_level, toc_section, tocNumber, tocText, tocInner)
|
||||||
|
link = '<a href="#%1"><span class="tocnumber">%2</span> <span class="toctext">%3</span></a>%4'
|
||||||
|
.gsub('%1', anchor_id.to_s)
|
||||||
|
.gsub('%2', tocNumber.to_s)
|
||||||
|
.gsub('%3', tocText)
|
||||||
|
.gsub('%4', tocInner ? tocInner : '')
|
||||||
|
'<li class="toc_level-%1 toc_section-%2">%3</li>'
|
||||||
|
.gsub('%1', toc_level.to_s)
|
||||||
|
.gsub('%2', toc_section.to_s)
|
||||||
|
.gsub('%3', link)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
Liquid::Template.register_filter(Jekyll::TOCGenerator)
|
@ -5,9 +5,10 @@ date: 2017-09-27
|
|||||||
permalink: typoclassopedia-exercise-solutions/
|
permalink: typoclassopedia-exercise-solutions/
|
||||||
categories: programming
|
categories: programming
|
||||||
math: true
|
math: true
|
||||||
|
toc: true
|
||||||
---
|
---
|
||||||
|
|
||||||
I wanted to get proficient in Haskell so I decided to follow [An [Essential] Haskell Reading List](http://www.stephendiehl.com/posts/essential_haskell.html), there I stumbled upon [Typoclassopedia](https://wiki.haskell.org/Typeclassopedia), while the material is great, I couldn't find solutions for the exercises to check against, so I decided I would write my own and hopefully the solutions would get fixed in case I have gone wrong by others. So if you think a solution is wrong, let me know in the comments!
|
I wanted to get proficient in Haskell so I decided to follow [An [Essential] Haskell Reading List](http://www.stephendiehl.com/posts/essential_haskell.html). There I stumbled upon [Typoclassopedia](https://wiki.haskell.org/Typeclassopedia), while the material is great, I couldn't find solutions for the exercises to check against, so I decided I would write my own and hopefully the solutions would get fixed in case I have gone wrong by others. So if you think a solution is wrong, let me know in the comments!
|
||||||
|
|
||||||
In each section below, I left some reference material for the exercises and then the solutions.
|
In each section below, I left some reference material for the exercises and then the solutions.
|
||||||
|
|
||||||
@ -347,3 +348,229 @@ You can check the type of `(flip ($) f) . (flip ($))` is something like this (de
|
|||||||
```
|
```
|
||||||
|
|
||||||
Also see [this question on Stack Overflow](https://stackoverflow.com/questions/46503793/applicative-prove-pure-f-x-pure-flip-x-pure-f/46505868#46505868) which includes alternative proofs.
|
Also see [this question on Stack Overflow](https://stackoverflow.com/questions/46503793/applicative-prove-pure-f-x-pure-flip-x-pure-f/46505868#46505868) which includes alternative proofs.
|
||||||
|
|
||||||
|
## Instances
|
||||||
|
|
||||||
|
Applicative instance of lists as a collection of values:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
newtype ZipList a = ZipList { getZipList :: [a] }
|
||||||
|
|
||||||
|
instance Applicative ZipList where
|
||||||
|
pure :: a -> ZipList a
|
||||||
|
pure = undefined -- exercise
|
||||||
|
|
||||||
|
(<*>) :: ZipList (a -> b) -> ZipList a -> ZipList b
|
||||||
|
(ZipList gs) <*> (ZipList xs) = ZipList (zipWith ($) gs xs)
|
||||||
|
```
|
||||||
|
|
||||||
|
Applicative instance of lists as a non-deterministic computation context:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
instance Applicative [] where
|
||||||
|
pure :: a -> [a]
|
||||||
|
pure x = [x]
|
||||||
|
|
||||||
|
(<*>) :: [a -> b] -> [a] -> [b]
|
||||||
|
gs <*> xs = [ g x | g <- gs, x <- xs ]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exercises
|
||||||
|
|
||||||
|
1. Implement an instance of `Applicative` for `Maybe`.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
instance Applicative (Maybe a) where
|
||||||
|
pure :: a -> Maybe a
|
||||||
|
pure x = Just x
|
||||||
|
|
||||||
|
(<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
|
||||||
|
_ <*> Nothing = Nothing
|
||||||
|
Nothing <*> _ = Nothing
|
||||||
|
(Just f) <*> (Just x) = Just (f x)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Determine the correct definition of `pure` for the `ZipList` instance of `Applicative`—there is only one implementation that satisfies the law relating `pure` and `(<*>)`.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
newtype ZipList a = ZipList { getZipList :: [a] }
|
||||||
|
|
||||||
|
instance Functor ZipList where
|
||||||
|
fmap f (ZipList list) = ZipList { getZipList = fmap f list }
|
||||||
|
|
||||||
|
instance Applicative ZipList where
|
||||||
|
pure = ZipList . pure
|
||||||
|
|
||||||
|
(ZipList gs) <*> (ZipList xs) = ZipList (zipWith ($) gs xs)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check the Applicative laws for this implementation.
|
||||||
|
|
||||||
|
## Utility functions
|
||||||
|
|
||||||
|
### Exercises
|
||||||
|
|
||||||
|
1. Implement a function
|
||||||
|
`sequenceAL :: Applicative f => [f a] -> f [a]`
|
||||||
|
There is a generalized version of this, `sequenceA`, which works for any `Traversable` (see the later section on `Traversable`), but implementing this version specialized to lists is a good exercise.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
createList = replicate 1
|
||||||
|
|
||||||
|
sequenceAL :: Applicative f => [f a] -> f [a]
|
||||||
|
sequenceAL = foldr (\x b -> ((++) . createList <$> x) <*> b) (pure [])
|
||||||
|
```
|
||||||
|
|
||||||
|
Explanation:
|
||||||
|
|
||||||
|
First, `createList` is a simple function for creating a list of a single element, e.g. `createList 2 == [2]`.
|
||||||
|
|
||||||
|
Now let's take `sequenceAL` apart, first, it does a fold over the list `[f a]`, and `b` is initialized to `pure []`, which results in `f [a]` as required by the function's output.
|
||||||
|
|
||||||
|
Inside the function, `createList <$> x` applies `createList` to the value inside `f a`, resulting in `f [a]`, and then `(++)` is applied to the value again, so it becomes `f ((++) [a])`, now we can apply the function `(++) [a]` to `b` by `((++) . createList <$> x) <*> b`, which results in `f ([a] ++ b)`.
|
||||||
|
|
||||||
|
## Alternative formulation
|
||||||
|
|
||||||
|
### Definition
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
class Functor f => Monoidal f where
|
||||||
|
unit :: f ()
|
||||||
|
(**) :: f a -> f b -> f (a,b)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Laws:
|
||||||
|
|
||||||
|
1. Left identity
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
unit ** v ≅ v
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Right identity
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
u ** unit ≅ u
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Associativity
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
u ** (v ** w) ≅ (u ** v) ** w
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Neutrality
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
fmap (g *** h) (u ** v) = fmap g u ** fmap h v
|
||||||
|
```
|
||||||
|
|
||||||
|
### Isomorphism
|
||||||
|
|
||||||
|
In the laws above, `≅` refers to isomorphism rather than equality. In particular we consider:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
(x,()) ≅ x ≅ ((),x)
|
||||||
|
((x,y),z) ≅ (x,(y,z))
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Exercises
|
||||||
|
|
||||||
|
instance Applicative [] where
|
||||||
|
pure :: a -> [a]
|
||||||
|
pure x = [x]
|
||||||
|
|
||||||
|
(<*>) :: [a -> b] -> [a] -> [b]
|
||||||
|
gs <*> xs = [ g x | g <- gs, x <- xs ]
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
pure id <*> v = v
|
||||||
|
```
|
||||||
|
```haskell
|
||||||
|
pure f <*> pure x = pure (f x)
|
||||||
|
```
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
u <*> pure y = pure ($ y) <*> u
|
||||||
|
```
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
u <*> (v <*> w) = pure (.) <*> u <*> v <*> w
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Implement `pure` and `<*>` in terms of `unit` and `**`, and vice versa.
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
unit :: f ()
|
||||||
|
unit = pure ()
|
||||||
|
|
||||||
|
(**) :: f a -> f b -> f (a, b)
|
||||||
|
a ** b = fmap (,) a <*> b
|
||||||
|
|
||||||
|
pure :: a -> f a
|
||||||
|
pure x = unit ** x
|
||||||
|
|
||||||
|
(<*>) :: f (a -> b) -> f a -> f b
|
||||||
|
f <*> a = fmap (uncurry ($)) (f ** a) = fmap (\(f, a) -> f a) (f ** a)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Are there any `Applicative` instances for which there are also functions `f () -> ()` and `f (a,b) -> (f a, f b)`, satisfying some "reasonable" laws?
|
||||||
|
|
||||||
|
The [`Arrow`](https://wiki.haskell.org/Typeclassopedia#Arrow) type class seems to satisfy these criteria.
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
first unit = ()
|
||||||
|
|
||||||
|
(id *** f) (a, b) = (f a, f b)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. (Tricky) Prove that given your implementations from the first exercise, the usual Applicative laws and the Monoidal laws stated above are equivalent.
|
||||||
|
|
||||||
|
1. Identity Law
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
pure id <*> v
|
||||||
|
= fmap (uncurry ($)) ((unit ** id) ** v)
|
||||||
|
= fmap (uncurry ($)) (id ** v)
|
||||||
|
= fmap id v
|
||||||
|
= v
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Homomorphism
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
pure f <*> pure x
|
||||||
|
= (unit ** f) <*> (unit ** x)
|
||||||
|
= fmap (\(f, a) -> f a) (unit ** f) (unit ** x)
|
||||||
|
= fmap (\(f, a) -> f a) (f ** x)
|
||||||
|
= fmap f x
|
||||||
|
= pure (f x)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Interchange
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
u <*> pure y
|
||||||
|
= fmap (uncurry ($)) (u ** (unit ** y))
|
||||||
|
= fmap (uncurry ($)) (u ** y)
|
||||||
|
= fmap (u $) y
|
||||||
|
= fmap ($ y) u
|
||||||
|
= pure ($ y) <*> u
|
||||||
|
|
||||||
|
4. Composition
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
u <*> (v <*> w)
|
||||||
|
= fmap (uncurry ($)) (u ** (fmap (uncurry ($)) (v ** w)))
|
||||||
|
= fmap (uncurry ($)) (u ** (fmap v w))
|
||||||
|
= fmap u (fmap v w)
|
||||||
|
= fmap (u . v) w
|
||||||
|
= pure (.) <*> u <*> v <*> w =
|
||||||
|
```
|
||||||
|
29
_sass/toc.scss
Normal file
29
_sass/toc.scss
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#toc-container {
|
||||||
|
h2 {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, input + span {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
position: absolute;
|
||||||
|
width: 50px;
|
||||||
|
height: 20px;
|
||||||
|
opacity: 0;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input + span::before {
|
||||||
|
content: '[hide]';
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked ~ ul {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + span::before {
|
||||||
|
content: '[show]';
|
||||||
|
}
|
||||||
|
}
|
@ -47,5 +47,6 @@ $on-laptop: 800px;
|
|||||||
@import
|
@import
|
||||||
"base",
|
"base",
|
||||||
"layout",
|
"layout",
|
||||||
"syntax-highlighting"
|
"syntax-highlighting",
|
||||||
|
"toc"
|
||||||
;
|
;
|
||||||
|
Loading…
Reference in New Issue
Block a user