The feature that makes D my favorite programming language

UFCS gives you the power to compose reusable code that has a natural flow without sacrificing convenience.
80 readers like this
80 readers like this
Coding on a computer

Back in 2017, I wrote about why the D programming language is a great choice for development. But there is one outstanding feature in D I didn't expand enough on: the Universal Function Call Syntax (UFCS). UFCS is a syntactic sugar in D that enables chaining any regular function on a type (string, number, boolean, etc.) like its member function of that type.

If you don't already have D installed, install a D compiler so you can run the D code in this article yourself.

Consider this example code:

// file: ufcs_demo.d

module ufcs_demo;

import std.stdio : writeln;

int[] evenNumbers(int[] numbers)
{
    import std.array : array;
    import std.algorithm : filter;

    return numbers.filter!(n => n % 2 == 0).array;
}

void main()
{
    writeln(evenNumbers([1, 2, 3, 4]));
}

Compile this with your favorite D compiler to see what this simple example application does:

$ dmd ufcs_demo.d
$ ./ufcs_demo
[2, 4]

But with UFCS as a built-in feature of D, you can also write your code in a natural way:

...
writeln([1, 2, 3, 4].evenNumbers());
...

or completely remove the now-redundant parenthesis to make it feel like evenNumbers is a property:

...
writeln([1, 2, 3, 4].evenNumbers); // prints 2, 4
...

So the complete code now becomes:

// file: ufcs_demo.d

module ufcs_demo;

import std.stdio : writeln;

int[] evenNumbers(int[] numbers)
{
    import std.array : array;
    import std.algorithm : filter;

    return numbers.filter!(n => n % 2 == 0).array;
}

void main()
{
    writeln([1, 2, 3, 4].evenNumbers);
}

Compile it with your favorite D compiler and try it out. As expected, it produces the same output:

$ dmd ufcs_demo.d
$ ./ufcs_demo
[2, 4]

During compilation, the compiler automatically places the array as the first argument to the function. This is a regular pattern that makes using D such a joy, so it very much feels the same as you naturally think about your code. The result is functional-style programming.

You can probably guess what this prints:

//file: cool.d
import std.stdio : writeln;
import std.uni : asLowerCase, asCapitalized;

void main()
{
    string mySentence = "D IS COOL";
    writeln(mySentence.asLowerCase.asCapitalized);
}

But just to confirm:

$ dmd cool.d
$ ./cool
D is cool

Combined with other D features, UFCS gives you the power to compose reusable code that has a natural flow to it without sacrificing convenience.

Time to try D

As I've written before, D is a great language for development. It's easy to install from the D download page, so download the compiler, take a look at the examples, and experience D for yourself.

What to read next
Lawrence is a freelance full stack engineer (React, Node.Js, D) and a 100% Linux user. He loves to design and code. Passionate about SaaS and the modern Web.

29 Comments

Even before you started explaining UFCS, you were using it.

`numbers.filter!(n => n % 2 == 0).array`

Both filter and array take an argument

`array(filter!(n => n % 2 == 0)(numbers))`

You're right Jesse. What I did instead was to mention my previous post which did talk about the filter function...which uses both UFCS and templates. So I didn't have to explain that filter!... template function and the different calling conventions too.

In reply to by Jesse (not verified)

Im web developer i have acceoted the terms and conditions of the site

Nim has this as well if you're interested in new languages

Good to know. It's something I think every modern language should consider. Especially when it can be resolved at compile time with no runtime cost. D seems to have inspired Nim in some way.

In reply to by jyapayne (not verified)

And? What new problem does this solve?

Hi. I'm the author of Next Generation Shell. Without knowing D or what UFCS was (at the time), I have added support for UFCS to NGS just because it made sense, especially when combined with multiple-dispatch. It's nice to hear that the UFCS feature is praised.

Just to give some feeling of NGS:

--- D ---
module ufcs_demo;

import std.stdio : writeln;

int[] evenNumbers(int[] numbers)
{
import std.array : array;
import std.algorithm : filter;

return numbers.filter!(n => n % 2 == 0).array;
}

void main()
{
writeln([1, 2, 3, 4].evenNumbers);
}

--- NGS ---

F even_numbers(numbers:Arr) {
numbers.filter(F(n) n % 2 == 0)
}

F main() {
[1,2,3,4].even_numbers().echo()
}

---

Next Generation Shell project is at https://github.com/ngs-lang/ngs

UFCS seems very similar to .Net extensions. What are the differences between them?
Does the compiler optimize the output of UFCS instructions?

I'm not familiar with .Net extensions. However with D, the compiler rewrites it like how you'd normal call it. So it's a convenient syntax for the programmer.

So 1.add(2): gets rewritten to add(2,2) during compilation.

In reply to by Rubidium37 (not verified)

Looks like the pipe operator in Elixir, Elm, Haskell, etc, or the arrow macro in Clojure.

- I don't see how this is any better than writing a method
- what happens if you need to add arguments later on ?
- disguising methods as properties makes it hard to understand the performance implications of calling them (reading a property is a no-op, calling a function is not)
- adding properties & methods to a standard type is at best weird, at worst really confusing
:/

Hey Arthur, UFCS helps avoid writing code like: finish(make(bake(1,4)));

In D you can write it as: bake(1,2).make(). finish () OR more idiomatically, 1.bake(3).make.finish. So it reads in the order you'd process it in your mind when reading the code.

Any other extra argument gets written like 1.call(2,3,4,5,...). of course if using UFCS makes sense only in that particular context.

UFCS is actually just a syntax trick. It comes at no extra performance cost. The D compiler rewrites it to normal styling during compilation. But for the programmer, UFCS just beautifies your code. So its more lightweight compared to using a class. Sometimes people write classes to get a similar feel but with D UFCS gives you that experience. UFCS is not built into the types, its just a syntactic sugar.

In reply to by Arthur W. (not verified)

Nice guys.. This is very interesting to learn about D programming. My first programming language was Pascal then VBA. I used to disregard D to be boring as I shifted to formatting and scripting languages such as Rubby n Rays and Python. I now know that D has few similarity with pascal and .NET. I like the integration. I can use the VBA API piping with ease. Will try D finally.

The even numbers function may as well be an alias for filter! (...).

You even don't need to download a compiler just to play around. There's a https://run.dlang.io/ to try it out without installing.

As you mentioned it is syntactic sugar. Impeoves code readability - making it closer to real English. So why not
writeln.mySentence.asLowerCase.asCapitalized;

Or bring even closer:
print mySentence asLowerCase asCapitalized

Would love to see a language like this.

I will tray UFCS, appears be cool!??

I absolutely love D, and the feel of its progression from C/C++, hard to believe it's already 20 years since its release. These days it's looking like a mix of C/C++ with a dash of ruby. I wish I could use it in a professional capacity. Last year i had to clean up an absolute mess written in C++ that was leaking GB/s of ram (no lie), this would have been a great project to rewrite in D. for an old C/C++ programmer like me, the syntax is very easy on the eyes, compared to the likes of the flavour du jour Rust :)

Hi Steeve, I see similar comments quite often. Most of the D programmers are/were also C++ guys also. Part of why D has one of the strongest C/C++ code interoperability.

In reply to by Steeve

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.