<?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: Leon Timmermans</title>
    <description>The latest articles on DEV Community by Leon Timmermans (@leontimmermans).</description>
    <link>https://dev.to/leontimmermans</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%2F535518%2F155a46f0-07e1-47e8-b1fa-2130045947c4.png</url>
      <title>DEV Community: Leon Timmermans</title>
      <link>https://dev.to/leontimmermans</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/leontimmermans"/>
    <language>en</language>
    <item>
      <title>A year of strife</title>
      <dc:creator>Leon Timmermans</dc:creator>
      <pubDate>Wed, 14 Apr 2021 19:59:48 +0000</pubDate>
      <link>https://dev.to/leontimmermans/a-year-of-strife-16o9</link>
      <guid>https://dev.to/leontimmermans/a-year-of-strife-16o9</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclaimer: this is based on my own personal experience and perception. It's not The Truth, but it is my truth.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last year, in early May, I got an email that got me worried. Really really worried. Not because of disagreement (though I did disagree with it), but because I knew this would be the most controversial thing I've seen in my more than decade on perl5-porters (the mailing list where core contributors discuss the development of perl). If I was wrong it was only because controversial turned out not to be quite strong enough a word for what would happen.&lt;/p&gt;

&lt;p&gt;In it, a radical change was proposed. Radical in technology, but I'm not going to focus on that today. Radical in &lt;a href="http://blogs.perl.org/users/leon_timmermans/2020/08/perl7-is-a-fork-of-values.html"&gt;values&lt;/a&gt;, which really means it's radical for the community as well. It immediately divided both core contributors and much of the community into many factions with two main contingents.&lt;/p&gt;

&lt;p&gt;What may seem like a simple technical disagreement was everything but. It was a fight for the soul of the language and the community that we love. While there were a lot of (mostly silent) people in the middle, in the discourse there were two sides and both (or at least enough people on both sides) were entirely convinced that the language and community that we all love only has a future if their side wins, and that the other will drive it to ruin.&lt;/p&gt;

&lt;p&gt;If that doesn't sound like the prologue of a Greek tragedy, then I don't know what does.&lt;/p&gt;

&lt;p&gt;It led to vigorous discussion; initially behind the scenes, and after the Conference-in-the-Cloud also on &lt;a href="https://www.nntp.perl.org/group/perl.perl5.porters/2020/06/msg257565.html"&gt;perl5-porters&lt;/a&gt; and elsewhere. And quite frankly, this only stopped when people mostly stopped talking to each other after 2 weeks because there was little point to it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The alarm bells really should have gone off here.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Soon enough the conflict moved to github. The unfortunate thing is that there were two groups of people: one with the will to make this happen but not quite the knowledge, and they started writing code. And one group that didn't want this to happen that did have the knowledge, who started reviewing said code. As you can probably guess, this soon became a mutually aggravating situation. After a few weeks of stepping on each other's toes, it became an open conflict.&lt;/p&gt;

&lt;p&gt;The stakes were astronomical. I've heard several people say (in private) that they felt they would be needing to look for a different career and community over this. Combine this with a pandemic that has all of us stressed out, this led to a situation which I can only summarize as &lt;em&gt;both sides kept pushing until the other side would give up&lt;/em&gt;. Given the sheer stubbornness of the average perl contributor this could only end when a sufficient number of people have burned out that neither side is capable of doing pretty much anything. It was a dynamic that was quite frankly burning me out too, to the point where several of my loved ones told me I should give up on this for sake of my own mental health (and I've heard similar stories from others).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's easy to assume that the things that happened were a few abusive assholes, but in my experience it was mostly a vicious cycle of previously reasonable and kind people lashing out at the people that were burning them out&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And that's where the alarm bells became cold-war style air sirens, the kind you can hear from miles away (sorry gen-z, you probably have no idea what I'm talking about).&lt;/p&gt;

&lt;p&gt;This was the moment when we (or actually a bunch of community elders) started up a process to set up a new governance, because any decision making process that can generate this amount of strife is in need of repairs. Many people saw it as a diversion, but I don't agree. I think it was the only way to restart this conversation, and to get to a point (currently still in the future) where we can heal again.&lt;/p&gt;

&lt;p&gt;But when hell broke loose, another thing was finally spoken out, though it was easily missed in between all the unpleasantness, is a simple truth that there is a third option that had been unthinkable a year ago: forking. This changes everything about the equation, literally everything.&lt;/p&gt;

&lt;p&gt;Ultimately I believe that is what ended the discussion. It ended with the simple knowledge that the opposition was able and willing to fork if that's what it takes to prevent their worst-case scenario. In a way it was a Solomon's judgement for the steering committee. It ended the discussion not just because almost no one wants to fork the community, but because perl7 as announced last summer wouldn't be viable given a competitor that actually supports all of CPAN today and will continue to do so for the foreseeable future (just look at how long it took the python3 transition to take over, and that's without people wanting to keep supporting python2).&lt;/p&gt;

&lt;p&gt;It is a pyrrhic victory. Pyrrhic not just because "we" haven't really won anything. Everyone lost because friendships were damaged (and perhaps lost) over this. We lost because a bunch of the people who matter for moving perl forward are currently effectively burned out (a few loudly so, but many in painful silence). We lost because people stopped contributing over this (most prominently but not limited to former project lead Sawyer).&lt;/p&gt;

&lt;h3&gt;
  
  
  So what can we learn from this, and how can we move forward?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We needed different governance, for a multitude of reasons. The old pumpking model had a tendency to burn people out, that was not sustainable (to be honest, Sawyer seemed burned out to me even before any of this happened, I can only imagine where he is now). Having more people in our executive, and having them there for bound terms (it's easier to stop when it takes effort to continue) will hopefully prevent that in the future. But also because it will lead to more diverse points of view. I suspect we'll be tweaking our governance for a while we gain more experience with it, but at least we have a foundation to do that with now.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Going through this during a pandemic really didn't help. I honestly believe this situation would not have gotten so badly out of hand if we could meet up face to face and talk to each other with a beverage in our hands; if we weren't all so tense because we haven't seen many of our loved ones in ages. We should remember that. Some discussions should be done in real life; sometimes the right response to a conflict is "Ooh, I get the impression this may be a Chartreuse-level discussion. Are you up for that?"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We should have more attention for our contributors' mental health; we need to remember the human on the other side of the conversation. It's perhaps cliche to say we should act with more empathy for each other, but frankly we collectively also acted with too little empathy for ourselves.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It seems that the proponents initially mistook a lot of the grievances as mere bike-shedding, when they actually were meaningful concerns that wouldn't go away by themselves. They didn't seem to realize that they were &lt;a href="https://www.dictionary.com/browse/cross--the--rubicon"&gt;crossing the Rubicon&lt;/a&gt; by actually moving forward with their plans without a consensus. We should not set ourselves up for failure like that, there is no way that could have ended well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One thing we started doing much too late was mediation (Neil in particular has done great work there lately). We need people who can see all perspectives and bring them all back together. This is where we truly miss Larry more than anything, because bringing different needs together was his true talent. I don't think (or hope) we will be needing this often, but it's probably good to be prepared for such an eventuality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We talk a lot about speaking with kindness, but we don't talk nearly enough about listening with kindness. Unlike speaking, listening kindly can't really be enforced, but the speaking without listening can easily become dysfunctional. Achieving this requires a cultural change. I don't know how to get us there, but I do know that it will require leadership.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The other thing that requires leadership is bringing us back together again. Perl is unique in the &lt;a href="https://twitter.com/leon_timmermans/status/1376299679818874880"&gt;breadth&lt;/a&gt; of its user base and that is both Perl's greatest strength as its greatest weakness. These conflicting interests were at the root of this conflict, and finding a path that serves all those interests (including the less loud ones) will be the main challenge for the steering committee.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>perl</category>
      <category>community</category>
    </item>
    <item>
      <title>Better argument parsing with Getopt::Long</title>
      <dc:creator>Leon Timmermans</dc:creator>
      <pubDate>Tue, 15 Dec 2020 14:17:54 +0000</pubDate>
      <link>https://dev.to/leontimmermans/better-argument-parsing-with-getopt-long-2o13</link>
      <guid>https://dev.to/leontimmermans/better-argument-parsing-with-getopt-long-2o13</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Raku has a built-in argument parser. This is a really good idea, given how common argument parsing it, but I still ended up writing my own, and to explain why I did so, I should first explain what the built-in parser does.&lt;/p&gt;

&lt;p&gt;The raku built-in parser is a blind parser. This means that it converts the input arguments into a &lt;a href="https://docs.raku.org/type/Capture"&gt;Capture&lt;/a&gt; without any knowledge of the &lt;code&gt;MAIN&lt;/code&gt; sub, and then tries to call &lt;code&gt;MAIN&lt;/code&gt; with that capture. This has several implications.&lt;/p&gt;

&lt;p&gt;The first is that the input syntax has to be context-free. It allows for &lt;code&gt;-foo&lt;/code&gt;/&lt;code&gt;--foo&lt;/code&gt; (meaning &lt;code&gt;:foo&lt;/code&gt;), &lt;code&gt;-/foo&lt;/code&gt;/&lt;code&gt;--/foo&lt;/code&gt; (meaning &lt;code&gt;:!foo&lt;/code&gt;) and &lt;code&gt;-foo=bar&lt;/code&gt;/&lt;code&gt;--foo=bar&lt;/code&gt; (meaning &lt;code&gt;:foo(val("bar"))&lt;/code&gt;). It does not allow traditional unix syntaxes such as &lt;code&gt;-j2&lt;/code&gt; or &lt;code&gt;--jobs 2&lt;/code&gt; as that would require knowing in advance that those two options take an argument.&lt;/p&gt;

&lt;p&gt;The second issue with is is that it fails in very confusing ways. To explain this I will give some examples using &lt;code&gt;zef&lt;/code&gt; (chosen because of its ubiquity).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ zef instal
Usage:
  zef [--force|--force-fetch] [--timeout|--fetch-timeout=&amp;lt;Int&amp;gt;] [--degree|--fetch-degree=&amp;lt;Int&amp;gt;] [--update=&amp;lt;Any&amp;gt;] fetch [&amp;lt;identities&amp;gt; ...] -- Download specific distributions
  zef [--force|--force-test] [--timeout|--test-timeout=&amp;lt;Int&amp;gt;] test [&amp;lt;paths&amp;gt; ...] -- Run tests
  zef [--force|--force-build] [--timeout|--build-timeout=&amp;lt;Int&amp;gt;] build [&amp;lt;paths&amp;gt; ...] -- Run Build.pm
  zef [--fetch] [--build] [--test] [--depends] [--test-depends] [--build-depends] [--force] [--force-resolve] [--force-fetch] [--force-extract] [--force-build] [--force-test] [--force-install] [--timeout=&amp;lt;Int&amp;gt;] [--fetch-timeout=&amp;lt;Int&amp;gt;] [--extract-timeout=&amp;lt;Int&amp;gt;] [--build-timeout=&amp;lt;Int&amp;gt;] [--test-timeout=&amp;lt;Int&amp;gt;] [--install-timeout=&amp;lt;Int&amp;gt;] [--degree=&amp;lt;Int&amp;gt;] [--fetch-degree=&amp;lt;Int&amp;gt;] [--test-degree=&amp;lt;Int&amp;gt;] [--dry] [--upgrade] [--deps-only] [--serial] [--contained] [--update=&amp;lt;Any&amp;gt;] [--exclude=&amp;lt;Any&amp;gt;] [--to|--install-to=&amp;lt;Any&amp;gt;] install [&amp;lt;wants&amp;gt; ...] -- Install
  zef [--from|--uninstall-from=&amp;lt;Any&amp;gt;] uninstall [&amp;lt;identities&amp;gt; ...] -- Uninstall
  zef [--wrap=&amp;lt;Int&amp;gt;] [--update=&amp;lt;Any&amp;gt;] search [&amp;lt;terms&amp;gt; ...] -- Get a list of possible distribution candidates for the given terms
  zef [--max=&amp;lt;Int&amp;gt;] [--update=&amp;lt;Any&amp;gt;] [-i|--installed] list [&amp;lt;at&amp;gt; ...] -- A list of available modules from enabled repositories
  zef [--fetch] [--build] [--test] [--depends] [--test-depends] [--build-depends] [--force] [--force-resolve] [--force-fetch] [--force-extract] [--force-build] [--force-test] [--force-install] [--timeout=&amp;lt;Int&amp;gt;] [--fetch-timeout=&amp;lt;Int&amp;gt;] [--extract-timeout=&amp;lt;Int&amp;gt;] [--build-timeout=&amp;lt;Int&amp;gt;] [--test-timeout=&amp;lt;Int&amp;gt;] [--install-timeout=&amp;lt;Int&amp;gt;] [--degree=&amp;lt;Int&amp;gt;] [--fetch-degree=&amp;lt;Int&amp;gt;] [--test-degree=&amp;lt;Int&amp;gt;] [--dry] [--update] [--serial] [--exclude=&amp;lt;Any&amp;gt;] [--to|--install-to=&amp;lt;Any&amp;gt;] upgrade [&amp;lt;identities&amp;gt; ...] -- Upgrade installed distributions (BETA)
  zef [--depends] [--test-depends] [--build-depends] depends &amp;lt;identity&amp;gt; -- View dependencies of a distribution
  zef [--depends] [--test-depends] [--build-depends] rdepends &amp;lt;identity&amp;gt; -- View direct reverse dependencies of a distribution
  zef [--sha1] locate &amp;lt;identity&amp;gt; -- Lookup locally installed distributions by short-name, name-path, or sha1 id
  zef [--update=&amp;lt;Any&amp;gt;] [--wrap=&amp;lt;Int&amp;gt;] info &amp;lt;identity&amp;gt; -- Detailed distribution information
  zef [--open] browse &amp;lt;identity&amp;gt; &amp;lt;url-type&amp;gt; -- Browse a distribution's available support urls (homepage, bugtracker, source)
  zef look &amp;lt;identity&amp;gt; -- Download a single module and change into its directory
  zef [--fetch] [--build] [--test] [--depends] [--test-depends] [--build-depends] [--force] [--force-resolve] [--force-fetch] [--force-extract] [--force-build] [--force-test] [--force-install] [--timeout=&amp;lt;Int&amp;gt;] [--fetch-timeout=&amp;lt;Int&amp;gt;] [--extract-timeout=&amp;lt;Int&amp;gt;] [--build-timeout=&amp;lt;Int&amp;gt;] [--test-timeout=&amp;lt;Int&amp;gt;] [--install-timeout=&amp;lt;Int&amp;gt;] [--degree=&amp;lt;Int&amp;gt;] [--fetch-degree=&amp;lt;Int&amp;gt;] [--test-degree=&amp;lt;Int&amp;gt;] [--update] [--upgrade] [--dry] [--serial] [--exclude=&amp;lt;Any&amp;gt;] [--to|--install-to=&amp;lt;Any&amp;gt;] smoke -- Smoke test
  zef update [&amp;lt;names&amp;gt; ...] -- Update package indexes
  zef [--confirm] nuke [&amp;lt;names&amp;gt; ...] -- Nuke module installations (site, home) and repositories from config (RootDir, StoreDir, TempDir)
  zef [--version] -- Detailed version information
  zef [-h|--help]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem here is a simple typo in the word "install", but nothing in this wall of output actually hints at that.&lt;/p&gt;

&lt;p&gt;If it can match a subcommand the error message is better/shorter, but still not terribly helpful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ zef install
Usage:
  zef [--fetch] [--build] [--test] [--depends] [--test-depends] [--build-depends] [--force] [--force-resolve] [--force-fetch] [--force-extract] [--force-build] [--force-test] [--force-install] [--timeout=&amp;lt;Int&amp;gt;] [--fetch-timeout=&amp;lt;Int&amp;gt;] [--extract-timeout=&amp;lt;Int&amp;gt;] [--build-timeout=&amp;lt;Int&amp;gt;] [--test-timeout=&amp;lt;Int&amp;gt;] [--install-timeout=&amp;lt;Int&amp;gt;] [--degree=&amp;lt;Int&amp;gt;] [--fetch-degree=&amp;lt;Int&amp;gt;] [--test-degree=&amp;lt;Int&amp;gt;] [--dry] [--upgrade] [--deps-only] [--serial] [--contained] [--update=&amp;lt;Any&amp;gt;] [--exclude=&amp;lt;Any&amp;gt;] [--to|--install-to=&amp;lt;Any&amp;gt;] install [&amp;lt;wants&amp;gt; ...] -- Install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of telling us to give a module to install, it lists all the possible arguments for this subcommand (though to be fair, this one is largely zef's fault for making that first MAIN argument mandatory).&lt;/p&gt;

&lt;p&gt;The same error message is given for &lt;code&gt;zef install Foo --timeout=3.5&lt;/code&gt; (because a &lt;code&gt;Rat&lt;/code&gt; is not an &lt;code&gt;Int&lt;/code&gt;) and &lt;code&gt;zef install Foo --timeout 10 --timeout 10&lt;/code&gt; (it passes a two value list instead of an &lt;code&gt;Int&lt;/code&gt; to &lt;code&gt;:$timeout&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;These error messages are not helpful (and in some cases, it being an error isn't either). The problem here is simple: Raku knows it can't dispatch the capture to any of the &lt;code&gt;MAIN&lt;/code&gt; candidates, but it doesn't know why. Figuring out why requires exactly the sort of introspection that it tries to avoid so hard.&lt;/p&gt;

&lt;p&gt;But the most confusing way the argument parsing fail has to be the way it handles enums. It will interpret any known enum literal in scope as an enum as a string, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zef install True
Cannot resolve caller new(Zef::Identity:U: Bool:D); none of these signatures match:
    (Zef::Identity: Str :$name!, :ver(:$version), :$auth, :$api, :$from, *%_)
    (Zef::Identity: Str $id, *%_)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's impossible to pass Raku programs using built-in argument parsing any of the strings &lt;code&gt;True&lt;/code&gt;, &lt;code&gt;False&lt;/code&gt;, &lt;code&gt;Less&lt;/code&gt;, &lt;code&gt;More&lt;/code&gt;, &lt;code&gt;Same&lt;/code&gt;, a &lt;a href="https://github.com/rakudo/rakudo/issues/2794"&gt;bunch of others&lt;/a&gt; and any enum literal you've defined in your script as strings because they'll be converted into something else entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;So instead of using the built-in argument parser, I wrote my own argument parsing module: &lt;a href="https://modules.raku.org/dist/Getopt::Long:cpan:LEONT"&gt;Getopt::Long&lt;/a&gt;. Unlike the built-in one, it is contextual. It will first look at the &lt;code&gt;sub MAIN&lt;/code&gt; to know what arguments to expect, than parse based on that, and then call &lt;code&gt;MAIN&lt;/code&gt;. This way, one can parse &lt;code&gt;-j2&lt;/code&gt; to &lt;code&gt;:j(2)&lt;/code&gt;, and &lt;code&gt;--jobs 2&lt;/code&gt; to &lt;code&gt;:jobs(2)&lt;/code&gt;. This results in a far more unixish interface than what is possible using the default parsing.&lt;/p&gt;

&lt;p&gt;But there's a second advantage: better error messages. It will try its hardest to give an informative error message. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Unknown option --foo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Option --foo doesn't take arguments&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cannot convert --foo argument "10a" to number: trailing characters after number&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Invalid Date '20-02-20' given as --foo argument; use yyyy-mm-dd instead&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It tries very hard to detect any potential issue before dispatching is done, so that it can give an informative error message.&lt;/p&gt;

&lt;p&gt;This should make it much easier for the user of a Raku program to figure out what they did wrong, while at the same time offering a much more standard interface to your users. It allows for a series of options to make the external interface either more unixish (the default) or more like raku's argument parsing (for backwards compatibility).&lt;/p&gt;

&lt;p&gt;Getopt::Long offers both a functional interface (much like the &lt;a href="https://metacpan.org/pod/Getopt::Long"&gt;Perl module&lt;/a&gt; that inspired it), and a &lt;code&gt;MAIN&lt;/code&gt; wrapper that can function as a drop in replacement of the existing argument parser: all you have to do to benefit from this is add a &lt;code&gt;use Getopt::Long;&lt;/code&gt; to your script!&lt;/p&gt;

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