Skip to content

Instantly share code, notes, and snippets.

@kassane
Created December 7, 2023 13:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kassane/a3d60f4467e68bc827bde9c8409d150c to your computer and use it in GitHub Desktop.
Save kassane/a3d60f4467e68bc827bde9c8409d150c to your computer and use it in GitHub Desktop.

Named arguments in D language

DIP1030

Named/labeled/keyword arguments are useful in Python and OCaml, but in the D language they were proposed by DIP1030 and adopted three years ago.

Currently, templates are not supported, but recent DMDs -preview allow you to try out these features without any flags. The verification environment this time is DMD2.106.0. function call

Now let's take an example of basic usage.

import std;

void f(int a, int b) {
  writeln("f(a: ", a, ", b: ", b, ")");
}

void g(int a = 0, int b) {
  f(a: a, b: b);
}

void main() {
  f(a: 1, b: 2);
  f(a: 1, 2);
  f(1, b: 2);
  1.f(b: 2);
  // f(b: 1, 2); error                                                                                                                                                           
  // 2.f(a: 1); error                                                                                                                                                            
  g(b: 2);
  // g(1); error
}

Basically, there are benefits such as being able to freely specify the order in which they are called, and if the names have meaning, they are easier to read. Unlike Python, it's nice to have more freedom, such as being able to declare parameters with default values ​​before formal parameters without default values, and unnamed parameters being allowed to come after named parameters.

struct/class initialization

Speaking of function-like things, structures and classes can also be constructed with named arguments.

struct S {
  int a;
  int b;
}

struct SS {
  S s;
  int c;
}

class C {
  this(int a, int b) {
    writeln("C(a:", a, ", b:", b, ")");
  }
}

void main() {
  S s = {a: 1, b: 2};
  writeln(s);
  // writeln(S{a:1, b:2}); // error
  writeln(S(a:1, b:2));
  SS ss = {{a: 1, b: 2}, c: 3};
  writeln(ss);
  writeln(SS(S(a: 1, b: 2), c: 3));

  C c = new C(a:1, b:2);
}

Until now, structures had static initialization {} using curly braces, but it was not possible to write something like this, and it was a hassle to assign it to some variable and then call it. Named arguments can avoid this. It is also possible to call constructors with named arguments, something that was not possible with classes. foo(S{a:1, b:2})S s = {a:1, b:2}; foo(s)

Will it be a paradigm shift?

Currently, named arguments for templates have not been implemented, so no one knows about this feature without any announcement, but we believe that it will revolutionize library design even now.

For example, it is a use case that requires building with a name like HTML.

string html(string lang, string inner) {
  return "<html lang=\"" ~ lang ~ "\">" ~ inner ~ "</html>";
}

string img(string src, string alt) {
  return "<img src=\"" ~ src ~ "\", alt=\"" ~ alt ~ "\" />";
}

unittest {
  html(lang: "ja", img(src: "hello.png", alt: "hello image"));
}

Or, functions in fields such as linear algebra and machine learning have a large number of arguments, and are useful when only a part of them needs to be changed (example of optax.adam)

void adam(float[] params, float[] states, float lr = 1e-4, float beta1 = 0.9, float beta2 = 0.999, float eps=1e-08, float eps_root=0.0);

unittest {
  adam(params, states, beta2: 0.98);
}

A design that strictly favors named arguments like OCaml's Core is also interesting https://opensource.janestreet.com/core/

Reference link

Original post

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment