Last year, my theme was "yes, D *can* do that" and "just try it!", with a focus on lower level code. This year, I'd like to keep the same idea, but on the other end of the spectrum: the same D language that we can use to write bare-metal code can also write Javascript-style code.

I'll talk primarily about my jsvar.d, and some about its (optional) companion, script.d, showing what they can do and how I was able to do it.

Kind: a song and dance number regular talk. Maybe I'll actually make slides this time too! lol Target audience: intermediate, you'll want to know some D but maybe you haven't thought of using it this way before.

Outline of highlights:

  • What is a dynamic language? Why can't D do that too, like Javascript? It can!
  • 	// this is valid D code! 
    	var a = 10; 
    	var b = "20"; 
    	var c = a + b; 
    	var d = json!q{ "foo": { "bar": 10.2 } }; 
    	writeln(; // {"bar":10.2} = (var a) => a ~ b; 
    	writeln("hello! ")); 
  • What does D need with a dynamic type?
  • External API or script interaction are practical uses of the result, and the techniques are applicable to many other places.

  • Mention Variant but not really talking about it, its approach is more to just be a container of another type rather than being the weakly typed, dynamic object jsvar offers.
  • 	import std.variant;
    	Variant a = "test";
    	string s = a.get!string;
  • How to do the operator overloads in D and opDispatch. When combined with templates, this gives easy bridging between the dynamic type and other static types.
  • 	public var opBinary(string op, T)(T t) {
    		var n;
    		if(payloadType() == Type.Object) {
    			var* operator = this._payload._object._peekMember("opBinary", true);
    			if(operator !is null && operator._type == Type.Function) {
    				return, op, t);
    		return _op!(n, this, op, T)(t);
    	public var opBinaryRight(string op, T)(T s) {
    		return var(s).opBinary!op(this);
  • How we can use compile-time reflection to handle even complex cases like functions.
  • 	} else static if(isCallable!T) {
    		this._type = Type.Function;
    		static if(is(T == typeof(this._payload._function))) {
    			this._payload._function = t;
    		} else
    		this._payload._function = delegate var(var _this, var[] args) {
    			var ret;
    			ParameterTypeTuple!T fargs;
    			foreach(idx, a; fargs) {
    				if(idx == args.length)
    				cast(Unqual!(typeof(a))) fargs[idx] = args[idx].get!(typeof(a));
    			static if(is(ReturnType!t == void)) {
    			} else {
    				ret = t(fargs);
    			return ret;
    	// and also wrapped native classes, automatically
    	WrappedNativeObject wrapNativeObject(Class)(Class obj) if(is(Class == class)) {
    		return new class WrappedNativeObject {
    			override Object getObject() {
    				return obj;
    			this() {
    				wrappedType = typeid(obj);
    				// wrap the other methods
    				// and wrap members as scriptable properties
    				foreach(memberName; __traits(allMembers, Class)) {
    					static if(is(typeof(__traits(getMember, obj, memberName)) type))
    					static if(is(typeof(__traits(getMember, obj, memberName)))) {
    						static if(is(type == function)) {
    							_properties[memberName] = &__traits(getMember, obj, memberName);
    						} else {
    							// if it has a type but is not a function, it is prolly a member
    							_properties[memberName] = new PropertyPrototype(
    								() => var(__traits(getMember, obj, memberName)),
    								(var v) {
    								static if(memberName == "handleCharEvent") {
    								import std.stdio; writeln("setting ", memberName, " to ", v.get!type.ptr);
    									__traits(getMember, obj, memberName) = v.get!(type);
  • talk about the ref return and @properties (still broken!). making null into an lvalue
  • 	public @property ref var opDispatch(string name, string file = __FILE__, size_t line = __LINE__)() {
    		return this[name];
    	public @property ref var opDispatch(string name, string file = __FILE__, size_t line = __LINE__, T)(T r) {
    		return this.opIndexAssign!T(r, name);
    	public ref var NULL() {
    		var* v = new var;
    		*v = null;
    		return *v;
  • dangers of delegates in structs and using a static nested function to capture specific variables
  • 	else static if(isDelegate!T) {
    		// making a local copy because otherwise the delegate might refer to a struct on the stack and get corrupted later or something
    		auto func = this._payload._function;
    		// the static helper lets me pass specific variables to the closure
    		static T helper(typeof(func) func) {
    			return delegate ReturnType!T (ParameterTypeTuple!T args) {
    				var[] arr;
    				foreach(arg; args)
    					arr ~= var(arg);
    				var ret = func(var(null), arr);
    				static if(is(ReturnType!T == void))
    					return ret.get!(ReturnType!T);
    		return helper(func);
  • the tricks used in the script interpreter - CtList + mixin to automate a lot of code generation while still being runtime scriptable
  • 	class CastExpression : Expression {
    		string type;
    		Expression e1;
    		override string toString() {
    			return "cast(" ~ type ~ ") " ~ e1.toString();
    		override InterpretResult interpret(PrototypeObject sc) {
    			var n = e1.interpret(sc).value;
    			foreach(possibleType; CtList!("int", "long", "float", "double", "real", "char", "dchar", "string", "int[]", "string[]", "float[]")) {
    				if(type == possibleType)
    					n = mixin("cast(" ~ possibleType ~ ") n");
    			return InterpretResult(n, sc);
  • demo some applications using them. maybe show the terminal.d and simpledisplay.d stuff too
  • 	// why an argument? It needs to initialize and there's no default ctor!
    	auto terminal = Terminal(ConsoleOutputMode.linear);
    	terminal.getline(); // supports editing
  • my inspector
  • 	var globals = var.emptyObject;
    	globals.loadJsonFile = delegate var(string name) { 
    		import std.file; 
    		return var.fromJson(readText(name)); 
    	globals.saveJsonFile = delegate var(string name, var obj) { 
    		import std.file; 
    		write(name, obj.toJson()); 
    		return obj; 
    	// wrapping my http2.d was easy too!
    	globals["get"] = delegate var(string path) { 
    		auto request = client.navigateTo(Uri(path)); 
    		return var(request.waitForCompletion()); 
    	writeln(interpret(line, globals));
    Speaker bio:

    Adam D. Ruppe is a master programmer and serious contender - if not winner - for world champion of D for seven consecutive years and counting. He is among the most prolific D programmers on the planet, having written all manner of code in it from bare metal programs to professional web projects, and is also the author of the critically praised and community renowned "D Cookbook".

    With expert level knowledge in several programming languages, techniques, and domains coupled with versatile practicality and humble generosity, Adam rarely finds a problem he cannot solve and is a common sight in the D community, always willing - and able - to help on nearly any user question.

    Recent D projects:
  • The JSON inspector seen above, utilizing a pure-D async HTTP library, pure D script interpreter and pure D terminal library:
  • A simple volume control, showing OS interaction and terminal UI:
  • A cross-platform terminal emulator in pure D, using a D windowing library:
  • Windows COM helpers:
  • And, of course, the always growing collection of useful modules, including jsvar.d, script.d, dom.d, and much more:

  • Page generated by Ddoc.