Thursday, July 26, 2012

Problems with D

D has a lot of nice features. But it also has...
  • Unintuitive syntax:
    • advanced import syntax is rather baffling to me
    • contents of "is(...)" looks like an expression, but it's not
    • 4-5 meanings for "static"
    • unnecessarily confusing tuple syntax (e.g. implicit tuple expansion)
    • inconsistency between template declaration and call site ("foo(X, Y)(X x, Y y)" vs "foo!(X, Y)(x, y)")
    • even the humble foreach loop--why foreach (x; y) instead of foreach(x in y), given that "in" is a reserved word anyway?
  • Emphasis on C heritage: "break" in switch cases, function definitions are not visually distinctive or greppable, no pattern-matching or destructuring, no statements-as-expressions (e.g. str = switch(x) { case 1: "one"; case 2: "two"; }), overuse of 'static' and parenthesis...
  • Too many different rules (e.g. symbol lookup works differently for modules than for static symbols in classes and mixins. lookup rules are also different for UFCS/member functions, although in this case there is a good reason for the difference.)
  • Types and identifiers seem to share a single namespace:
    class Door { }
    class Fridge {
      Door Door; // I do this often in C# and even C++, but illegal in D.
    }
    
    This severity of this issue is reduced by the fact that field and function names in D tend to use lowerCamelCase while type names more often use PascalCase, unlike C# where both types and member names use PascalCase.
  • Syntax feels ambiguous in some places, as if it wasn't fully thought out. TODO: find a good example.
  • Users cannot make contracts work in production code (Release builds)
  • String mixins will be hostile to IDE analysis and refactoring tools; we need to think of a better approach to metaprogramming.
  • Using CTFE (compile-time function evaluation) in combination with static if (including "if" for overload resolution) will also be difficult for IDEs due to their unbounded CPU usage, but this feature is so powerful it's probably worth the trouble.
  • Pet peeve: "ref" and "out" are not allowed at call sites. I don't mind if D does not require "ref" and "out" at call sites, but it should at least allow it. For clarity, in C++ I already use "OUT" at call sites as well as in function declarations (it's pre-#defined on Windows in WinDef.h), but D doesn't have a preprocessor so it can't support the same.
  • foreach can behave in several different ways. The way the compiler selects between these behaviors is unintuitive.
  • D seems not to have enough rules to prevent escape of stack pointers into heap objects or parent stack frames:
    int* ptrEscape()
    {
      int x = 7;
      return oops(&x);
    }
    int* oops(int* ptr) { return ptr; }
    
    // For extra fun, try this:
    int doubleOops() { int* x = ptrEscape(); int y = *x + 1; return *x + y; }
    enum DoubleOops = doubleOops();
    // Result in DMD 2.059 Windows:
    // Assertion failure: '0' on line 2018 in file 'interpret.c'
    // Plus a message box appears: "abnormal program termination"
    
  • D has nice metaprogramming facilities but seemingly not as good as I was hoping; for example there is no custom attribute system, and I don't think there is a way to send a D AST to a template, so one cannot write compile-time macros that restructure D code. Also, D compiler-as-a-service is not yet implemented.
Frankly, I wish D would copy more ideas straight from C#, because C# is mostly quite well designed, although it doesn't have as many features as D (notably absent: slices, mixins, compile-time templates, scope(exit))

0 Comments:

Post a Comment

<< Home