diff --git a/Resume.pdf b/Resume.pdf deleted file mode 100644 index ac97cbb..0000000 Binary files a/Resume.pdf and /dev/null differ diff --git a/_config.yml b/_config.yml index b703bc3..818afe5 100644 --- a/_config.yml +++ b/_config.yml @@ -32,3 +32,6 @@ collections: output: true permalink: /travel/:path/ path: /travel + +contentsLabel: "Table of Contents" +showToggleButton: true diff --git a/_layouts/default.html b/_layouts/default.html index cf41411..7c2ace6 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -10,7 +10,11 @@

- {{ content }} + {% if page.toc %} + {{ content | toc_generate }} + {% else %} + {{ content }} + {% endif %}
diff --git a/_plugins/tocGenerator.rb b/_plugins/tocGenerator.rb new file mode 100644 index 0000000..9f4e868 --- /dev/null +++ b/_plugins/tocGenerator.rb @@ -0,0 +1,132 @@ +require 'nokogiri' + +module Jekyll + + module TOCGenerator + + TOGGLE_HTML = '

%1

' + TOC_CONTAINER_HTML = '
%1%2
    %3
' + HIDE_HTML = '' + + 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 = '' 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 = '%2 %3%4' + .gsub('%1', anchor_id.to_s) + .gsub('%2', tocNumber.to_s) + .gsub('%3', tocText) + .gsub('%4', tocInner ? tocInner : '') + '
  • %3
  • ' + .gsub('%1', toc_level.to_s) + .gsub('%2', toc_section.to_s) + .gsub('%3', link) + end + + end + +end + +Liquid::Template.register_filter(Jekyll::TOCGenerator) diff --git a/_posts/2017-09-27-typoclassopedia-exercise-answers.md b/_posts/2017-09-27-typoclassopedia-exercise-answers.md index 934425b..157357b 100644 --- a/_posts/2017-09-27-typoclassopedia-exercise-answers.md +++ b/_posts/2017-09-27-typoclassopedia-exercise-answers.md @@ -5,9 +5,10 @@ date: 2017-09-27 permalink: typoclassopedia-exercise-solutions/ categories: programming 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. @@ -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. + +## 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 = + ``` diff --git a/_sass/toc.scss b/_sass/toc.scss new file mode 100644 index 0000000..1431123 --- /dev/null +++ b/_sass/toc.scss @@ -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]'; + } +} diff --git a/css/main.scss b/css/main.scss index ccbae5a..b802182 100644 --- a/css/main.scss +++ b/css/main.scss @@ -47,5 +47,6 @@ $on-laptop: 800px; @import "base", "layout", - "syntax-highlighting" + "syntax-highlighting", + "toc" ;