<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kang-min Liu</title>
    <description>The latest articles on DEV Community by Kang-min Liu (@gugod).</description>
    <link>https://dev.to/gugod</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F133464%2F992a9408-706a-4045-8049-3e564ca06983.jpeg</url>
      <title>DEV Community: Kang-min Liu</title>
      <link>https://dev.to/gugod</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gugod"/>
    <language>en</language>
    <item>
      <title>My new workflow of collecting news clips</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Sat, 01 Mar 2025 02:41:00 +0000</pubDate>
      <link>https://dev.to/gugod/my-new-workflow-of-collecting-news-clips-4kd3</link>
      <guid>https://dev.to/gugod/my-new-workflow-of-collecting-news-clips-4kd3</guid>
      <description>&lt;p&gt;Markdownload: &lt;a href="https://addons.mozilla.org/en-GB/firefox/addon/markdownload/" rel="noopener noreferrer"&gt;https://addons.mozilla.org/en-GB/firefox/addon/markdownload/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have recently started using Markdownload as a way to collect news articles locally.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a new browser window or tab group or workspace.&lt;/li&gt;
&lt;li&gt;Read news in my RSS reader, skim through the news and open the desired news website in the browser.&lt;/li&gt;
&lt;li&gt;Repeat step 2 until it's enough.&lt;/li&gt;
&lt;li&gt;Right-click on the browser's context menu and select "Download all tabs as Markdown."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, new Markdown text files would appear the downloaded folder. The content is the core portions of those webpages. Most of the time, it works quite accurately. There might be some irrelevant information at the beginning or the en and would require manual trimming. Perhaps an extra refinement with local LLM would be interesting.&lt;/p&gt;

&lt;p&gt;The main reason I'm doing this is that sometimes when I see something that might have an impact in the future, I find it difficult to locate the original news article later on. By saving news articles as Markdown files, I can then easily use grep to search for certain keywords and since there will be no irrelevant information in the search space, the result would be very accurate.&lt;/p&gt;

&lt;p&gt;I've been doing this for about two weeks now and have collected 60 news articles. Perhaps in a few months I'll discover something new.&lt;/p&gt;

</description>
      <category>rss</category>
      <category>markdownload</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Safe-navigation monad</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Mon, 19 Jun 2023 16:01:49 +0000</pubDate>
      <link>https://dev.to/gugod/safe-navigation-monad-4lel</link>
      <guid>https://dev.to/gugod/safe-navigation-monad-4lel</guid>
      <description>&lt;p&gt;Recently on reddit.com/r/perl, pmz posted a question like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;XML::Twig, get value (or not) without dying&lt;/p&gt;
&lt;p&gt;Currently I do:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
if (defined $elt-&amp;gt;first_child('addr')-&amp;gt;first_child('postalCode')) {
  $patient{patient_postal_code} = $elt-&amp;gt;first_child('addr')-&amp;gt;first_child('postalCode')-&amp;gt;text ;
}
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;because if I don't check for "defined" and the resulting value is null , it dies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Link to the original post: &lt;a href="https://www.reddit.com/r/perl/comments/1492sc1/xmltwig_get_value_or_not_without_dying/"&gt;https://www.reddit.com/r/perl/comments/1492sc1/xmltwig_get_value_or_not_without_dying/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While on one hand the question is about how to use XML::Twig, on the other hand the obvious inconvenience here is when &lt;code&gt;first_child('addr')&lt;/code&gt; returns &lt;code&gt;undef&lt;/code&gt;, which means there are no &lt;code&gt;&amp;lt;addr&amp;gt;&lt;/code&gt; element underneath, the following call of &lt;code&gt;first_child('postalCode')&lt;/code&gt; would make the programm die. Generally speaking: in a chain of calls we expect objects to be present in all positions, but sometimes there are &lt;code&gt;undef&lt;/code&gt;. Given that, is there a way to avoid the program from dying and let the entire call chain return &lt;code&gt;undef&lt;/code&gt; if &lt;code&gt;undef&lt;/code&gt; is encountered in the calling chain ?&lt;/p&gt;

&lt;p&gt;To formalize the question a bit more generically: assume a class with instance methods &lt;code&gt;a()&lt;/code&gt;, &lt;code&gt;b()&lt;/code&gt;, and &lt;code&gt;c()&lt;/code&gt;. These methods may return an instance of same class, or ocassionally, &lt;code&gt;undef&lt;/code&gt;. Consider the following chain of calls originally from &lt;code&gt;$o&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$res = $o-&amp;gt;a()-&amp;gt;b()-&amp;gt;c();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In case any of &lt;code&gt;a()&lt;/code&gt;, &lt;code&gt;b()&lt;/code&gt;, or &lt;code&gt;c()&lt;/code&gt; returns &lt;code&gt;undef&lt;/code&gt;, the program dies with messages like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can't call method "c" on an undefined value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Which suggests &lt;code&gt;b()&lt;/code&gt; returns &lt;code&gt;undef&lt;/code&gt; and since &lt;code&gt;undef&lt;/code&gt; is not an object, we cannot call methods on it.&lt;/p&gt;

&lt;p&gt;Now, could we rewrite the same program to prevent the abovementioned error from happening, while making &lt;code&gt;$res&lt;/code&gt; be &lt;code&gt;undef&lt;/code&gt; if any of &lt;code&gt;a()&lt;/code&gt;, &lt;code&gt;b()&lt;/code&gt;, &lt;code&gt;c()&lt;/code&gt; returns &lt;code&gt;undef&lt;/code&gt;, or otherwise, the return value of &lt;code&gt;c()&lt;/code&gt; ?&lt;/p&gt;

&lt;p&gt;In some other programming languages, such purpose could be satisfied by using the safe-navigation operator, such as &lt;code&gt;?.&lt;/code&gt; in javascript or kotlin:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;res = o.a()?.b()?.c();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or in raku, &lt;a href="https://docs.raku.org/language/operators#methodop_.%3F"&gt;&lt;code&gt;.?&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$res = $o.a().?b().?c();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;However, we haven't seen anything similar up until perl 5.38 just yet.&lt;/p&gt;

&lt;p&gt;A rather intuitive way to rewrite would be something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$res_a = $o-&amp;gt;a();
$res_b = $res_a &amp;amp;&amp;amp; $res_a-&amp;gt;b();
$res   = $res_b &amp;amp;&amp;amp; $res_b-&amp;gt;c();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;However, besides making the program much longer and less easier to grasp, the rewrite is not generic. It'll be different for similar statements with different method names. Not a good strategy.&lt;/p&gt;

&lt;p&gt;Meanwhile, here's a super simple and generic way:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$res = eval { $o-&amp;gt;a()-&amp;gt;b()-&amp;gt;c() };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;However, with the powerful side-effect of &lt;code&gt;eval&lt;/code&gt;, all exceptions would be ignored while we are only interested in ignoring undefined values. That is a lot more than what we want. Even though it looks simple, it is probably not applicable.&lt;/p&gt;

&lt;p&gt;Here is a solution with Monad design pattern.&lt;/p&gt;

&lt;p&gt;The rewritten version looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$res = SafeNav-&amp;gt;wrap($o) -&amp;gt;a()-&amp;gt;b()-&amp;gt;c() -&amp;gt;unwrap();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;SafeNav&lt;/code&gt; is defined as the folowing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use v5.36;
package SafeNav {
    sub wrap ($class, $o) { bless \$o, $class }
    sub unwrap ($self)    { $$self            }

    sub AUTOLOAD {
        our $AUTOLOAD;
        my $method = substr $AUTOLOAD, 2 + rindex($AUTOLOAD, '::');

        my ($self, @args) = @_;

        # [a]
        (defined $$self) ?
            __PACKAGE__-&amp;gt;wrap( $$self -&amp;gt; $method(@args) ) :     # [a.1]
            $self;                                              # [a.2]
    }

    sub DESTROY {}
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;SafeNav&lt;/code&gt; is a class that wraps all scalar values and equips with &lt;code&gt;AUTOLOAD&lt;/code&gt; for responding to all method calls. &lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;AUTOLOAD&lt;/code&gt; there is the core part of our logic in [a]: If we are not wrapping an &lt;code&gt;undef&lt;/code&gt; value, we call the original method on it, then re-wrap the return value ([a.1]). Or if we are wrapping an &lt;code&gt;undef&lt;/code&gt;, we ignore the method call and just lay down and keep being ourselves ([a.2]).&lt;/p&gt;

&lt;p&gt;Thanks to the mechanism of &lt;code&gt;AUTOLOAD&lt;/code&gt;, the original form of &lt;code&gt;-&amp;gt;a()-&amp;gt;b()-&amp;gt;c()&lt;/code&gt; is kept exactly the same after the rewrite. Let's put both versions side-by-side for a quick comparison:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$res = $o -&amp;gt;a()-&amp;gt;b()-&amp;gt;c();
$res = SafeNav-&amp;gt;wrap($o) -&amp;gt;a()-&amp;gt;b()-&amp;gt;c() -&amp;gt;unwrap();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;wrap()&lt;/code&gt; at the front, together with &lt;code&gt;unwrap()&lt;/code&gt; at the back, form a clear boundary in which &lt;code&gt;SafeNav&lt;/code&gt; is effective. Method calls after &lt;code&gt;unwrap()&lt;/code&gt; are not not guarded by &lt;code&gt;SafeNav&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that, we properly ignore &lt;code&gt;undef&lt;/code&gt; values, nothing more. If other kinds of exceptions are thrown from method a, b, c, the program would correctly abort. In the 3 proposed ways to rewrite the program in this article, the &lt;code&gt;SafeNav&lt;/code&gt; monad is both generic and not adding too much verbosity to the original program.&lt;/p&gt;




&lt;p&gt;Original post: &lt;a href="https://gugod.org/2023/06/perl-safe-navigation-monad-en/"&gt;https://gugod.org/2023/06/perl-safe-navigation-monad-en/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>perl</category>
      <category>monad</category>
      <category>safenav</category>
    </item>
    <item>
      <title>Making Single-binary Release with pp</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Thu, 21 Oct 2021 15:01:08 +0000</pubDate>
      <link>https://dev.to/gugod/making-single-binary-release-with-pp-5171</link>
      <guid>https://dev.to/gugod/making-single-binary-release-with-pp-5171</guid>
      <description>&lt;p&gt;&lt;code&gt;pp&lt;/code&gt; comes with &lt;code&gt;PAR::Packer&lt;/code&gt;, which is a tool for "compiling" a bunch of modules and codes and makes a single binary.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;perldoc pp&lt;/code&gt; already contains a good amount of documentation one can refer to.&lt;/p&gt;

&lt;p&gt;While it works with system perl, I found it even easier to first prepare a directory of &lt;code&gt;local::lib&lt;/code&gt;, then just package that entire directory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pp&lt;/code&gt; tries its best to determine the list of dependencies of the given program in various ways but none of those are guaranteed to be 100% accurate. As matter of fact that guarantee is impossible to make. This is partially due to the fact that a running program can load modules in ways that cannot be easily determined by reading the program source code, or in a relative hidden code path that cannot be easily captured.&lt;/p&gt;

&lt;p&gt;That is a good amount of flexibility, but definitely a pricey one. Although, arguably it is also an issue brought by the tool (&lt;code&gt;pp&lt;/code&gt;, &lt;code&gt;perlcc&lt;/code&gt;, &lt;code&gt;perl2exe&lt;/code&gt;, etc.). I guess that is because the dynamic nature is so convenient as long as all the installation are done right. Having a tool that is able to automically complie all dependencies together was not needed that much. It has definidently needed, that is why we have those tools now, but in the last mile of their completion, lies an undecidable problem.&lt;/p&gt;

&lt;p&gt;So we will need to manually add those missing depedencies to &lt;code&gt;pp&lt;/code&gt; command, which is fine only when the list is small. Since we wish to just pack all the declared dependencies together, we don't care that much even if that's going to make the result a little bigger than it has to be. If we can have the same build script that works everywhere, it is as happy as in Christmas. (An pure metaphoric experssion. Pesonally I feel nothing spceial in Dec 25.)&lt;/p&gt;

&lt;p&gt;Anyway...... it turns out to be much easier to decide the dependency at installation time, since that's all well-declared and tools like &lt;code&gt;cpm&lt;/code&gt;, or &lt;code&gt;cpanm&lt;/code&gt; already does this perfectly. If we install dependencies in a self-contained directory, we could just archive the entire directory together with the program we are packing, and that should be good to go.&lt;/p&gt;

&lt;p&gt;Let's say we cd into the source code of &lt;code&gt;foo&lt;/code&gt; and we are trying to compile the program &lt;code&gt;foo&lt;/code&gt; as a binary. The executable is at &lt;code&gt;bin/foo&lt;/code&gt;, while its own moulders such as &lt;code&gt;Foo.pm&lt;/code&gt;, &lt;code&gt;Foo/Bar.pm&lt;/code&gt; are put under the conventional directory &lt;code&gt;lib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Given that, this script should produce &lt;code&gt;foo&lt;/code&gt; as a single binary that as if all dependencies are "statically-linked" inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# Prepare local/ 
cpanm -L local -q --installdeps .
# or: cpm install

perlversion=$(perl -MConfig -e 'print $Config{version}')
pp -B \
    -I ./local/lib/perl5 \
    -a "./local/lib/perl5/;$perlversion/" \
    -a lib \
    -o foo \
    bin/foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alas, this is almost perfect -- except modules in corelist might still be missing. They won't be inside &lt;code&gt;local/&lt;/code&gt; and if they are somehow not discovered by &lt;code&gt;pp&lt;/code&gt; then they'll be missing in the end result. We won't know this until we manually test the result &lt;code&gt;foo&lt;/code&gt; thoroughly. Basically we should always add a bunch of &lt;code&gt;-M&lt;/code&gt; flags in the build script instead of assuming &lt;code&gt;pp&lt;/code&gt; would do the right thing.&lt;/p&gt;

&lt;p&gt;For example, like so, when all of &lt;code&gt;Getopt::Long&lt;/code&gt;, &lt;code&gt;JSON::PP&lt;/code&gt;, and &lt;code&gt;Sys::Hostname&lt;/code&gt; are required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pp -B \
    -M Getopt::Long:: \
    -M JSON::PP:: \
    -M Sys::Hostname:: \
    -I local/lib/perl5 \
    -a "./local/lib/perl5/;$perlversion/" \
    -a lib \
    -o foo \
    bin/foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A rather tedious modification as the list of dependent modules now exists in two places in the repo. Surely there is some way to refactor this.&lt;/p&gt;

&lt;p&gt;I've verified the following script &lt;code&gt;build-minicpan.sh&lt;/code&gt; that can download the tarball of &lt;code&gt;CPAN::Mini&lt;/code&gt; and build a working &lt;code&gt;minicpan&lt;/code&gt; out of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash
set -e

curl --silent -O https://cpan.metacpan.org/authors/id/R/RJ/RJBS/CPAN-Mini-1.111016.tar.gz

tar -xzf CPAN-Mini-1.111016.tar.gz

cd CPAN-Mini-1.111016

cpanm -n -q -L local --installdeps .

perlversion=$(perl -MConfig -e 'print $Config{version}')

pp -B \
   -M Getopt::Long:: \
   -I ./local/lib/perl5 \
   -a "./local/lib/perl5/;$perlversion/" \
   -a lib \
   -o ../minicpan \
   bin/minicpan

echo "DONE: minicpan"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To me this seems to be an area worth exploring... I've been experimenting the automation of this in a side-project: &lt;a href="https://github.com/gugod/pau"&gt;pau&lt;/a&gt;, which is a collection of shell functions that can install app to their own self-contained directory and expose just the program itself. Very similar to what &lt;a href="https://pypa.github.io/pipx/"&gt;pipx&lt;/a&gt; does. &lt;code&gt;pp&lt;/code&gt; support was added not long ago but still there is no good way to figure out all the missing modules and automatically add them as &lt;code&gt;-M&lt;/code&gt; arguments.&lt;/p&gt;

&lt;p&gt;Maybe as a lazy solution, we should just &lt;em&gt;always&lt;/em&gt; produce a heavy-pack that includes the whole core lib directory (usually &lt;code&gt;$INC[-1]&lt;/code&gt;) regardless whether any of them are used.&lt;/p&gt;

&lt;p&gt;Maybe.&lt;/p&gt;




&lt;p&gt;Originally posted at: &lt;a href="https://gugod.org/2021/10/perl-making-single-binary-release-with-pp/"&gt;gugod's blog -- Making single binary release with pp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>perl</category>
      <category>cpan</category>
      <category>compiling</category>
    </item>
    <item>
      <title>Email for everything ?</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Sun, 17 Oct 2021 13:06:44 +0000</pubDate>
      <link>https://dev.to/gugod/email-for-everything--4517</link>
      <guid>https://dev.to/gugod/email-for-everything--4517</guid>
      <description>&lt;p&gt;Owning an IMAP-capable email account can be very convenient since that&lt;br&gt;
be used as a centralized storage for information around us. After all,&lt;br&gt;
the easiest way to synchronize information across multiple devices is by&lt;br&gt;
sending emails to ourselves.&lt;/p&gt;

&lt;p&gt;Since we already setup our emails on multiple devices, there are no more&lt;br&gt;
accounts to sign-up, no more passwords to be managed.&lt;/p&gt;

&lt;p&gt;Several purposes can be easily satisfied by briefly re-purposing emails&lt;br&gt;
in various ways.&lt;/p&gt;

&lt;p&gt;Note-taking -- quick notes taken on-the-road with mobile devices can be&lt;br&gt;
saved as drafts -- usually in folder named like "Drafts".  The saved&lt;br&gt;
draft can be edited in a full-fledged UI. Since those are draft mails&lt;br&gt;
they can just be re-saved over and over. Basically just like normal&lt;br&gt;
files.&lt;/p&gt;

&lt;p&gt;TODO list -- by conventionally adding TODO in Subject line, we can make&lt;br&gt;
a personal TODO lists. Each TODO item would be one mail, with subject&lt;br&gt;
line being the gist and body being details. Marking things DONE is just&lt;br&gt;
replying the mail with modified title.&lt;/p&gt;

&lt;p&gt;A "Read it Latter" queue -- similar how TODO lists can be&lt;br&gt;
implemented. For each reading we wish to postpone, we mail to ourselves&lt;br&gt;
with the link and perhaps with a gist of what it is about.&lt;/p&gt;

&lt;p&gt;Perhaps with some extra tooling, we can easily send a full copy of the&lt;br&gt;
whole web page so we have the whole thing ready to be consumed when we&lt;br&gt;
are ready. For the moment it seems on macOS, this can be easily done&lt;br&gt;
with Safari + Mail -- we can "Share" the page under "Reader" mode and&lt;br&gt;
Safari will just clip the content out. Very nice.&lt;/p&gt;

&lt;p&gt;These can be all be implement with one IMAP-capable email account. We&lt;br&gt;
would probably have different folders for different type of usages. In&lt;br&gt;
addition, we could also use different alias or &lt;code&gt;+&lt;/code&gt;-suffixes to further&lt;br&gt;
distinguish different contexts too.&lt;/p&gt;

&lt;p&gt;These ideas are about exchanging ideas from our present selves with&lt;br&gt;
our future selves. We write something and let future us to read,&lt;br&gt;
follow-up, react.&lt;/p&gt;

&lt;p&gt;If we self-host IMAP servers and configure it to share certain folders&lt;br&gt;
to multiple users. Besides emailing to each other, users can discover&lt;br&gt;
new information by browsing those shared folders. Should user be able&lt;br&gt;
to directly post new messages to those folders, that is a basic BBS.&lt;br&gt;
In a sense this setup is conceptually the same having a single NNTP&lt;br&gt;
server for that group of user.&lt;/p&gt;

&lt;p&gt;If each users can own one dedicated folder that is specially&lt;br&gt;
configured so that new posts can be made only by its owner, while&lt;br&gt;
replies can be posted by everyone -- that is a multi-blog service,&lt;br&gt;
or a SNS.&lt;/p&gt;

&lt;p&gt;Suppose such IMAP-based SNS exists... since users are required to use&lt;br&gt;
email client to access the SNS and since advanced email client all&lt;br&gt;
have search/filtering feature, users can fine-tune the so called&lt;br&gt;
"algorithm" at the client side to boost posts however they like.&lt;/p&gt;

&lt;p&gt;Since this requires users to do a whole lot more to fine tune "the&lt;br&gt;
algorithm" on the client side, it also means to give users more&lt;br&gt;
freedom and less there can be less chance of having some centralized&lt;br&gt;
recommendation system in between content and eyeballs.&lt;/p&gt;

&lt;p&gt;Wouldn't that be nice ?&lt;/p&gt;

&lt;p&gt;
--&lt;br&gt;
Best,&lt;br&gt;
Kang-min Liu
&lt;/p&gt;




&lt;p&gt;Originall posted at &lt;a href="https://gugod.org/2021/10/email-for-everything/"&gt;gugod's blog -- Email for everything ?&lt;/a&gt;&lt;/p&gt;

</description>
      <category>email</category>
      <category>ideas</category>
    </item>
    <item>
      <title>CPAN Release of TooMuchCode 0.18</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Wed, 29 Sep 2021 00:32:55 +0000</pubDate>
      <link>https://dev.to/gugod/cpan-release-of-toomuchcode-0-18-a8</link>
      <guid>https://dev.to/gugod/cpan-release-of-toomuchcode-0-18-a8</guid>
      <description>&lt;p&gt;&lt;a href="https://metacpan.org/dist/Perl-Critic-TooMuchCode"&gt;Perl::Critic::TooMuchCode&lt;/a&gt; is a set of policy addons that generally checks for dead code or redundant code.&lt;/p&gt;

&lt;p&gt;It's just about a week after &lt;a href="https://gugod.org/2021/09/cpan-release-of-toomuchcode-0.17/"&gt;the release of 0.17&lt;/a&gt; and after asynchronously exchanged some thoughts with &lt;a href="https://github.com/oalders"&gt;@oalders&lt;/a&gt; and &lt;a href="https://github.com/ferki"&gt;@ferki&lt;/a&gt; (&lt;a href="https://github.com/gugod/Perl-Critic-TooMuchCode/issues/25"&gt;Issue #25&lt;/a&gt;), I decide to make a follow-up release shortly.&lt;/p&gt;

&lt;p&gt;In version 0.17 we updated the policy ProhibitDuplicateLiteral to include a new config parameter &lt;code&gt;whitelist&lt;/code&gt; to replace the old parameter &lt;code&gt;whitelist_number&lt;/code&gt;. In version 0.18 they are both removed. The replacement for them is the parameter &lt;code&gt;allowlist&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So now if we really need to write number 42 and "forty two" literally many times, we list them in &lt;code&gt;.perlcriticrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[TooMuchCode::ProhibitDuplicateLiteral]
allowlist = "forty two" 42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name "allowlist" was previously suggested by &lt;a href="https://github.com/ferki"&gt;@ferki&lt;/a&gt; already and I amended to match existing convention of using "whitelist". Now I decide that such convention should be changed (See &lt;a href="https://github.com/gugod/Perl-Critic-TooMuchCode/pull/28"&gt;PR #28&lt;/a&gt;). It's a better naming choice in the sense that it direct and explicit, perhaps until the day the word "allow" lost its current meaning.&lt;/p&gt;

&lt;p&gt;The other improvement is to address &lt;a href="https://github.com/gugod/Perl-Critic-TooMuchCode/issues/19"&gt;Issue 18&lt;/a&gt; (thanks to &lt;a href="https://github.com/oalders"&gt;@oalders&lt;/a&gt; again.), in which assignment of imported symbols to &lt;code&gt;@EXPORT&lt;/code&gt; or &lt;code&gt;@EXPORT_OK&lt;/code&gt; as correctly counted as using those symbol by the policy ProhibitUnusedImport.&lt;/p&gt;

&lt;p&gt;There have not counted because it is also a correct thing to do. Considering we have three symbols in place: &lt;code&gt;foo&lt;/code&gt;, &lt;code&gt;$bar&lt;/code&gt;, and &lt;code&gt;@baz&lt;/code&gt;, and an assignment like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my @stuff = qw(foo $bar @baz);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although those symbols appears in the statement literally, evaluating that statement does not depends on the evaluation of those symbols because they are all quoted inside of &lt;code&gt;qw()&lt;/code&gt;. &lt;code&gt;@stuff&lt;/code&gt; is evaluated to 3 strings: &lt;code&gt;'foo'&lt;/code&gt;, &lt;code&gt;'$bar'&lt;/code&gt;, and &lt;code&gt;'@baz'&lt;/code&gt;, which has nothing to do with &lt;code&gt;foo&lt;/code&gt; (bareword, perhaps a subroutine name), &lt;code&gt;$bar&lt;/code&gt;, and '@baz'.&lt;/p&gt;

&lt;p&gt;However, if the LHS array variable is either &lt;code&gt;@EXPORT&lt;/code&gt; or &lt;code&gt;@EXPORT_OK&lt;/code&gt;, the assignment has different meaning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;our @EXPORT = qw(foo $bar @baz);
our @EXPORT_OK = qw(@baz);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@EXPORT&lt;/code&gt; itself is still assigned with just 3 strings, but eventually when the current module is imported (&lt;code&gt;use&lt;/code&gt;-ed or &lt;code&gt;require&lt;/code&gt;-ed) by a foreign piece of code, those 3 strings refer some content in the symbol table and they might be used remotely.&lt;/p&gt;

&lt;p&gt;Well, whether &lt;code&gt;foo&lt;/code&gt; is really used remotely is probably unknown-able. It should be sufficient to just consider the case of assignments involving &lt;code&gt;@EXPORT&lt;/code&gt; and &lt;code&gt;@EXPORT_OK&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I'm guessing that there are probably more special cases yet to be discovered regarding those special variables in &lt;a href="https://perldoc.pl/perlvar"&gt;perlvar&lt;/a&gt;, or however famous module X defined its own sub-language for us to cope with.&lt;/p&gt;




&lt;p&gt;Originally posted at &lt;a href="https://gugod.org/2021/09/cpan-release-of-toomuchcode-0.18/"&gt;gugod's blog -- CPAN Release of TooMuchCode 0.18&lt;/a&gt;&lt;/p&gt;

</description>
      <category>perl</category>
      <category>cpan</category>
    </item>
    <item>
      <title>CPAN installation as a test, with GitHub workflow</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Wed, 22 Sep 2021 00:48:05 +0000</pubDate>
      <link>https://dev.to/gugod/cpan-installation-as-a-test-with-github-workflow-3gm6</link>
      <guid>https://dev.to/gugod/cpan-installation-as-a-test-with-github-workflow-3gm6</guid>
      <description>&lt;p&gt;If I made a distribution Foo and uploaded it to CPAN, I'd expect that it is install-able via various CPAN clients such as &lt;code&gt;cpan&lt;/code&gt;, &lt;code&gt;cpanm&lt;/code&gt;, &lt;code&gt;cpm&lt;/code&gt;, that is, from a fresh perl installation, these commands should be successful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cpan Foo
cpanm Foo
cpm install -g Foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's a bit non-obvious is that this requires a chain of conventions to be satisfied in order to happen smoothly... distribution Foo needs to properly ship with build procedure / installer and &lt;code&gt;META.json&lt;/code&gt;, in which all dependencies are correctly declared.&lt;/p&gt;

&lt;p&gt;But that is not enough, even if I correctly authored all the meta-data in distribution Foo, if there is even a single miss in any other dependencies in Foo's dependency tree, the installation would fail.&lt;/p&gt;

&lt;p&gt;For that reason, I thought it might be worth it to test the installation process during development, at least make a CI workflow that tries to install everything. The successful of such test would just be whether the installation is successfully finished or not. Conventionally the exit status (&lt;code&gt;$?&lt;/code&gt;) reflects that.&lt;/p&gt;

&lt;p&gt;Here's something I came up with. It is a GitHub workflow that first build a distribution, a &lt;code&gt;.tar.gz&lt;/code&gt; file, then try to install that file with &lt;code&gt;cpanm&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Installation Test

on:
  push:
    branches:
      - fun

jobs:
  build:
    runs-on: 'ubuntu-latest'
    steps:
      - uses: actions/checkout@v2
      - name: Setup Perl environment
        uses: shogo82148/actions-setup-perl@v1
      - name: Install Authoring Dependencies
        run: cpanm -n App::ModuleBuildTiny &amp;amp;&amp;amp; cpanm -q -n --no-man-pages --installdeps .
      - name: Generate dist
        run: mbtiny dist
      - name: Display what is generated
        run: |-
          echo *.tar.gz
          tar tvzf *.tar.gz
      - uses: actions/upload-artifact@v2
        with:
          name: dist-for-installation-test
          path: '*.tar.gz'
          retention-days: 1
  install:
    runs-on: 'ubuntu-latest'
    needs: build
    container:
      image: perl:5.34
    steps:
      - uses: actions/download-artifact@v2
        with:
          name: dist-for-installation-test
      - name: Display the downloaded files
        run: ls -R
      - name: Install in a perl:5.34 container (--notest)
        run: cpanm --notest *.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a lot of instructions but what matters is the final one &lt;code&gt;cpanm --notest *.tar.gz&lt;/code&gt;. This would install whatever was built and all its dependencies, without running module tests`, and if even that failed, that means some meta data is missing and the distribution would not be install-able even if it is uploaded to CPAN.&lt;/p&gt;

&lt;p&gt;Of course that is just &lt;code&gt;cpanm&lt;/code&gt;, I could also add &lt;code&gt;cpan&lt;/code&gt; and &lt;code&gt;cpm&lt;/code&gt; there to verify whether there is a difference between all these CPAN clients. Also, I could setup a matrix run so it runs on all perl versions.&lt;/p&gt;

&lt;p&gt;This workflow is currently used in &lt;a href="https://github.com/gugod/Perl-Critic-TooMuchCode"&gt;Perl-Critic-TooMuchCode&lt;/a&gt;, with a few runs already finished &lt;a href="https://github.com/gugod/Perl-Critic-TooMuchCode/actions/workflows/installation-test.yml"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The workflow contains two jobs, &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;install&lt;/code&gt;. &lt;code&gt;install&lt;/code&gt; needs &lt;code&gt;build&lt;/code&gt; so it is always executed after successful &lt;code&gt;build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;build&lt;/code&gt; job runs on the default &lt;code&gt;ubuntu-latest&lt;/code&gt; machine, but the &lt;code&gt;install&lt;/code&gt; job runs in a container instance with presumably a different version of perl, and also without a clone of current repo. With this setup I can simulate the installation process on a fresh machine.&lt;/p&gt;

&lt;p&gt;To share the distribution &lt;code&gt;.tar.gz&lt;/code&gt; file across different jobs, the only way I can fin is by uploading the file to the "artifact" storage -- basically an external storage. This probably cost me something if a lot of files are accumulated. I changed the &lt;code&gt;retention&lt;/code&gt; period to 1 day because I don't plan to download this anyway.&lt;/p&gt;

&lt;p&gt;On the other hand, it could be part of doing the actual CPAN release. Maybe with some modification the workflow would build a new version with new version number, and I would just have to download the artifact then re-upload to CPAN. This would save the setup of preparing authoring tools. It could be a useful scenario for teaching new developers to upload something to CPAN.&lt;/p&gt;

&lt;p&gt;The installation process also download a lot of stuffs from cpan.org and cost some bandwidth on the way. It's best not to do so on every commits or on a branch with frequent pushes. Definitely suitable pre-release though.&lt;/p&gt;

&lt;p&gt;Most likely, this idea of "Installation as a test" isn't new, and it is a bit convenient to have it checked in CI.&lt;/p&gt;




&lt;p&gt;Originally posted on &lt;a href="https://gugod.org/2021/09/en-cpan-installation-as-a-test-with-github-workflow/"&gt;gugod's blog -- CPAN installation as a test, with GitHub workflow&lt;/a&gt;&lt;/p&gt;

</description>
      <category>perl</category>
      <category>cpan</category>
      <category>installation</category>
      <category>testing</category>
    </item>
    <item>
      <title>CPAN Release of TooMuchCode 0.17</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Mon, 20 Sep 2021 23:05:15 +0000</pubDate>
      <link>https://dev.to/gugod/cpan-release-of-toomuchcode-0-17-3e6m</link>
      <guid>https://dev.to/gugod/cpan-release-of-toomuchcode-0-17-3e6m</guid>
      <description>&lt;p&gt;&lt;a href="https://metacpan.org/dist/Perl-Critic-TooMuchCode"&gt;Perl::Critic::TooMuchCode&lt;/a&gt; is a set of policy addons that generally checks for dead code or redundant code.&lt;/p&gt;

&lt;p&gt;I feel grateful that this small project starts to draw some attention and endorsement and now it is receiving pull-requests from the Perl/CPAN community.&lt;/p&gt;

&lt;p&gt;In version 0.17 we improved the policy ProhibitDuplicateLiteral and now it we can whitelist strings and numbers in configurations. If you somehow really need to use number 42 and "forty two" literally in the code many times, you list them in &lt;code&gt;.perlcriticrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[TooMuchCode::ProhibitDuplicateLiteral]
whitelist = "forty two" 42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to &lt;a href="https://github.com/ferki"&gt;@ferki&lt;/a&gt; from project &lt;a href="https://www.rexify.org/"&gt;Rex&lt;/a&gt;! &lt;/p&gt;

&lt;p&gt;Rex is an automation framework, or remote-execution framework. In a sense, similar to Ansible. It's a simple way of telling machine what to do, with some code named Rexfile -- a task manifest. which is also perl code. Within which you'd repeat some strings literally such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service 'apache2',  ensure =&amp;gt; 'started';
service 'mysql',    ensure =&amp;gt; 'started';
service 'memcached, ensure =&amp;gt; 'started';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... and that is OK. Because those manifests should be direct instead of indirect/abstract with the use of variables / constants to just hold the string &lt;code&gt;'started'&lt;/code&gt;. Doing such is an unnecessary level of indirection since the purpose of task manifests is to tell machine what to do and we do not want to make human readers read twice just to understand what those statement means. After all, repeatition by itself isn't necessarily the problem.&lt;/p&gt;

&lt;p&gt;Well, apparently in the context of coding Rexfile, some repetition are allowed. In general, perhaps repetition are OK in any DSL code. DSL itself already reduce some repetition by hiding the details and DSL keywords tends to be high-level constructs that are designed to be easily understandable for human readers. Repetition of DSL keywords almost never leads to be unreadable or "bad small".&lt;/p&gt;

&lt;p&gt;I might have repeated the word "repetition" too many times.&lt;/p&gt;




&lt;p&gt;Originally posted at: &lt;a href="https://gugod.org/2021/09/cpan-release-of-toomuchcode-0.17/"&gt;gugod's blog -- CPAN Release of TooMuchCode 0.17&lt;/a&gt;&lt;/p&gt;

</description>
      <category>perl</category>
      <category>cpan</category>
    </item>
    <item>
      <title>CPAN Release of Time::Verbal module</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Wed, 08 Sep 2021 15:09:37 +0000</pubDate>
      <link>https://dev.to/gugod/cpan-release-of-time-verbal-module-1ecl</link>
      <guid>https://dev.to/gugod/cpan-release-of-time-verbal-module-1ecl</guid>
      <description>&lt;p&gt;&lt;a href="https://metacpan.org/pod/Time::Verbal"&gt;Time::Verbal&lt;/a&gt; 1.1.1 made its way to CPAN.&lt;/p&gt;

&lt;p&gt;This module does one little thing: converting time durations to verbal formats, rounded to some "sensible" unit. Just as how you would say such thing as a human.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my o = Time::Verbal-&amp;gt;new();
say $o-&amp;gt;distance( time(), time() - 4567 );
#=&amp;gt; about 1 hour
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides the default English format, other 67 languages identified by these ISO language codes are supported:&lt;/p&gt;

&lt;p&gt;ar bg bn-IN bs ca cy da de-AT de-CH de dsb el en-AU en-GB en-US eo es-AR es-CL es-CO es-MX es-PE es et eu fa fi fr-CA fr-CH fr fur gl-ES gsw-CH he hi-IN hi hr hsb hu id is it ja ko lo lt lv mk mn nb nl nn pl pt-BR pt-PT rm ro ru sk sl sr-Latn sr sv-SE sw tr uk vi zh-CN zh-TW&lt;/p&gt;

&lt;p&gt;I don't know all these languages (nor their language codes), but &lt;a href="https://metacpan.org/pod/Locale::Wolowitz"&gt;Locale::Wolowiz&lt;/a&gt; does. Or, at least it is the library that does the translation, given a bunch of JSON files that contain the body of translations.&lt;/p&gt;

&lt;p&gt;So here's how you say "about 1 hour" in Vietnamese:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my o = Time::Verbal-&amp;gt;new( "locale" =&amp;gt; "vi" );
say $o-&amp;gt;distance( time(), time() - 4567 );
#=&amp;gt; khoảng 1 giờ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A default version of translations of all the above-mentioned 68 languages are provided  with &lt;code&gt;Time::Verbal&lt;/code&gt; itself and is ready to use once installed properly -- a usual &lt;code&gt;cpan&lt;/code&gt; or &lt;code&gt;cpanm&lt;/code&gt; or &lt;code&gt;cpm&lt;/code&gt; command would suffice. However, in case some customize version is need because, say, you wish to provide better UX to whom speak the Vulcan variant of Klingon, you need to create a file named after the language code -- which can be arbitrary such as &lt;code&gt;tlh-Vulcan&lt;/code&gt;. You would create &lt;code&gt;tlh-Vulcan.json&lt;/code&gt; and put it under &lt;code&gt;/app/i18n&lt;/code&gt;, or really any directory of your choice, then do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my o = Time::Verbal-&amp;gt;new(
    "locale" =&amp;gt; "tlh-Vulcan",
    "i18n_dir" =&amp;gt; "/app/i18n"
);
say $o-&amp;gt;distance( time(), time() - 4567 );
#=&amp;gt; Chaq wa' rep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Disclaimer -- I don't know if that's how Vulcanized Klingon speaks. Don't take my example as if it is authentic.&lt;/p&gt;

&lt;p&gt;To prepare the translation, take a look of the json files under the default &lt;code&gt;i18n_dir&lt;/code&gt;, which can be printed by running this oneliner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;perl -MTime::Verbal -E 'say Time::Verbal-&amp;gt;new()-&amp;gt;i18n_dir'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It shouldn't be difficult to grasp the structure of those translation files and produce a new one. It is however, very difficult to start describing the schema of thoes json file as an indolent developer.&lt;/p&gt;

&lt;p&gt;The translations are accquired from &lt;a href="https://github.com/svenfuchs/rails-i18n"&gt;rails-i18n&lt;/a&gt; project -- which is one of the first modules that does things like this.&lt;/p&gt;




&lt;p&gt;Originally posted at &lt;a href="https://gugod.org/2021/09/cpan-release-of-time-verbal/"&gt;https://gugod.org/2021/09/cpan-release-of-time-verbal/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>perl</category>
      <category>cpan</category>
    </item>
    <item>
      <title>A simple thought about key-value pairs</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Fri, 03 Sep 2021 23:54:17 +0000</pubDate>
      <link>https://dev.to/gugod/a-simple-thought-about-key-value-pairs-1bap</link>
      <guid>https://dev.to/gugod/a-simple-thought-about-key-value-pairs-1bap</guid>
      <description>&lt;p&gt;Very often, we wrote Web API that produce JSON output and also very often, we want to ensure that certain values in the output are produced as numerical literals (&lt;code&gt;42&lt;/code&gt; instead of &lt;code&gt;"42"&lt;/code&gt;) or as boolean literals (&lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;). The only reliable way, imho, is to cast them when printing the JSON output, or when preparing the structure for that output -- that is basically as "late" as possible.&lt;/p&gt;

&lt;p&gt;Since we also often produce JSON with &lt;code&gt;JSON&lt;/code&gt; module, we may have a &lt;code&gt;TO_JSON&lt;/code&gt; around that looks like this, with many inline type-coercion expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;TO_JSON&lt;/span&gt; &lt;span class="p"&gt;($self) {&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;# string&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;align&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;""&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;align&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="c1"&gt;# number&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;height&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;width&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="c1"&gt;# boolean&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;is_closed&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is_landscape&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fits&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;LIMIT&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the unavoidable fate comes and we include more and more attributes in the &lt;code&gt;TO_JSON&lt;/code&gt;, such inline expressions becomes bulky and a bit tedious to look at... just imagine the situation of having a few dozens of inline ternary expressions not perfectly aligned and one of them being wrong. It would be nicer to make them look good and make those inline expressions look like some sort of annotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;TO_JSON&lt;/span&gt; &lt;span class="p"&gt;($self) {&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;align&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;json_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;align&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;height&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;json_num&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;width&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;json_num&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;

        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;json_bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;is_closed&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is_landscape&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;json_bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fits&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;json_bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;LIMIT&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These helper methods that does type coercion by themselfs are trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;json_str&lt;/span&gt; &lt;span class="p"&gt;($x) {&lt;/span&gt; &lt;span class="p"&gt;""&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;json_num&lt;/span&gt; &lt;span class="p"&gt;($x) {&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;json_bool&lt;/span&gt; &lt;span class="p"&gt;($x) {&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, perhaps even better, we could annotate the attributes in groups, arranged by their expected type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;TO_JSON&lt;/span&gt; &lt;span class="p"&gt;($self) {&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;kvpairs_json_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;align&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;align&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;

        &lt;span class="nv"&gt;kvpairs_json_num&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;height&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;width&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;

        &lt;span class="nv"&gt;kvpairs_json_bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;is_closed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is_landscape&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fits&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;LIMIT&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;pairmap&lt;/code&gt; function from &lt;code&gt;List::Util&lt;/code&gt;, the new helper methods looks also trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;List::&lt;/span&gt;&lt;span class="nv"&gt;Util&lt;/span&gt; &lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;pairmap&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;

&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;kvpairs_json_str&lt;/span&gt; &lt;span class="p"&gt;(@kvlist) {&lt;/span&gt;
    &lt;span class="nv"&gt;pairmap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;json_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;pairs&lt;/span&gt; &lt;span class="nv"&gt;@kvlist&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;kvpairs_json_num&lt;/span&gt; &lt;span class="p"&gt;(@kvlist) {&lt;/span&gt;
    &lt;span class="nv"&gt;pairmap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;json_num&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;pairs&lt;/span&gt; &lt;span class="nv"&gt;@kvlist&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;kvpairs_json_bool&lt;/span&gt; &lt;span class="p"&gt;(@kvlist) {&lt;/span&gt;
    &lt;span class="nv"&gt;pairmap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;json_bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;pairs&lt;/span&gt; &lt;span class="nv"&gt;@kvlist&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since for our purpose of printing object as JSON, keys can be ignored because they are always string literals directly written as part of our code. We only have to deal all the values which are every 2nd elements in &lt;code&gt;@kvlist&lt;/code&gt;. Also, because the overall result of &lt;code&gt;TO_JSON&lt;/code&gt; is a HashRef, the order of keys as they are written in the code is irrelevant and that allows us to arrange those key-value pairs in groups so overall it looks nicer and organized.&lt;/p&gt;

&lt;p&gt;Meanwhile, we also often hide partial kvpairs given some condition because some attributes are optional and perhaps convey special meaning when they are part of the output. A typical example would be the the conventional "error" attribute in API responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$o&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$o&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):(),&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a ternary op with an ever weirder look. I wonder if this is better:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;kvpairs_without_undef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$o&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is like marking a section of kvpairs as optional and exclude all pairs with &lt;code&gt;undef&lt;/code&gt; value. The helper function &lt;code&gt;kvpairs_without_undef&lt;/code&gt; can be done with the help of &lt;code&gt;pairgrep&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;List::&lt;/span&gt;&lt;span class="nv"&gt;Util&lt;/span&gt; &lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;pairgrep&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;

&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;kvpairs_without_undef&lt;/span&gt; &lt;span class="p"&gt;(@kvlist) {&lt;/span&gt;
    &lt;span class="nv"&gt;pairgrep&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;@kvlist&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is probably doing a lot of copying and perhaps there are some performance enhancements to be done. But well, these are just thoughts, optimizing thoughts is even before the so called premature optimization.&lt;/p&gt;

&lt;p&gt;(Perhaps that's fun though.)&lt;/p&gt;




&lt;p&gt;Originally published at: &lt;a href="https://gugod.org/2021/07/perl-simple-thoughts-kvpairs/"&gt;https://gugod.org/2021/07/perl-simple-thoughts-kvpairs/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>perl</category>
      <category>json</category>
      <category>kvpairs</category>
    </item>
    <item>
      <title>Visualization of perl development history</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Thu, 10 Jun 2021 06:32:06 +0000</pubDate>
      <link>https://dev.to/gugod/visualization-of-perl-development-history-4afm</link>
      <guid>https://dev.to/gugod/visualization-of-perl-development-history-4afm</guid>
      <description>&lt;p&gt;About 10 years ago I tried &lt;a href="https://gource.io/"&gt;gource&lt;/a&gt; and made a visualization of the commit history of &lt;a href="https://github.com/Perl/perl5"&gt;perl5.git&lt;/a&gt;. It was when grouce was new, shiny and cool: &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/jl9HVEJl_-w"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;10 years passed like a blink and it has been a remarkable decade for perl5.git. As we see the growth of commit volume, there is also stable pace of releasees thanks to the standardization of release procedure. With relocation of communication platform, participation and small fixes seems to be much easier. While it has not be a perfectly stable decade, it is a good one.&lt;/p&gt;

&lt;p&gt;So as a 10-yearly review, here's the updated version of that gource visualization on perl5.git, all the way to mid 2021: &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/8udGUs7TnEM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;These are the commands used to produce that video:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~/src/perl5
gource -s 0.01 --auto-skip-seconds 0.1 -r 30 -f --user-scale 3 --file-idle-time 0 --hide bloom,filenames,dirnames --date-format '%Y' -o ~/var/gource-perl.ppm
ffmpeg  -an -y -f image2pipe -vcodec ppm -i ~/var/gource-perl.ppm -r 30.000 ~/var/gource-out.webm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;本篇文章為 &lt;a href="https://gugod.org/2021/06/perl-development-history-visualized/"&gt;perl 開發歷史紀錄之視覺化&lt;/a&gt; 之英文版&lt;/p&gt;

</description>
      <category>perl</category>
      <category>gource</category>
    </item>
    <item>
      <title>Solving Perl Weekly Challenge 096 -- Reverse Words and Edit distance.</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Thu, 21 Jan 2021 12:25:05 +0000</pubDate>
      <link>https://dev.to/gugod/solving-perl-weekly-challenge-096-reverse-words-and-edit-distance-42lk</link>
      <guid>https://dev.to/gugod/solving-perl-weekly-challenge-096-reverse-words-and-edit-distance-42lk</guid>
      <description>&lt;p&gt;This week, &lt;a href="https://perlweeklychallenge.org/blog/perl-weekly-challenge-096/"&gt;Perl Weekly Challenge 096&lt;/a&gt; gives us two tasks about string manipulation. Besides Raku, let's try &lt;a href="https://janet-lang.org/"&gt;janet&lt;/a&gt; and &lt;a href="https://www.rust-lang.org/"&gt;rust&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The algorithm for computing edit distance is of pratcial uses, such as in &lt;code&gt;git&lt;/code&gt;: &lt;a href="https://github.com/git/git/blob/master/levenshtein.c"&gt;git/levenshtein&lt;/a&gt;, for recomending commands when user runs an unknown command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# git lag
git: 'lag' is not a git command. See 'git --help'.

The most similar commands are
    log
    tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same as in perlbrew: &lt;a href="https://github.com/gugod/App-perlbrew/blob/develop/lib/App/Perlbrew/Util.pm#L23"&gt;perlbrew/editdist&lt;/a&gt;. Meanwhile in &lt;code&gt;rakudo&lt;/code&gt;, the compiler of Raku language: &lt;a href="https://github.com/rakudo/rakudo/blob/master/src/Perl6/World.nqp#L61"&gt;rakudo/levenshtein&lt;/a&gt;, for recommending subroutines when seeing unknown subroutine name in the code. Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# raku -e 'pay "hello"'
===SORRY!=== Error while compiling -e
Undeclared routine:
    pay used at line 1. Did you mean 'say'?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a difference though. In git, the algorithm is 'Damerau-Levenshtein distance'. While in perlbrew and raku, the algorithm is ' levenshtein distance'. The former sees character-swaping as one operation, the latter does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  TASK #1 › Reverse Words
&lt;/h2&gt;

&lt;p&gt;Submitted by: Mohammad S Anwar&lt;/p&gt;

&lt;p&gt;You are given a string $S.&lt;/p&gt;

&lt;p&gt;Write a script to reverse the order of words in the given string. The string may contain leading/trailing spaces. The string may have more than one space between words in the string. Print the result without leading/trailing spaces and there should be only one space between words.&lt;/p&gt;

&lt;p&gt;Example 1:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input: $S = "The Weekly Challenge"
Output: "Challenge Weekly The"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example 2:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input: $S = "    Perl and   Raku are  part of the same family  "
Output: "family same the of part are Raku and Perl"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Solution #1 &amp;gt; Revrse Words
&lt;/h2&gt;

&lt;p&gt;For this task, we first split the string by whitespacse to get a collection words, and re-join them in reverse order.&lt;/p&gt;

&lt;p&gt;Raku:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sub reverse-words (Str $S --&amp;gt; Str) {
    $S.words.reverse.join(" ");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Janet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight janet"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reverse-words&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"Reverse the string word by word"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;string/join&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;reverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;string/split&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rust:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;reverse_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="nf"&gt;.split_whitespace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.rev&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="py"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turns out it looks more or less the same in these 3 languages. The &lt;code&gt;string/split&lt;/code&gt; in janet producess one empty string when encountering two consecutive whitespaces, therefore an extra &lt;code&gt;filter&lt;/code&gt; is required to remove those extra empty strings.&lt;/p&gt;

&lt;h2&gt;
  
  
  TASK #2 › Edit Distance
&lt;/h2&gt;

&lt;p&gt;Submitted by: Mohammad S Anwar&lt;/p&gt;

&lt;p&gt;You are given two strings $S1 and $S2.&lt;/p&gt;

&lt;p&gt;Write a script to find out the minimum operations required to convert $S1 into $S2. The operations can be insert, remove or replace a character. Please check out Wikipedia page for more information.&lt;/p&gt;

&lt;p&gt;Example 1:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input: $S1 = "kitten"; $S2 = "sitting"
Output: 3

Operation 1: replace 'k' with 's'
Operation 2: replace 'e' with 'i'
Operation 3: insert 'g' at the end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example 2:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input: $S1 = "sunday"; $S2 = "monday"
Output: 2

Operation 1: replace 's' with 'm'
Operation 2: replace 'u' with 'o'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Solution #2 &amp;gt; Edit Distance
&lt;/h2&gt;

&lt;p&gt;In the following, a navie recursive implementation of Levenshtein distance is provided, basically follow the definition from Wikipedia: &lt;a href="https://en.wikipedia.org/wiki/Levenshtein_distance#Definition"&gt;Levenshtein-distance#Definition&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="/images/lev-def.svg" class="article-body-image-wrapper"&gt;&lt;img src="/images/lev-def.svg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is pretty straight forward this way. However, the implementations from the abovementioned projects are all done with Dynamic Programming. When the inputs (S1 or S2) are lengthy, the about of computation should be greatly reduced.&lt;/p&gt;

&lt;p&gt;Raku:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sub edit-distance (Str $S1, Str $S2 --&amp;gt; Int) {
    my sub lev ($a, $b) {
        return $a.chars if $b.chars == 0;
        return $b.chars if $a.chars == 0;
        return lev( $a.substr(1), $b.substr(1) ) if $a.substr(0,1) eq $b.substr(0,1);
        return 1 + (
            lev($a, $b.substr(1)),
            lev($a.substr(1), $b),
            lev($a.substr(1), $b.substr(1)),
        ).min;
    };

    return lev($S1, $S2);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Janet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(defn lev
  "Compute the Levenshtein distance between string a and b"
  [a b]
    (cond (= 0 (length a)) (length b)
          (= 0 (length b)) (length a)
          (let [
                a_head (string/slice a 0 1)
                a_tail (string/slice a 1)
                b_head (string/slice b 0 1)
                b_tail (string/slice b 1)
                levtail (lev a_tail b_tail)
                ]
            (if (= a_head b_head) levtail
                (+ 1 (min
                      (lev a b_tail)
                      (lev a_tail b)
                      levtail))))))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rust:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;edit_distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;chars_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="nf"&gt;.chars&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;chars_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="nf"&gt;.chars&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;lev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;chars_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;chars_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;lev&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset_a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset_b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;offset_a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;offset_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;offset_b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;offset_a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;n3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset_a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset_b&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;offset_a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;offset_b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;n1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset_a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset_b&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset_b&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.min&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1_u32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;本文為《&lt;a href="https://gugod.org/2021/01/pwc-096/"&gt;解 Perl Weekly Challenge 096 -- 單字次序倒轉與兩字串編輯距離&lt;/a&gt;》之英文版。&lt;/p&gt;

</description>
      <category>raku</category>
      <category>janet</category>
      <category>rust</category>
    </item>
    <item>
      <title>Solving Perl Weekly Challenge 095 -- Palindrome Number and Stack</title>
      <dc:creator>Kang-min Liu</dc:creator>
      <pubDate>Mon, 11 Jan 2021 13:04:08 +0000</pubDate>
      <link>https://dev.to/gugod/solving-perl-weekly-challenge-095-palindrome-number-and-stack-3njh</link>
      <guid>https://dev.to/gugod/solving-perl-weekly-challenge-095-palindrome-number-and-stack-3njh</guid>
      <description>&lt;p&gt;Two basic tasks from &lt;a href="https://perlweeklychallenge.org/blog/perl-weekly-challenge-095/"&gt;Perl Weekly Challenge 095&lt;/a&gt;. Palindrome number is also on leetcode: &lt;a href="https://leetcode.com/problems/palindrome-number/"&gt;leetcode/palindrome-number&lt;/a&gt;. For languages that coerce values to Int / Str automatically, these two tasks does not require too much code.&lt;/p&gt;

&lt;h2&gt;
  
  
  TASK #1 › Palindrome Number
&lt;/h2&gt;

&lt;p&gt;Submitted by: Mohammad S Anwar&lt;/p&gt;

&lt;p&gt;You are given a number $N.&lt;/p&gt;

&lt;p&gt;Write a script to figure out if the given number is Palindrome. Print 1 if true otherwise 0.&lt;/p&gt;

&lt;p&gt;Example 1:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input: 1221
Output: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example 2:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input: -101
Output: 0, since -101 and 101- are not the same.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Example 3:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input: 90
Output: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Solution #1 &amp;gt; Palindrome Number
&lt;/h2&gt;

&lt;p&gt;It is pretty straightforward: simply convert the input number to string, then check whether that string is palindromic. Based on the example, all negative numbers are non-palindromic. I wonder numbers with decimal points are considered.&lt;/p&gt;

&lt;p&gt;Let's say we are only dealing with integers. Here's the implementation in Raku.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sub is-palindrome-number (Int $n --&amp;gt; Bool) {
    return "$n" eq "$n".flip;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The operation of flipping a string corresponds to the &lt;code&gt;flip&lt;/code&gt; subroutine from &lt;code&gt;Str&lt;/code&gt; in Raku. To convert a number &lt;code&gt;$n&lt;/code&gt; to string, either &lt;code&gt;$n.Str&lt;/code&gt; or &lt;code&gt;"$n"&lt;/code&gt; would do. The part where it says &lt;code&gt;(Int $n --&amp;gt; Bool)&lt;/code&gt; is the signature of this subroutine. It means that the subroutine takes one &lt;code&gt;Int&lt;/code&gt; argument, and returns a &lt;code&gt;Bool&lt;/code&gt; (Boolean values. &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  TASK #2 › Demo Stack
&lt;/h2&gt;

&lt;p&gt;Submitted by: Mohammad S Anwar&lt;/p&gt;

&lt;p&gt;Write a script to demonstrate Stack operations like below:&lt;br&gt;
push($n) - add $n to the stack&lt;br&gt;
pop() - remove the top element&lt;br&gt;
top() - get the top element&lt;br&gt;
min() - return the minimum element&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my $stack = Stack-&amp;gt;new;
$stack-&amp;gt;push(2);
$stack-&amp;gt;push(-1);
$stack-&amp;gt;push(0);
$stack-&amp;gt;pop;       # removes 0
print $stack-&amp;gt;top; # prints -1
$stack-&amp;gt;push(0);
print $stack-&amp;gt;min; # prints -1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Solution #2 &amp;gt; Demo Stack
&lt;/h2&gt;

&lt;p&gt;If we are just defining a class with the abovementioned methods, they can all be delegated to one subroutine in Raku. Let's use this &lt;code&gt;IntStack&lt;/code&gt; as an example, which is stack that can hold a bunch of integers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class IntStack {
    has Int @!store;

    method push(Int $n) {
        @!store.push($n);
    }
    method pop(--&amp;gt; Int) {
        @!store.pop;
    }
    method top(--&amp;gt; Int) {
        @!store.tail;
    }
    method min(--&amp;gt; Int) {
        @!store.min;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which can be used like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    my $stack = IntStack.new;
    $stack.push(2);
    $stack.push(-1);
    $stack.push(0);

    $stack.pop;
    say $stack.top;  #=&amp;gt; -1

    $stack.push(0);

    say $stack.min;  #=&amp;gt; -1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;本文為《&lt;a href="https://gugod.org/2021/01/pwc-095/"&gt;解 Perl Weekly Challenge 095 -- 回文數與堆疊&lt;/a&gt;》之英文版&lt;/p&gt;

</description>
      <category>raku</category>
    </item>
  </channel>
</rss>
