Skip to content

Commit 0916f00

Browse files
Add new post: docs/posts/2025-04-27-fljd-civilistjvel.html
posts/2025-04-27-fljd-civilistjvel.md
1 parent cd81784 commit 0916f00

File tree

8 files changed

+246
-154
lines changed

8 files changed

+246
-154
lines changed

docs/archive.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ <h1>Archives</h1>
3737
Previous posts:
3838
<ul>
3939

40+
<li>
41+
<a href="./posts/2025-04-27-fljd-civilistjvel.html">Civilistjävel - Följd</a> - April 27, 2025
42+
</li>
43+
4044
<li>
4145
<a href="./posts/2025-04-23-stochasticdrift-barker.html">Barker - Stochastic Drift</a> - April 23, 2025
4246
</li>

docs/atom.xml

Lines changed: 28 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,35 @@
1010
<email>usefulalgorithm@gmail.com</email>
1111

1212
</author>
13-
<updated>2025-04-23T00:00:00Z</updated>
13+
<updated>2025-04-27T00:00:00Z</updated>
1414
<entry>
15+
<title>Civilistjävel - Följd</title>
16+
<link href="http://usefulalgorithm.github.io/posts/2025-04-27-fljd-civilistjvel.html" />
17+
<id>http://usefulalgorithm.github.io/posts/2025-04-27-fljd-civilistjvel.html</id>
18+
<published>2025-04-27T00:00:00Z</published>
19+
<updated>2025-04-27T00:00:00Z</updated>
20+
<summary type="html"><![CDATA[<article>
21+
<section class="header">
22+
Posted on April 27, 2025
23+
24+
<br>
25+
26+
Tags: <a title="All pages tagged &#39;2025&#39;." href="/tags/2025.html" rel="tag">2025</a>, <a title="All pages tagged &#39;music&#39;." href="/tags/music.html" rel="tag">music</a>, <a title="All pages tagged &#39;ambient, dub techno&#39;." href="/tags/ambient%EF%BC%8C%20dub%20techno.html" rel="tag">ambient, dub techno</a>
27+
28+
</section>
29+
<section>
30+
<p><img src="https://f4.bcbits.com/img/a2320697940_10.jpg" /></p>
31+
<p>去年他發的 <em>Brödföda</em> 我很喜歡,這張則是它的續作,風格上更極簡一點。也有更多 dub techno 歌,像十分鐘的 “XVI” 就很讚。</p>
32+
<hr />
33+
<p>Fav tracks: XIV, XVI, XVII
34+
Score: 8.1/10</p>
35+
<p>Release date: 2025-02-14</p>
36+
<p>Labels: Rubadub</p>
37+
</section>
38+
</article>
39+
]]></summary>
40+
</entry>
41+
<entry>
1542
<title>Barker - Stochastic Drift</title>
1643
<link href="http://usefulalgorithm.github.io/posts/2025-04-23-stochasticdrift-barker.html" />
1744
<id>http://usefulalgorithm.github.io/posts/2025-04-23-stochasticdrift-barker.html</id>
@@ -255,81 +282,5 @@
255282
</article>
256283
]]></summary>
257284
</entry>
258-
<entry>
259-
<title>Using Haskell to generate post template (or, using Haskell in inappropriate places)</title>
260-
<link href="http://usefulalgorithm.github.io/posts/2025-03-25-pulling-album-info.html" />
261-
<id>http://usefulalgorithm.github.io/posts/2025-03-25-pulling-album-info.html</id>
262-
<published>2025-03-25T00:00:00Z</published>
263-
<updated>2025-03-25T00:00:00Z</updated>
264-
<summary type="html"><![CDATA[<article>
265-
<section class="header">
266-
Posted on March 25, 2025
267-
268-
<br>
269-
270-
Tags: <a title="All pages tagged &#39;about this blog&#39;." href="/tags/about%20this%20blog.html" rel="tag">about this blog</a>, <a title="All pages tagged &#39;haskell&#39;." href="/tags/haskell.html" rel="tag">haskell</a>
271-
272-
</section>
273-
<section>
274-
<p>我前陣子太閒,就想要自動地在我執行 Github workflow 的時候去觸發一個腳本,從一些網路上的資料庫抓我想要寫的專輯的相關資料。一般來說,做這種事情通常是直接用 Python 或是 Bash script + <code>jq</code> 之類的,又簡單又好懂,不過我天生喜歡瞎忙,想說既然都在用 Haskell 了,不如也來用 Haskell 寫這腳本。事實證明真的是比用 Python 麻煩很多…</p>
275-
<h3 id="從哪裡抓專輯資料">從哪裡抓專輯資料?</h3>
276-
<p>我本來以為可以從 Bandcamp 去拉,但我寫信去要 API key 時,得到的回覆是沒有公開 API 可以拿到某張專輯的資訊。網上有一些專案好像可以不用爬蟲不用 API key 去拿到專輯資訊,不過看起來有點醜,就覺得算了。最後決定直接從 <a href="https://www.discogs.com/developers">Discogs</a> 上面抓。做法是先用 <code>search</code> 找到某張專輯的 <a href="https://support.discogs.com/hc/en-us/articles/360005055493-Database-Guidelines-16-Master-Release">master release</a>,拿到 master release 的 id,再用這個 id 去查找專輯的發行日期、廠牌跟封面。如果這張專輯太冷門,根本沒有 master release 的話,那麼就直接挑第一個 release,用它的 id 去拿我要的資訊。</p>
277-
<h3 id="用-haskell-解析-json-資料">用 Haskell 解析 JSON 資料</h3>
278-
<p>處理 JSON 資料時,Haskell 通常用 <a href="https://hackage.haskell.org/package/aeson">aeson</a>。使用方式是先定義一個資料結構:</p>
279-
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Release</span> <span class="ot">=</span> <span class="dt">Release</span> {</span>
280-
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> artists ::</span> [<span class="dt">String</span>],</span>
281-
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ot"> title ::</span> <span class="dt">String</span>,</span>
282-
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="ot"> year ::</span> <span class="dt">Int</span>,</span>
283-
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="ot"> released ::</span> <span class="dt">String</span>,</span>
284-
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="ot"> imageUrl ::</span> <span class="dt">String</span>,</span>
285-
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="ot"> labels ::</span> [<span class="dt">String</span>],</span>
286-
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="ot"> uri ::</span> <span class="dt">String</span></span>
287-
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>} <span class="kw">deriving</span> (<span class="dt">Show</span>, <span class="dt">Eq</span>, <span class="dt">Generic</span>)</span></code></pre></div>
288-
<p>接著實作 <code>ToJSON</code> 和 <code>FromJSON</code>。<code>ToJSON</code> 很簡單,直接把資料結構轉成 JSON 格式;<code>FromJSON</code> 就麻煩多了,得詳細定義如何把 JSON 轉成資料結構:</p>
289-
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">ToJSON</span> <span class="dt">Release</span> <span class="co">-- 無腦轉</span></span>
290-
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
291-
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">FromJSON</span> <span class="dt">Release</span> <span class="kw">where</span></span>
292-
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> parseJSON (<span class="dt">Object</span> v) <span class="ot">=</span> <span class="kw">do</span></span>
293-
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> artists <span class="ot">&lt;-</span> v <span class="op">.:</span> <span class="st">&quot;artists&quot;</span> <span class="op">&gt;&gt;=</span> <span class="fu">traverse</span> (<span class="op">.:</span> <span class="st">&quot;name&quot;</span>) <span class="co">-- &quot;artists&quot; 是個陣列,這裡的意思是取出陣列中每個元素的 &quot;name&quot;</span></span>
294-
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> title <span class="ot">&lt;-</span> v <span class="op">.:</span> <span class="st">&quot;title&quot;</span> <span class="co">-- .: 表示從 v 中取出 &quot;title&quot; 的值</span></span>
295-
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> year <span class="ot">&lt;-</span> v <span class="op">.:</span> <span class="st">&quot;year&quot;</span></span>
296-
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> released <span class="ot">&lt;-</span> v <span class="op">.:</span> <span class="st">&quot;released&quot;</span></span>
297-
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> images <span class="ot">&lt;-</span> v <span class="op">.:</span> <span class="st">&quot;images&quot;</span></span>
298-
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> imageUrl <span class="ot">&lt;-</span> <span class="kw">case</span> images <span class="kw">of</span></span>
299-
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> (img<span class="op">:</span>_) <span class="ot">-&gt;</span> img <span class="op">.:</span> <span class="st">&quot;resource_url&quot;</span></span>
300-
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> [] <span class="ot">-&gt;</span> <span class="fu">fail</span> <span class="st">&quot;No images found&quot;</span></span>
301-
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> labels <span class="ot">&lt;-</span> v <span class="op">.:</span> <span class="st">&quot;labels&quot;</span> <span class="op">&gt;&gt;=</span> <span class="fu">traverse</span> (<span class="op">.:</span> <span class="st">&quot;name&quot;</span>)</span>
302-
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> uri <span class="ot">&lt;-</span> v <span class="op">.:</span> <span class="st">&quot;uri&quot;</span></span>
303-
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="fu">return</span> <span class="dt">Release</span> {</span>
304-
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a> artists <span class="ot">=</span> artists,</span>
305-
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> title <span class="ot">=</span> title,</span>
306-
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a> year <span class="ot">=</span> year,</span>
307-
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a> released <span class="ot">=</span> released,</span>
308-
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a> imageUrl <span class="ot">=</span> imageUrl,</span>
309-
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a> labels <span class="ot">=</span> labels,</span>
310-
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a> uri <span class="ot">=</span> uri</span>
311-
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a> }</span></code></pre></div>
312-
<p>可以看出來,如果資料結構很複雜,解析起來就得定義一堆結構來對應。用 Python 的話,這有點像寫一堆 Pydantic class,但每個 class 都得自己實作 <code>decode</code>。</p>
313-
<p>另一個麻煩點是,當你只想取一個深層的值時,Haskell 就有點麻煩了。對比下面兩行程式碼:</p>
314-
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>body <span class="op">^?</span> key <span class="st">&quot;results&quot;</span> <span class="op">.</span> nth <span class="dv">0</span> <span class="op">.</span> key (fromString queryKey) <span class="op">.</span> _Integer</span></code></pre></div>
315-
<p>跟</p>
316-
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="bu">int</span>(body[<span class="st">&quot;results&quot;</span>][<span class="dv">0</span>][queryKey])</span></code></pre></div>
317-
<p>可能我很淺?但我覺得 Python 的好讀多了。另外如果要在 Haskell 做這種操作,需要引入 <a href="https://hackage.haskell.org/package/lens-aeson">lens-aeson</a> 套件,原生是不支援的。</p>
318-
<h3 id="套件管理">套件管理</h3>
319-
<p>Haskell 的套件管理工具有 cabal 和 stack,感覺有點像 pip 和 poetry,但功能重疊得更多。</p>
320-
<h3 id="在-github-workflow-裡執行-haskell-程式">在 Github workflow 裡執行 Haskell 程式</h3>
321-
<p>如果每次在 workflow 裡面跑的時候都要重新 <code>stack build</code> (可以想像就是 <code>make build</code>)的話會花很多時間,實測下來大概需要二十分鐘左右。真的是太久了… 幸好有個專案就是在做 <a href="https://github.com/freckle/stack-action/tree/v5/">stack action</a>,快取做得蠻好的,所以其實只有第一次提交有改到 Haskell code 的改動時會需要去把整包編好,其他時候就直接用快取的執行檔,那這大概一分鐘之內就能做完。</p>
322-
<h3 id="結論">結論</h3>
323-
<p>還是用 Python 做這種事情會比較快:</p>
324-
<ul>
325-
<li>比較多人用:Discogs 有提供 Python SDK,甚至不用自己解析 API response。</li>
326-
<li>Github workflow 設定起來比較不麻煩,而且不會需要花老半天編譯</li>
327-
<li>JSON support</li>
328-
</ul>
329-
<p>不過如果你時間很多的話也是個不錯的體驗啦… 所有的程式碼都在 <a href="https://github.com/usefulalgorithm/usefulalgorithm.github.io/tree/main/scripts/pull_album_info">這裡</a>。</p>
330-
</section>
331-
</article>
332-
]]></summary>
333-
</entry>
334285

335286
</feed>

docs/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ <h1>Home</h1>
4343
<h2>Posts</h2>
4444
<ul>
4545

46+
<li>
47+
<a href="./posts/2025-04-27-fljd-civilistjvel.html">Civilistjävel - Följd</a> - April 27, 2025
48+
</li>
49+
4650
<li>
4751
<a href="./posts/2025-04-23-stochasticdrift-barker.html">Barker - Stochastic Drift</a> - April 23, 2025
4852
</li>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="x-ua-compatible" content="ie=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<meta property="og:image" content="/images/plane-of-immanence.png">
8+
<title>Blog without Organs - Civilistjävel - Följd</title>
9+
<link rel="stylesheet" href="../css/default.css" />
10+
<link rel="stylesheet" href="../css/syntax.css" />
11+
<link rel="icon" href="../images/favicon.ico" />
12+
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro|Roboto+Mono:300|Noto+Sans+TC:300&display=swap" rel="stylesheet">
13+
<!-- <script src="https://kit.fontawesome.com/5b5c2f44f2.js"></script> -->
14+
<script type="text/javascript"> (function() { var css =
15+
document.createElement('link'); css.href =
16+
'https://use.fontawesome.com/releases/v5.1.0/css/all.css'; css.rel =
17+
'stylesheet'; css.type = 'text/css';
18+
document.getElementsByTagName('head')[0].appendChild(css); })();
19+
</script>
20+
21+
</head>
22+
<body>
23+
<header>
24+
<div class="logo">
25+
<a href="../">Blog without Organs</a>
26+
</div>
27+
<nav>
28+
<a href="../">Home</a>
29+
<a href="../about.html">About</a>
30+
<a href="../blogroll.html">Blogroll</a>
31+
<a href="../archive.html">Archive</a>
32+
</nav>
33+
</header>
34+
35+
<main role="main">
36+
<h1>Civilistjävel - Följd</h1>
37+
<article>
38+
<section class="header">
39+
Posted on April 27, 2025
40+
41+
<br>
42+
43+
Tags: <a title="All pages tagged '2025'." href="../tags/2025.html" rel="tag">2025</a>, <a title="All pages tagged 'music'." href="../tags/music.html" rel="tag">music</a>, <a title="All pages tagged 'ambient, dub techno'." href="../tags/ambient%EF%BC%8C%20dub%20techno.html" rel="tag">ambient, dub techno</a>
44+
45+
</section>
46+
<section>
47+
<p><img src="https://f4.bcbits.com/img/a2320697940_10.jpg" /></p>
48+
<p>去年他發的 <em>Brödföda</em> 我很喜歡,這張則是它的續作,風格上更極簡一點。也有更多 dub techno 歌,像十分鐘的 “XVI” 就很讚。</p>
49+
<hr />
50+
<p>Fav tracks: XIV, XVI, XVII
51+
Score: 8.1/10</p>
52+
<p>Release date: 2025-02-14</p>
53+
<p>Labels: Rubadub</p>
54+
</section>
55+
</article>
56+
57+
</main>
58+
59+
60+
<div class="comments">
61+
<h2>Comments</h2>
62+
<div id="disqus_thread"></div>
63+
</div>
64+
<script>
65+
66+
var disqus_config = function () {
67+
this.page.identifier = '/posts/2025-04-27-fljd-civilistjvel.html';
68+
this.page.url = 'https://usefulalgorithm.github.io/posts/' + '/posts/2025-04-27-fljd-civilistjvel.html';
69+
};
70+
(function() {
71+
var d = document, s = d.createElement('script');
72+
s.src = 'https://usefulalgorithm.disqus.com/embed.js';
73+
s.setAttribute('data-timestamp', +new Date());
74+
(d.head || d.body).appendChild(s);
75+
})();
76+
</script>
77+
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
78+
79+
80+
<div class="contacts">
81+
<a href="https://usefulalgorithm.github.io/rss.xml">
82+
<i class="fas fa-rss"></i>
83+
</a>
84+
<a href="https://github.com/usefulalgorithm">
85+
<i class="fab fa-github"></i>
86+
</a>
87+
<a href="https://www.linkedin.com/in/tsungjulii">
88+
<i class="fab fa-linkedin-in"></i>
89+
</a>
90+
<a href="https://twitter.com/usefulalgorithm">
91+
<i class="fab fa-twitter"></i>
92+
</a>
93+
<a href="mailto:usefulalgorithm@gmail.com">
94+
<i class="far fa-envelope"></i>
95+
</a>
96+
</div>
97+
<footer>
98+
Site proudly generated by
99+
<a href="http://jaspervdj.be/hakyll">Hakyll</a>
100+
</footer>
101+
</body>
102+
</html>

0 commit comments

Comments
 (0)