-
Notifications
You must be signed in to change notification settings - Fork 0
/
2019-04-22-000-comonadic-builders--minor-addition.html
158 lines (136 loc) · 11 KB
/
2019-04-22-000-comonadic-builders--minor-addition.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="alternate"
type="application/rss+xml"
href="https://magnus.therning.org/feed.xml"
title="RSS feed for https://magnus.therning.org/">
<title>Comonadic builders, minor addition</title>
<meta name="author" content="Magnus Therning"><meta name="referrer" content="no-referrer"><link href= "static/style.css" rel="stylesheet" type="text/css" /><link href= "static/htmlize.css" rel="stylesheet" type="text/css" /><link href= "static/extra_style.css" rel="stylesheet" type="text/css" /></head>
<body>
<div id="preamble" class="status"><div class="nav-bar"><a class="nav-link" href="./index.html">Top</a><a class="nav-link" href="./archive.html">Archive</a><a class="nav-link align-right" href="./feed.xml"><img src="static/rss-feed-icon.png" style="height: 24px;" /></a></div></div>
<div id="content">
<div class="post-date">22 Apr 2019</div><h1 class="post-title"><a href="https://magnus.therning.org/2019-04-22-000-comonadic-builders--minor-addition.html">Comonadic builders, minor addition</a></h1>
<p>
When reading about <a href="https://chshersh.github.io/posts/2019-03-25-comonadic-builders">Comonadic builders</a> the other day I reacted to this comment:
</p>
<blockquote>
<p>
The <code>comonad</code> package has the <a href="https://hackage.haskell.org/package/comonad-5.0.4/docs/Control-Comonad-Trans-Traced.html#t:TracedT">Traced</a> <code>newtype</code> wrapper around the function
<code>(->)</code>. The <code>Comonad</code> instance for this <code>newtype</code> gives us the desired
behaviour. However, dealing with the <code>newtype</code> wrapping and unwrapping makes our
code noisy and truly harder to understand, so let's use the <code>Comonad</code> instance
for the arrow <code>(->)</code> itself
</p>
</blockquote>
<p>
So, just for fun I thought I work out the "noisy and truly harder" bits.
</p>
<p>
To begin with I needed two language extensions and two imports
</p>
<div class="org-src-container">
<pre class="src src-haskell"><span class="org-haskell-pragma">{-# LANGUAGE OverloadedStrings#-}</span>
<span class="org-haskell-pragma">{-# LANGUAGE RecordWildCards #-}</span>
<span class="org-haskell-keyword">import</span> <span class="org-haskell-constructor">Control.Comonad.Traced</span>
<span class="org-haskell-keyword">import</span> <span class="org-haskell-constructor">Data.Text</span>
</pre>
</div>
<p>
After that I could copy quite a bit of stuff directly from the other post
</p>
<ul class="org-ul">
<li><code>Settings</code> definition</li>
<li>The <code>Semigroup</code> instance for <code>Settings</code></li>
<li>The <code>Monoid</code> instance for <code>Settings</code></li>
<li><code>Project</code> definition</li>
</ul>
<p>
After this everything had only minor changes. First off the <code>ProjectBuilder</code>
type had to be changed to
</p>
<div class="org-src-container">
<pre class="src src-haskell"><span class="org-haskell-keyword">type</span> <span class="org-haskell-type">ProjectBuilder</span> <span class="org-haskell-operator">=</span> <span class="org-haskell-type">Traced</span> <span class="org-haskell-type">Settings</span> <span class="org-haskell-type">Project</span>
</pre>
</div>
<p>
With that done the types of all the functions can actually be left as they are,
but of course the definitions have to modified. However, it turned out that the
necessary modifications were rather smaller than I had expected. First out
<code>buildProject</code> which I decided to call <code>buildProjectW</code> to make it possible to
keep the original code and the new code in the same file without causing name
clashes:
</p>
<div class="org-src-container">
<pre class="src src-haskell"><span class="org-haskell-definition">buildProjectW</span> <span class="org-haskell-operator">::</span> <span class="org-haskell-type">Text</span> <span class="org-haskell-operator">-></span> <span class="org-haskell-type">ProjectBuilder</span>
<span class="org-haskell-definition">buildProjectW</span> <span class="org-haskell-operator">=</span> traced <span class="org-haskell-operator">.</span> buildProject
<span class="org-haskell-keyword">where</span>
buildProject projectName <span class="org-haskell-constructor">Settings</span><span class="org-rainbow-delimiters-depth-1">{</span><span class="org-haskell-operator">..</span><span class="org-rainbow-delimiters-depth-1">}</span> <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">Project</span>
<span class="org-rainbow-delimiters-depth-1">{</span> projectHasLibrary <span class="org-haskell-operator">=</span> getAny settingsHasLibrary
, projectGitHub <span class="org-haskell-operator">=</span> getAny settingsGitHub
, projectTravis <span class="org-haskell-operator">=</span> getAny settingsTravis
, <span class="org-haskell-operator">..</span>
<span class="org-rainbow-delimiters-depth-1">}</span>
</pre>
</div>
<p>
The only difference is the addition of <code>traced .</code> to wrap it up in the
<code>newtype</code>, the rest is copied straight from the original article.
</p>
<p>
The two simple project combinator functions, which I call <code>hasLibraryBW</code> and
<code>gitHubBW</code>, needed a bit of tweaking. In the original version combinators take a
<code>builder</code> which is an ordinary function, so it can just be called. Now however,
the function is wrapped in a <code>newtype</code> so a bit of unwrapping is necessary:
</p>
<div class="org-src-container">
<pre class="src src-haskell"><span class="org-haskell-definition">hasLibraryBW</span> <span class="org-haskell-operator">::</span> <span class="org-haskell-type">ProjectBuilder</span> <span class="org-haskell-operator">-></span> <span class="org-haskell-type">Project</span>
<span class="org-haskell-definition">hasLibraryBW</span> builder <span class="org-haskell-operator">=</span> runTraced builder <span class="org-haskell-operator">$</span> mempty <span class="org-rainbow-delimiters-depth-1">{</span> settingsHasLibrary <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">Any</span> <span class="org-haskell-constructor">True</span> <span class="org-rainbow-delimiters-depth-1">}</span>
<span class="org-haskell-definition">gitHubBW</span> <span class="org-haskell-operator">::</span> <span class="org-haskell-type">ProjectBuilder</span> <span class="org-haskell-operator">-></span> <span class="org-haskell-type">Project</span>
<span class="org-haskell-definition">gitHubBW</span> builder <span class="org-haskell-operator">=</span> runTraced builder <span class="org-haskell-operator">$</span> mempty <span class="org-rainbow-delimiters-depth-1">{</span> settingsGitHub <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">Any</span> <span class="org-haskell-constructor">True</span> <span class="org-rainbow-delimiters-depth-1">}</span>
</pre>
</div>
<p>
Once again it's rather small differences from the code in the article.
</p>
<p>
As for the final combinator, which I call <code>travisBW</code>, actually needed no changes
at all. I only rewrote it using a <code>when</code> clause, because I prefer that style
over <code>let</code>:
</p>
<div class="org-src-container">
<pre class="src src-haskell"><span class="org-haskell-definition">travisBW</span> <span class="org-haskell-operator">::</span> <span class="org-haskell-type">ProjectBuilder</span> <span class="org-haskell-operator">-></span> <span class="org-haskell-type">Project</span>
<span class="org-haskell-definition">travisBW</span> builder <span class="org-haskell-operator">=</span> project <span class="org-rainbow-delimiters-depth-1">{</span> projectTravis <span class="org-haskell-operator">=</span> projectGitHub project <span class="org-rainbow-delimiters-depth-1">}</span>
<span class="org-haskell-keyword">where</span>
project <span class="org-haskell-operator">=</span> extract builder
</pre>
</div>
<p>
Finally, to show that this implementation hasn't really changed the behaviour
</p>
<div class="org-src-container">
<pre class="src src-haskell"><span class="org-haskell-definition">λ</span> extract <span class="org-haskell-operator">$</span> buildProjectW <span class="org-string">"travis"</span> <span class="org-haskell-operator">=>></span> travisBW
<span class="org-haskell-constructor">Project</span> <span class="org-rainbow-delimiters-depth-1">{</span> projectName <span class="org-haskell-operator">=</span> <span class="org-string">"travis"</span>
, projectHasLibrary <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">False</span>
, projectGitHub <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">False</span>
, projectTravis <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">False</span>
<span class="org-rainbow-delimiters-depth-1">}</span>
<span class="org-haskell-definition">λ</span> extract <span class="org-haskell-operator">$</span> buildProjectW <span class="org-string">"github-travis"</span> <span class="org-haskell-operator">=>></span> gitHubBW <span class="org-haskell-operator">=>></span> travisBW
<span class="org-haskell-constructor">Project</span> <span class="org-rainbow-delimiters-depth-1">{</span> projectName <span class="org-haskell-operator">=</span> <span class="org-string">"github-travis"</span>
, projectHasLibrary <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">False</span>
, projectGitHub <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">True</span>
, projectTravis <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">True</span>
<span class="org-rainbow-delimiters-depth-1">}</span>
<span class="org-haskell-definition">λ</span> extract <span class="org-haskell-operator">$</span> buildProjectW <span class="org-string">"travis-github"</span> <span class="org-haskell-operator">=>></span> travisBW <span class="org-haskell-operator">=>></span> gitHubBW
<span class="org-haskell-constructor">Project</span> <span class="org-rainbow-delimiters-depth-1">{</span> projectName <span class="org-haskell-operator">=</span> <span class="org-string">"travis-github"</span>
, projectHasLibrary <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">False</span>
, projectGitHub <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">True</span>
, projectTravis <span class="org-haskell-operator">=</span> <span class="org-haskell-constructor">True</span>
<span class="org-rainbow-delimiters-depth-1">}</span>
</pre>
</div>
<div class="taglist"><a href="https://magnus.therning.org/tags.html">Tags</a>: <a href="https://magnus.therning.org/tag-haskell.html">haskell</a> <a href="https://magnus.therning.org/tag-comonad.html">comonad</a> <a href="https://magnus.therning.org/tag-builder_pattern.html">builder_pattern</a> </div></div>
<div id="postamble" class="status"><!-- org-static-blog-page-postamble --></div>
</body>
</html>