musicmatzes blog

tools

This post was written during my trip through Iceland and published much later than it was written.

This is a really important topic in programming and I really hope to get this article right. Not only for technical correctness, but also for ease to understand, as explaining types is not that simple if one has never heard of them.

Let's give it a try...

What are types

Well, that's a question which is, in my opinion, not easy to answer. In fact, I thought several days about this question before writing this down, in hope it will become a sufficient answer. Hence, you might find other answers which are easier to understand and maybe more correct as mine, but I'll give it a try nonetheless.

From what I think

Types are a combination of abilities and properties that are combined to express and limit a certain scope of a thing.

For example, A type Car may have four wheels, two doors and a horn (its properties) and can drive slow, drive fast and park (its abilities). That is certainly not a real representation of a car (also because only a car is a real representation of a car) but because of the domain this is used in, it is sufficient in the scenario at hand. The type Car cannot be multiplied, but another type Number may have this ability. Thus, the scope and abilities are also limited in a certain way.

I hope this description is a good one for you to understand.

Now that we know what types are, we should also learn some other terms around the subject of types. The first thing I want to talk about here is “strong typing” and “weak typing”. The reason for this is: These things do not exist. Yes, you've read this correctly: There is no such thing as “strong typing”. There is only stronger and weaker typing. The Java programming language is not strongly typed, neither is it weakly typed (but it is, of course badly typed... forgive me that joke, pity java programmer).

But what is a stronger typing? That is rather simple to explain, actually. We discussed that types are limitations of things to be able to only do some specific operations and such. These things are enforced by the compiler or interpreter of the programming language, of course. And stronger typing only says that the compiler has more information (implicitly via the definition of the programming language) to enforce these rules, the rules of “the function A is defined for type T, so you cannot call it on U”. Of course there is more to that because of generic typing and so on, but that's basically it.

The next term is “type inference”. Type inference is nothing a programmer experiences explicitely, because it happens implicitly. Type inference is a feature of the compiler and interpreter of the language to guess the type of a variable without the programmer stating the actual type. It's nothing more to that actually.

I mentioned the term “generic types” in one of the former paragraphs already, so we should have a look there, too. Generic types, or shorter Generics, are types which are partial, in a way. So for example, one can define a Bag of things, whatever things is. This is often (at least in typed languages – languages where types actually matter for the compiler or interpreter) specified in the code via “type parameters” (though this term differs from language to language).

Why more types are better then few

The more types you introduce in your programs (internally or even for the public API), the more safety you get (speaking in the context of a stronger typed programming language, but also if you do a lot of runtime-type-checking in a weaker typed language). That does not mean that you should introduce a BlueCar, a BlackCar and a GreenCar as types in your program, but rather a type Color and a type Car whereas each Car has a Color – even if your domain is cars and not colors.

Maybe that example lacks a certain expressiveness, so consider this: Your Car has wheels. You can set the number of wheels when constructing the Car object. But instead of passing an integer here, which would yield an API where one can pass 17 as valid number for the number of wheels – or 1337 or possibly even -1. But if you introduce a type which represents the number of wheels, you get some safety into the construction of the Car object – safety checks in your code are not necessary anymore and thus your code will be shorter, better focused on what the actual problem is instead of fighting for valid values and of course, the compiler or interpreter can do the work for you.

Sounds nice, doesn't it? You can get this all with (almost) no cost attached, you just have to write down some more types. If your programming language contains feature like enumerations, you do not even have to make validity checks anymore, as the compiler can execute them.

Next

In the next post we will focus on the coding environment.

tags: #open-source #programming #software #tools #rust

This post was written during my trip through Iceland and published much latern than it was written.

While we heavily focused on the code-surrounding things in the last parts, we will return to focus on code-related things from here on.

This article discusses code verbosity and how it can improve your open source code and also your contributors experience a lot.

What is code verbosity

Code verbosity is mainly explicitness of code. For example, in Java you have to be more explicit when declaring a variable than in (recent) C++ or even Ruby:

String s = someFunctionCall(param); // Java

auto s = someFunctionCall(param); // C++

s = someFunctionCall param # Ruby

So code verbosity is how explicit you have to state certain things so that the compiler or interpreter understands your intention.

Because we do not always tell the compiler or interpreter what we want to do exactly and because we want to re-use functionality, we introduced abstractions. So abstractions are a way to make code less verbose, in some ways.

How to make code less verbose

Abstraction. It is as simple as this. You introduce abstraction to minimize repetition which leads to less verbose code. Of course, you cannot always make the code less verbose if the language does not allow it: in the above example we used the auto keyword for specifying the type in C++, which is nice, but not possible in Java. So in the borders of your languages abilities, you can make code less verbose.

If you do that right and the abstractions results in nice code, you know that you've done fine.

How much is too much

But there can also be too much abstraction which then yields unreadable code. Not unreadable as in clustered with stuff but just too abstract to grasp at first sight.

Abstraction can get too much. So make sure you introduce sensible abstractions, abstractions that can be combined nicely and of course one can step around the abstractions and use the core functionality, the not-abstracted things beneath.

As a sidenote: sometimes it makes sense to hide certain things completely or even introducing several layers of abstractions.

Next

This was a rather short one, I guess. The next article will be longer I hope, as it will be about typing.

tags: #open-source #programming #software #tools #rust

This post was written during my trip through Iceland and published much latern than it was written.

This is the last post which does not deal with code directly, I promise.

When it comes to open source hobby projects, contributions from others are often happily taken. But making the contribution process smooth for everyone does involve some precautions.

In this article I want to summarize how to make a contribution to a project as smooth as possible for all persons involved.

Public code and contributions

I wrote about this before and I want to shortly reiterate on it. “Open” as in open source (or even better: open contributions) is not black-white at all but there are several levels of grey in between, in my opinion.

The more open your code is, the better a contributor is able to contribute. Whether it be discussion, requests, bug reports, bug fixes or even feature implementation or general enhancement. On the other side, though, the more open your code is the less your contributors are “bound” (in a mentally way) to your project. It can happen (and it happened to me) that a contributor stops by for one pull request or issue and then you'll never hear of them. The better the contribution process is for them, the more likely they come back – and how relevant the project is to them, of course.

The contribution process in the rust community (for the Rust compiler itself) is awesome, from what I've heard. This, of course, enhances the “I will come back and give another issue a try” a lot. The contribution process of the nixpkgs project is slightly worse (but still rather good). Sometimes, nobody answers questions you might have for your pull request for several days or even weeks. This does not really make one eager to file another request.

Platforms

From what I think, github is the “most open” in the sense of “open contributions”. That's not only because of how github works, as other platforms work equally well (gitlab, gitea, gogs, bitbucket) but also because everyone is on github.

If you want to close down contributions a bit, you could host your own instances of gitea or gitlab – contributors can easily open an account for their contribution, though that slight hurdle will make the “casual code dumper” likely go away.

Even “more closed” would be a email-patch-workflow, git supports (and the kernel community uses successfully for years now). In this case, the code is often made available via a web interface like cgit or klaus.

Readme

A project should always contain a readme file in its root folder. The readme file is often the first thing a contributor will look at, not only but also because github renders and displays them.

Therefore, keeping your readme file up to date and filled with current information can be a good way to show your contributors (and users) what is going on in the projects code base. It should contain a short description what the project/code does and how it works (only from a users view – implementation details or why you implemented this in Haskell instead of JavaScript do not necessarily belong here). It should contain a few examples how to use the code or, if it is a library, how to call it. It also should contain build instructions (if necessary) or a pointer to a “BUILDING” file if the build process is long or complicated. At the end of the Readme file, a license statement (how the project is licensed) should be pasted. Not the entire license, but only a short note and a copyright note as well. It happens to be kind to do so.

Contributing File

Often, projects contain a Contributing file where guidelines (or even rules) are written down on how to contribute. It does not only contain statements on how code is submitted but also how issues are filed or requests should be made.

I think it is extremely important to have such an file available, especially if not hosting the code on a site like github, where it is obvious that code is submitted through pull requests and issues are submitted via the issue tracker.

The length of such an file should respect the size of the project itself. If the project contains 10KLOC, one should be able to read the contribution file in less than two minutes, preferably in less than one minute. It should state not only how code should be submitted, but also whether it should conform to some style guide (which itself can be outsourced to yet another file), how to behave in the community, how to write bug reports and also how to file issues (what information must be included).

Issue handling

Handling issues is clearly a way to improve the contributors experience. As soon as a contributor files an issue, she or he should be greeted and thanked for the issue. Take it this way: Someone just invested time to look at your project and cared enough to have a question, try it out or even found a bug. This is truly a cool thing and therefore they should be thanked for this, as soon as you have the time to do so. The Rust community even automated this, but I don't think this would be necessary for a small or medium sized project/community.

So be nice to every one. Nothing is worse than a maintainer that babbles about bad things or insults the contributor because of his or her ideas or ways an issue was proposed to be resolved. Don't ever do this. I've seen issues where the maintainer of the project started rambling about how bad things were (not the project itself but rather its dependencies or even things that had nothing to do with the project itself). I cannot believe that such projects will last long, let alone survive at all. These projects will die.

Also, your ramblings have nothing to do with the issue at hand and even if they do: be kind and humble will most likely be better in every way, right?

Next

In the next part we will finally go back and actually talk code.

What I want to discuss in the next article of this series is code verbosity. I want to make sure how DRY a code actually needs to be and how much abstraction is enough for the sake of understandability and cleanness of code.

tags: #open-source #programming #software #tools #rust

Why can't we do

fn foo(i: i32, j: i32) -> i32 {
    i + j
}

fn main() {
    println!("{}", foo((1, 2)));
}

in Rust?

The idea

First of all: I don't know much about what is going on in rust-compiler-land, hence I don't know whether this is technically possible or even in progress already. I'm simply writing down some thoughts here.

The idea, or rather the question, is: Why can't we map tuples on arguments in Rust? The code from above shows exactly what I mean by that: Why is the compiler not able to unpack the tuple and pass it as list of arguments to the function?

It would be really cool if it could do that. The usecase is something like this:

some_possibly_failing_fn() // -> Result<_, _>
  .and_then(|res| (res, some_other_failing_fn()?)) // Result<(_, _), _>
  .map(PartialEq::Eq) // Result<bool, _>

for example. If mapping of tuples on arguments would be possible, one could write code like this, where failing functions are chained and operators and functions can then be called on the results of the failing functions.

Currently, one has to write it like this:

some_possibly_failing_fn() // -> Result<_, _>
  .and_then(|res| (res, some_other_failing_fn()?)) // Result<(_, _), _>
  .map(|(a, b)| a == b) // Result<bool, _>

or, because it is solvable with one line less:

some_possibly_failing_fn() // -> Result<_, _>
  .and_then(|res| Ok(res == some_other_failing_fn()?)) // Result<bool, _>

You might think that the overhead for that convenience is rather low and thus it is not really beneficial to have such a functionality in the rust compiler.

That is true. But there are great possibilities of API design if this is mapping of tuples on arguments are possible. For example, one could write an extension to the Result type which extends tuples. Think of something like that:

// assuming:
//
// some_possibly_failing_fn() -> Result<T, _>;
// some_other_failing_fn()) -> Result<U, _>;
// even_other_failing_fn()) -> Result<W, _>;
// yet_another_failing_f()) -> Result<V, _>;
// check_results(T, U, W, V) -> Result<bool, Error>;
//

some_possibly_failing_fn() // -> Result<T, _>
  .and_then_chain(|| some_other_failing_fn()) // Result<(T, U), _>
  .and_then_chain(|| even_other_failing_fn()) // Result<(T, U, W), _>
  .and_then_chain(|| yet_another_failing_f()) // Result<(T, U, W, V), _>
  .and_then(check_results)

From an API-design standpoint, this is plain awesome.

Lets compare that to what we have to write today to do this:

some_possibly_failing_fn() // -> Result<T, _>
  .and_then(|a| Ok((a, some_other_failing_fn()?)))
  .and_then_chain(|(a, b)| Ok((a, b, even_other_failing_fn()?)))
  .and_then_chain(|(a, b, c)| Ok((a, b, c, yet_another_failing_f()?)))
  .and_then(|(a, b, c, d)| check_results(a, b, c, d))

and that's really not that readable.

From a technical point of view, the compiler is able to verify the length of the tuples and thus is able to check whether the tuple actually matches the number of parameters of a function.

As said, I'm not that much into the development of the language, thus I don't know whether there are other things that prevent this, though.

Update

After posting this as a question on users.rust-lang.org and getting some nice replies with explanations why this cannot be, I conclude:

It is not possible to do it in a way that does not yield weird edge cases, ugly implicit conversions and other weirdness we really do not want in the language.

But I learned something, and that was worth it!

tags: #tools #software #c++

Today I learned that the new C++ standard (C++17) has std::variant – but no pattern matching.

Variants and how they work

Variants or “tagged unions” are a special form of data type. They describe an abstract thing which could be one concrete thing or another concrete thing. Variants are available in a lot of languages, including and not limited to ML, Haskell, Algol 68, Pascal, Ada, Rust and even C.

With C++17, the C++ standard was extended to have the std::variant generic type, which is basically a “C union on steroids”.

(To the C++ greybeard programmers reading this: I know this is technically not right, how it is implemented and how this works is very different from an actual C union, but what it is used for is basically the same, so please don't yell at me for this statement.)

A std::variant can be used to express, for example, a return value of a function which might have failed. Maybe you think “Hey, that's what exceptions are for” – no, that's not what exceptions are for, though a lot of programmer use them like this. Exceptions are for unexpected behaviour of your program. If you're writing a terminal calculator, an invalid input is an expected failure. An unexpected failure would be that you cannot read from stdin!

In cases where expected failures happen, a std::variant can be used to return a “I failed doing my job”-value.

Why you need pattern matching

Pattern matching can be used to destructure variants efficiently (as in code, readability, extendability and thus maintainability) to access their inner values.

For example, in Haskell, one can destructure a variant like this:

data Result = Ok | Err

explainresult :: Result -> [Char]
explainresult Ok  = "All right"
explainresult Err = "Ouch"

Here, we define the variant Result and a function explainresult which destructures the variant and returns a string based on the passed variant. Of course, variants can hold data and this data can be accessed then.

As a matter of fact, variants are incredible useful in everyday programming and so is pattern matching for destructuring them.

What C++17 offers instead of pattern matching

C++17 does not offer pattern matching. Why? I don't know. But it really should have, because the way this issue is solved in C++17 is painful: std::visit is used for imitate pattern matching.

On the first glance, the following code looks rather okayish (well, it is still C++, right?):

std::vector<std::variant<char, long, float, int, double, long long>>
           vec = {5, '2', 5.4, 100ll, 2011l, 3.5f, 2017};

// display each value
for (auto& v: vec){
  std::visit([](auto&& e){std::cout << e << " ";}, v);
}

But this is simple. What if we want to actually do something with the element based on which type it has?

Well, not so nice: We have to design our own visitor and equip it with the functionality:

struct Visitor {
    void operator()(const string& s) const {
      // ...
    }

    void operator()(const int n) const {
      // ...
    }
};

While having a dedicated visitor which can be passed around is rather nice, having it as the only option for pattern matching is hell.

Also, the standard library does not provide a helper for building a visitor object from a list of lambdas!

All in all: Is the whole thing a step into the right direction? No. It is rather a stumble into the rightish direction. Nothing more.

A friendly advice from a non-Cpp programmer

Take a grain of salt. I'm not a (C++) professional, hence this might not reflect the opinions of the broader community. Also, the following might get some people really angry. But I don't care.

Don't use C++ for new projects!

I know C++ is a heavy target and the standard is backwards compatible and a lot of people count on the standards committee to get this right and yaddayaddayadda. From my point of view, C++ tries to catch up. This is, of course, nice. But at that speed, other languages will innovate much faster and better and C++ will slowly die. That's one reason I would never ever use C++ for a new project.

tags: #tools #software #c++

This post was written during my trip through Iceland and published much later than it was written.

Version Control is one important aspect when developing software as a whole, and especially when developing open source software.

Here are some thoughts about it.

Technology

First of all, technology wise it doesn't matter which version control system one uses. For the sake I'm using git here as an example VCS, though others might do as well.

One important thing, at least in my opinion, is that the VCS has some basic functionality. This is mainly that it can be used distributed and has a branching functionality (which are two things I like to believe go hand in hand).

So I do not care whether one uses git, mecurial, or anything else. Most important is that a (D)VCS is actually used.

Branching model

Branching is a method that came up before git was created, as bitkeeper had such functionality (as far as I can tell) before Linus Torvalds wrote git. It is only that git has revolutionised the way we do version control and brought branching to wider knowledge and use.

In my opinion it is really important how branching is done. There is not simply “the branching” but there are many ways to do branching and one might be better for certain use case than another. There are known models such as feature branching, the gitflow branching model and a rebase-merge workflow. I don't want to explain each of them because others have done so way better than I ever could.

What I want to tell is that branching is not only important, but as flexible as you might not even guess. This is not necessarily a good thing – I'll show you in a minute. In my opinion, branching and developing a branching model for a project is like developing an API. Once it is set up properly, it may serve as a communication rule for a project, putting developers on the same page about how certain things have to be handled. Having a protocol on how to work on things is a good thing. If implemented properly, branching can improve the work of everyone as it is one point less to think about.

The bad thing about flexibility of branching functionality is that it can be done wrong. It's as simply as that, but merging one branch into another when one is not supposed to do that, creates overhead which might not be reversible. This has happened to the best communities (for example the kernel community) but also happens in small communities, often due to too few knowledge of the tools at hand.

To summarize: If an open source project gets to a certain size (both code-wise and contributor/community-wise) a branching model should be implemented. If there are rules that contributors agree upon, it can improve working speed and therefore overall happiness in the community. Because developers like to bikeshed, it could also worsen happiness, of course. Though, it is better than no plan and chaos instead.

Hosting

I will not go into thoughts about hosting platforms in this article but rather on the how and why.

First of all, hosting the code somewhere with a way to show it in a web browser is a good way to improve the “open” part of open source code. Of course, tarball downloads and such suffice, but we are in the 21st century, so having a nice web interface is something one can expect.

Making the code browsable is often done via a VCS-specific web frontend, for example cgit for code version controlled with git. Therefore this web interfaces often also feature functionality to go back in time and view the history of one file. Maybe this is not needed often, but nevertheless helpful if needed.

I personally do not care about comments on code in my web interfaces or even ways to register users on the site, but of course some people like that. There are web interfaces that feature such things, for example for the git VCS there is gitea, gogs, gitlab, ... and many more. And of course there are the closed providers github, bitbucket and others...

Making code public and contributions easy

Hosting helps a lot with enabling contributions from strangers. No doubt, github makes contributions ridiculously easy.

I don't want to reiterate what others have said better and most people already know. What I want to point out here is that open source does not mean “open contributions”. One is completely free to reject all contributions one ones code base.

I really want to stress this. Open source does indeed mean that everyone is able to view the code, which also enables them to copy it (though redistribution might be limited or forbidden, as only free software allows you – by definition – to redistribute and alter code) but not necessarily that one is allowed or welcome to send in changes, feature requests or the like.

So if you want people to contribute to your code and suggest changes, features or report bugs, you should somehow give them the opportunity to do so. Depending on how “open” you want to be with your development you either should use a hosting platform (like github or bitbucket) or a slightly more “closed” variant, for example hosting your code on your own gitea instance. One step further you'd host your code on a site where people might be able to get it, maybe even with a “git clone”, though not send in pull requests, feature requests or open issues (for example a hosted git repository with cgit interface). Issues and bug reports could still be done via a mailinglist, if desired.

In fact, that last bit is what I consider for my own project imag.

SemVer, Change Management, Release Management

As soon as your code is out there, you have to think about change and release management. In my opinion, these are topics closely related to source code version control as VCS often offer functionality to do releases in one form or another and are clearly involved in the process of change management.

First of all, I'd like to suggest you read the SemVer specification. It is not that long but will help you understanding the next few paragraphs. So if you haven't read it already, go ahead and do so. Even if you don't apply SemVer to your projects it might open your eyes in one aspect or another.

But before we get into releases, we should first talk about change management, or better named for my points: Pull request management.

What I personally do with my PRs is, merge them when they're ready. This approach is easy and works, so far, pretty well. From time to time I have changes in my working branches (as stated before, I use feature branches) which might conflict with other peoples work. For the sake of contributor experience, I pause my PRs and wait until they are done with theirs. We will talk a lot about this in the next episode of this series, so I won't go into much detail. For now: This is a simple approach that works perfectly well so far for me and my (considerably small) open source projects.

But as soon as ones project grews bigger, that approach might not do the job anymore. If there are too many changes in a short amount of time which have to be agreed on and that have to be merged, it might be time to think about an alternative approach.

There are two ways I would tackle this problem. I never experienced it in the “real world”/in my projects, so the following is just a write down of my thoughts. Take a grain of salt from here on.

The first approach I can think of is to assign certain subsystems to certain people. If the amount of changes has become too big, one could assume that the codebase has also become tremendous. If that is the case, sub-maintainers can handle certain subsystems and the project leader can then periodically merge all changes together. This requires, of course, at least two people that are interested into the subject and willing to contribute maintaining efforts to the project.

If the latter is not the case or there are too few people around for this, one could consider a merge-window style approach, like known from Linus himself. Changes are pulled in every other week, for example, and the rest of the time, only bug fixes are merged into the project.

These two approaches might become handy some day if one is about to maintain a large code base alone (as in “as the only project owner”).

Now on to release management. In my opinion, releases should be done as soon as something works and from there on periodically. I myself made one mistake too often: Pull more things into one release than would have been good. For example the imag 0.2.0 release was over one year ago. 0.3.0 is almost ready, but not yet. I should've done more releases in between.

In my opinion, more releases with clear-cut edges are better than long release-cycles. As soon as there is a new feature for users – release. User-facing fixed – release. This might result in high numbers for versioning, but who cares?

This is where I want to throw SemVer in, to adjust my statement from the last paragraph with a “but”.

SemVer can be used to notify breaking user interfaces. This is a really good thing and therefore I think SemVer should be applied everywhere. SemVer also states that in the “ 0.y.z phase” everything is allowed to happen, also API breakage. This is where I want to adjust my statement from above. A lot of releases should be done in the 0.y.z phase, but also within that scope. As soon as a library or program hits the 1.0.0, changes should be applied carefully. One really does not want to end up with a program or library in version 127.0.0, right? That'd also decrease a users trust into the application as one can expect breakage with every new release.

So what I'd do and actually plan doing with my projects is releasing a number of zero-releases until I am confident that everything is all right and then go from there. For imag specifically I am not thinking about 1.0.0 because imag is far from ready, but for my other projects, especially toml-query, I think of 1.0.0 already.

Another point which popped into my head weeks after the initial draft of this article was: Do not plan the features of the next release with a release number! This might sound a bit odd, so let me explain. For example, you're planning three major features for the next release, which will be 0.15.0 then. And you're slowly getting to a point where the release becomes ready, you might need three more weeks to get it ready. Now, a contributor steps up and opens a pull request with another feature, which is already completely implemented, tested and also documented in the pull request. The contributor needs this feature as soon as possible in your code and you also think that it might be a great idea to release this as soon as possible. After you merged the request, you release the source – as 0.15.0, despite your three features are not yet completed.

Two things come to mind in this scenario: First, if two of your three features are already completed, they might show up in 0.15.0 but one feature has to be moved to the next release. If these two features are ready, but not tested, you might end up with a buggy release and have to release 0.15.1 soonish – more effort for you. If you do not merge your features into the master branch of your project, but you have a 0.15.0-prepare branch or something like that, you end up with a rather ugly merge-mess later on, as 0.15.0 is already released and you cannot just rename a public branch.

So how to handle this properly? I came to the conclusion that release-branches is the way to go here. In the scenario described above, you'd branch off of the release before, most certainly 0.14.x and create a new branch 0.15.0, where the pull request of the contributor would be merged than. As soon as the release is out, 0.15.0 will be tagged and merged back to the master branch.

What my point is here: you'd still need to rename your next milestone or rewrite your issues for the next release. That's why I would not plan “0.15.0”, but simply “the next release” – because you'll never know whether your planned things will actually be the next release or the the release after. So lessen the effort for yourself here!

Next

In the next article in this series I want to elaborate on how to make a contribution as pleasing as possible for the contributor. I guess I can talk a lot about that because I've contributed to a lot of projects already, including but not limited to linux, nixpkgs and nanoc.

tags: #open-source #programming #software #tools #rust

A common thing one has to do in Rust to leverage zero-cost abstractions and convenience is to extend types. By “extending” them I mean adding functionality (functions) to the type which make it more convenient to use and “improve” it for the domain at hand.

From time to time, one needs to extend generic types, which is a difficult thing to do. Especially when it comes to iterators, where extending is often tricky.

That's why I want to write some notes on it down.

How do filters work?

I have this crate called “filters” which offers nice functionality for building predicates with the builder pattern. A predicate is nothing more than a function which takes something by reference and returns a boolean for it:

fn(&self, some: &Thing) -> bool;

The function is now allowed to decide whether the Thing shall be used. This is useful when working with iterators:

let filter = LowerThan::new(5);
vec![1,2,3,4,5,6,7,8,9].iter().filter(|e| filter.filter(&e)).collect()

Filters can, as I mentioned, be build with the builder-pattern:

let filter = HigherThan::new(5)
    .and(LowerThan::new(20))
    .and(UnequalTo::new(15));

vec![1,2,3,4,5,6,7,8,9].iter().filter(|e| filter.filter(&e)).collect()

One can also define own filters (in fact, the HigherThan, LowerThan and UnequalTo types do not ship with the crate) by implementing the Filter trait:

struct LowerThan(u64);
impl Filter<u64> for LowerThan {
  fn filter(&self, elem: &u64) -> bool {
    *elem < self.0
  }
}

What are we going to do?

What we're going to do is rather easy. We want a functionality implemented on all iterator types which gives us the possibility to pass an object for filtering rather than writing down this nasty closure (as shown above):

iterator.filter(|e| filter.filter(&e))

We want to write:

iterator.filter_with(some_filter)

And we want to get back a nice iterator type.

Step 1: A type

First, we need a type representing a iterator which is filtered. This type, of course, has to be abstract over the actual iterator we want to filter (that's also how the rust standard library does this under the hood). The type also has to be generic over the filter in use. So basically we write down an entirely generic type:

pub struct FilteredIterator<T, F, I>(F, I)
    where F: Filter<T>,
          I: Iterator<Item = T>;

Here F is a filter over T and I is the iterator over T. The FilteredIterator holds both F and I.

Step 2: Implement Iterator on this type

The next step is implementing iterator on this new type. Because we want to use this iterator in a chain like this, for example:

forest
    .filtered_with(TreeWithColor::new(Color::Green))
    .map(|tree| tree.get_name())
    .enumerate()
    .map(|(i, tree_name)| {
      store_tree_number(tree_name, i)
    })
    .fold(Ok(()), |acc, e| {
      // ...
    })

we have to implement Iterator on it.

Because the type is generic, so is our iterator implementation.

impl<T, F, I> Iterator for FilteredIterator<T, F, I>
    where F: Filter<T>,
          I: Iterator<Item = T>
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        // ...
    }
}

The implementation is left out as task for the reader. Hint: The only function one needs is Filter::filter() and a while let Some(e).

Step 3: Transforming one iterator into a filtered iterator

Next, we want to be able to transform an existing iterator type into a filtered iterator. As I wrote above, we do this by providing a new trait for this. Because of the generic parameters our FilteredIterator type has, we need generic types in our trait as well:

pub trait FilterWith<T, F: Filter<T>> : Iterator<Item = T> + Sized {
    fn filter_with(self, f: F) -> FilteredIterator<T, F, Self>;
}

The trait provides only one function: filter_with(). This function takes self owning and a Filter which is used in the FilteredIterator later.

Step 4: Extending all iterators

Last but not least, we implement this trait on all Iterators. Even more generics here, of course:

impl<I, T, F: Filter<T>> FilterWith<T, F> for I
    where I: Iterator<Item = T>
{
    fn filter_with(self, f: F) -> FilteredIterator<T, F, Self> {
        FilteredIterator(f, self)
    }
}

The actual implementation is trivial, sure. But the type signature is not, so I'll explain.

  1. I is the type we implement the FilterWith trait for. Because we want to implement it for all iterators, we have to use a generic type.
  2. I is, of course, generic itself. We want to be able to filter all iterators over all types. So, T is the Item of the Iterator.
  3. F is needed because FilterWith is generic over the provided filter: We can filter with any Filter we want. So we have to be generic over that as well.

The output of the filter_with function is a FilteredIterator which is generic over T and F – but wait! FilteredIterator is generic over three types!

That's true. But the third generic type is the iterator it encapsulated – the iterator it actually filters. Because of that, we return FilteredIterator<T, F, Self> here – we don't need a generic type as third type here because we know that the iterator which will be encapsulated has exactly the type which the trait gets currently implemented for.

(I hope that explanation is easy to understand.)

Conclusion

Extending types in Rust is incredible easy – if one can grok generics. I know that generics are not that easy to grok for a beginner – I had a hard time learning how to use them myself. But it is really worth it, as our test shows:

#[test]
fn test_filter_with() {
    struct Foo;
    impl Filter<u64> for Foo {
        fn filter(&self, u: &u64) -> bool {
            *u > 5
        }
    }
    let foo = Foo;

    let v : Vec<u64> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
        .into_iter()
        .filter_with(foo)
        .collect();

    assert_eq!(v, vec![6, 7, 8, 9]);
}

tags: #tools #software #rust #open-source

After reading another post in the users.rust-lang.org forum where a crate author is looking for a maintainer for his crate, I started thinking. Do we need a “crates-team” in the rust community?

Incentive

The Rust community has a lot of great teams. Each of these teams has a different goal: The Core team, for example

is responsible for steering the design and development process, overseeing the introduction of new features, and ultimately making decisions for which there is no consensus [...]

(source)

Because people change, interests of people change and also because the life-situations people are in change, a crate author might not be able to continue maintaining their crates. I, for one, have such a problem: I am about to go on a really long vacation from may 2018 until early 2019. Most of that time I will be off the grid, in a mobile home in northern America. This is a one-time opportunity for me and I am really looking forward to that experience. But because of that I will have issues maintaining my crates in that time. I have 7 projects where I cannot continue development for about one year:

  • toml-query
  • filters
  • kairos
  • task-hookrs
  • git-dit (and libgitdit)
  • is-match

and of course imag, which contains over 40 crates at the time of writing. All of the above are ready to be used (imag is not) and are, in fact, used by others from what I know.

In my case, this is only temporarily: I will be able (and am willing to) continue development on them in 2019. But people change, interests change, people get kids, a dog or some new hobby. The end result is always: They have less time (or no time anymore) to continue developing their crates.

So why not have a team available which takes over maintainership?

What a crates-team would do

The crates-team won't always be able to continue development of the crate, but it might be able to continue maintainership. That is: Merge pull requests which add new functionality (or even just discuss whether this new functionality would be a nice addition to the crate), update the dependencies from time to time, fix and close bugs in the codebase, update the documentation and document known bugs.

The team would consist of people from the community (so basically hobbyists) which are willing to maintain a few crates in their free time. The crates-team would, of course, not take over maintainership for all abandoned crates. It would rather discuss whether a crate shall be maintained by them and then do so (or not).

Some ideas on basic rules for the team:

  • Only the following things are in the scope of “maintaining” a crate in the crates-team:
    • Update the dependencies to the newest version if and only if updating to the newest version is as simple as updating the version number in the Cargo.toml file. If it is more complicated, the crates-team is allowed to reject any requests or reply to such requests with a variant of the phrase “You're welcome to file an PR for this!”
    • Updating the documentation of a crate
    • Publish new versions of the crate, the first newly published version should change the cargo metainformation maintenance = ... to passively-maintained.
    • Discuss and (if consensus is reached) merge pull requests which add new functionality to the crate
    • Merge pull requests which
      • Fix bugs
      • Update dependencies
      • Update documentation
  • A crate only gets maintained if at least 2 members of the crates-team are willing to maintain the crate (these two get rights to publish new versions)

More rules are possible, of course.

Discussion

I'd love to discuss the subject on either users.rust-lang.org or reddit!

Also, feel free to reach out via mail (the thing which looks like an 'a') (thisdomain) (dot) de.

tags: #tools #software #rust #open-source

So I started developing an importer for importing github issues into git-dit – the distributed issue tracker build on git.

Turns out it works well, though some things are not yet implemented:

  • Wrapping of text. This is difficult because quotations are wrapped, but the quotation character is not prepended to the new line – which results in broken format
  • Importing only issues. Right now, PRs are imported ... which is not exactly what we want. I really hope I can figure this out to actually attach PR comments to the actual commits of the PR. This would be really nice. Issues shall be imported without parent (orphaned) like git-dit wants it.
  • Mapping of github handles to real names and email addresses.
  • Mapping github labels to git-dit “trailers”.

Have a look at my importer tool here (Just be told: This is WIP and shouldn't be used right now)!

Or at git-dit itself here (I am co-author).

tags: #tools #software #rust #open-source #git #github

This post was written during my trip through Iceland and published much latern than it was written.

In this and also maybe in the next few articles we will focus on rather code-related things than on direct code properties. I hope that's okay.

Planning of an application or library is not easy, not at all. But how much planning do we actually do before writing code? And should we do more?

My thoughts on the subject.

What we've learned

One that has studied computer science should know at least some UML types like class diagrams, flow charts, module plans and use case diagrams. They are used in (let's call it) “normal” software development and in the professional world out there.

But when we are developing open source software for our own needs and maybe for our friends, we do that often in our chambers at home. Class diagrams are often not being developed and I can say that I never saw a hobby programmer draw a use case diagram before writing the code of the application.

Why we don't use it

Why is that? Well, because open source software is often done as a hobby type of thing, there is often no need for planning ahead. A hobbyist is able to hold use case, simple class diagrams and flow charts “in his mind” because he has great knowledge of the domain.

In fact. as he defines the domain entirety, he is both stakeholder, project leader, software architect, programmer, tester and marketing guy at the same time. He knows what problems are about to be solved and therefore can adjust every aspect of the application to the needs required.

This holds true for small and medium sized applications or code bases, where the problems is of certain complexity but not too big. Basically one could say that every aspect of the domain has to fit into one head without much effort, in the open-source-programming-at-home-world. With a bit of training, I believe, one can even get to a point where only a few aspects of the domain have to be in a persons mind to be able to work on a solution

But there is certainly a point where the effort needed to solve a specific problem explodes. One can still write software to solve the problem at hand, but not in reasonable time.

So why don't we hobby programmers do not use planning tools like we've learned in university? Why don't we use diagrams to make things clearer, better documented, even before the real programming starts? The answer is quite simple: because it annoys the hell out of us. We don't like to plan ahead. We don't like to adjust plans as soon as we find out that changing a small aspect of our library could be changed to gain more flexibility and overall goodness. We don't like to check our plans before writing down the next module until it works.

Coding is fun, planning is not.

But should we use these things

In my opinion, this is foolish. We really should use the things we learned in university to plan out software and of course also to document it. It would be such a huge improvement of everything to simply think a bit more about it before actually implementing it!

How we do it

What we do and why we do not use tools to plan ahead is explained with one sentence: We program from the user interface to the implementation, because the other way round is to complicated. Or, with other words: We program top-down because bottom-up needs planning and therefore not that easy.

Of course, I'm speaking about the average case. I've programmed bottom-up before but, for me, it seems much more error prone than top-down does, especially without a plan.

Also, I do not say that top-down is not error prone. Not at all. When writing an API without an actual implementation in mind, one easily results in sacrificing cleanness and speed at some points to keep the API nice, which is not always a good idea. So top-down is only good as long as we get it right.

Tooling

Tooling is one big problem in this context. We do not have a toolchain for planning just yet. At least I do not have one that I would like to use. Because we are really good at controlling (versioning, moving around, managing) our source code (for example with git, and to some extend github), we also want to be able to do this with charts diagrams. But we also want the niceness of SVG-rendered graphics. We don't want to play around with layout all day long, but use tools to simply get the job done.

And there are no such tools available.

Sure, one can use graphviz to design such things, but then again we do not have a nice overview on what's going on while editing our work. One could use ascii-art to draw all those things, but hey... ascii-art. We are better than that, aren't we? We could render the ascii-art into SVG... though the tooling there is not yet as good as it should be. And even if it would be, version controlling these things with git is (I fail to believe otherwise) painful.

Conclusion

Well, I can only conclude the obvious here. We need better tooling for the open source programming community to do their planning, if they need to. Clearly, one does not always have to (or want to) plan things before trying out. But when one does, the tooling should be there and be useful and help with the process.

Next

In the next episode we will talk about version control of open source software projects. I'm not going into details about git or other systems used, but rather on the style how they should be used so everyone is pleased with it. This might be strongly biased, but hey, isn't this whole article series biased?

tags: #open-source #programming #software #tools #rust