One day I woke and decided I want to learn programming as a hobby. For someone
who doesn't know much about the subject other than writing a few Bash scripts, words like Functional Programming, Object Oriented or even the
word class
by itself were mysterious.
Doing my research to know where to start, very soon I was lost in tons of information available online, but I wanted to start learning anyway, and it is time to pick a programming language.
There are so many programming languages out there, deciding which language to learn was a difficult task for me, mainly because comparing languages based on technical knowledge was not an option.
I decided to read as much code as I could of different languages trying to solve the same problem snf take notes of which code is easy to reason about, what I like and what I don't like. Rosetta Code made it easy to compare different languages solutions for same the problem.
I started looking at code on Rosetta Code, going down the list reading code from different languages without paying attention to the language name, just focusing on the code. All looks fine, most code looked similar in one way or another to me.
Deep down the list, I noticed code that at first sight looked weird. "What is that!" I asked myself. Staring for a few seconds, a second look and the code doesn't look weird anymore, but now looks strange! "What are these colons and arrows doing!" I asked myself again. A third look at the code and somehow the code looked familiar, then beautiful and easy to follow (even without knowing what colons or arrows do). I was curious to know the name of that language; It was Raku! (Actually Perl6 at that time :-))
This pattern repeated with solutions to different problems, most languages looked similar in one way or another; it was only Raku code that stood out to me in a nice and attractive way. So I decided to learn Raku, and went ahead with reading Learn Raku in Y Minutes and the documentation
Enjoying what I learned so far with Raku, I wanted to write a package manager for fun and learning. Writing a package manager can be challenging for someone new to programming like me, but I started anyway knowing it will be fun. At the end of the day Raku is -Ofun!
I looked at the Panda and Zef package managers to learn what a package manger for Raku would entail. Panda was simple and easy to understand but also was deprecated. Zef looked updated and complete features-wise but wasn't easy to follow for me. Checking Panda and Zef gave me an idea what I need to do.
I wanted Pakku to be Simple, Fast, Light and Colorful like Camelia - the Raku mascot.
To me running commands is like communicating to a program, so I wanted the commands sent to a program be as close as possible to sentences people use to communicate with each other, no hyphens, no double hyphens...
For example instead of writing:
program delete --from=somewhere --recurse dirname
I can write:
program delete from somewhere recurse dirname
and program
should take care of identifying commands, sub-commands,
options of sub-commands; and also DWIM, for example if dirname
above is replaced
with recurse
, program
should parse the first recurse
as an option
and the last as the directory to be deleted and cover other corner cases.
I wouldn't be able to achieve that using Raku's MAIN
command line arguments,
fortunately Raku's Grammar made that easy.
These are examples of Pakku commands:
pakku add to site MyModule
pakku list repo site details
pakku pretty please dont remove from home MyModule
Pakku commands are processed in an easy to follow way, like stages in a pipeline.
Lets examine how Pakku installs a module:
1- Pakku receives the module(s) spec to be installed (module name or local path)
2- Check if the specified module is installed (satisfied) already or needs to force install
3- Try to satisfy the spec, this means obtain the meta info for the module and dependencies if needed, in the correct order to install
@spec
==> map( -> $spec { Spec.new: $spec } )
==> grep( -> $spec { $force or not self.satisfied: :$spec } )
==> map( -> $spec { self.satisfy: :$spec } )
==> map( -> $dep {
my @dep = self.get-deps( $dep, :$deps );
@dep.append: $dep unless $deps ~~ <only>;
@dep;
} )
...
...
==> my @meta;
1- Download the distribution archive and extract it
2- Prepare the fetched distribution directory to be installed by CompUnit::Repository::Installation
@meta
==> map( -> $meta {
my $prefix = $!fetcher.fetch: $meta.recman-src;
$meta.to-dist: :$prefix;
} )
==> my @dist;
1- Build the distribution, if needed
2- Test the distribution, if needed
3- Add the distribution
@dist
==> map( -> $dist {
$!builder.build: :$dist if $build;
$!tester.test: :$dist if $test;
$*repo.add: :$dist, :$force unless $!dont;
π¦ "ADD: ο½’$distο½£" unless $!dont;
} );
Pakku logs on 7 levels
0 (silent)
- No output what so ever1 (trace)
π€ - If you want to see everything2 (debug)
π - To debug some issue3 (info )
π¦ - Camelia delivers important things4 (warn )
π - Only when some warnings happen5 (error)
β - When errors are what you care about!6 (fatal)
π - You probably don't like to see that when running Pakku, me too!
Log symbols and messages colors can be changed and customized in the pakku.cnf
file.
Sample log messages you may see and their meaning:
π¦ PRC: ο½’ ... ο½£ β Start processing...
π SPC: ο½’ ... ο½£ β Processing spec
π MTA: ο½’ ... ο½£ β Processing meta
π€ FTC: ο½’ ... ο½£ β Fetch URL
π BLD: ο½’ ... ο½£ β Start building dist
π¦ BLT: ο½’ ... ο½£ β Built dist successfully
π TST: ο½’ ... ο½£ β Start testing dist
π¦ TST: ο½’ ... ο½£ β Tested dist successfully
π¦ ADD: ο½’ ... ο½£ β Added dist successfully
π MTA: ο½’ ... ο½£ β No valid meta obtained for spec
π BLD: ο½’ ... ο½£ β Bulding dist failed
π TST: ο½’ ... ο½£ β Testing dist failed
π CNF: ο½’ ... ο½£ β Config file error
π CMD: ο½’ ... ο½£ β Could not understand command
List details of a distribution
Thats all for now, thanks for reading