diff --git a/source/dub/commandline.d b/source/dub/commandline.d
index 6980f3f..61c53e6 100644
--- a/source/dub/commandline.d
+++ b/source/dub/commandline.d
@@ -35,7 +35,6 @@
 import std.stdio;
 import std.string;
 import std.typecons : Tuple, tuple;
-import std.variant;
 import std.path: setExtension;
 
 /** Retrieves a list of all available commands.
@@ -598,8 +597,10 @@
 */
 class CommandArgs {
 	struct Arg {
-		Variant defaultValue;
-		Variant value;
+		alias Value = SumType!(string[], string, bool, int, uint);
+
+		Value defaultValue;
+		Value value;
 		string names;
 		string[] helpText;
 		bool hidden;
@@ -658,15 +659,24 @@
 
 	void getopt(T)(string names, T* var, void delegate(string, string) @safe parseValue, string[] help_text = null, bool hidden=false)
 	{
+		import std.traits : OriginalType;
+
 		foreach (ref arg; m_recognizedArgs)
 			if (names == arg.names) {
 				assert(help_text is null, format!("Duplicated argument '%s' must not change helptext, consider to remove the duplication")(names));
-				*var = arg.value.get!T;
+				*var = arg.value.match!(
+					(OriginalType!T v) => cast(T)v,
+					(_) {
+						if (false)
+							return T.init;
+						assert(false, "value from previous getopt has different type than the current getopt call");
+					}
+				);
 				return;
 			}
 		assert(help_text.length > 0);
 		Arg arg;
-		arg.defaultValue = *var;
+		arg.defaultValue = cast(OriginalType!T)*var;
 		arg.names = names;
 		arg.helpText = help_text;
 		arg.hidden = hidden;
@@ -674,7 +684,7 @@
 			m_args.getopt(config.passThrough, names, var);
 		else
 			m_args.getopt(config.passThrough, names, parseValue);
-		arg.value = *var;
+		arg.value = cast(OriginalType!T)*var;
 		m_recognizedArgs ~= arg;
 	}
 
@@ -2725,13 +2735,16 @@
 		} else writeWS(longArgColumn);
 		size_t col = longArgColumn;
 		if (larg !is null) {
-			if (arg.defaultValue.peek!bool) {
-				writef("--%s", larg);
-				col += larg.length + 2;
-			} else {
-				writef("--%s=VALUE", larg);
-				col += larg.length + 8;
-			}
+			arg.defaultValue.match!(
+				(bool b) {
+					writef("--%s", larg);
+					col += larg.length + 2;
+				},
+				(_) {
+					writef("--%s=VALUE", larg);
+					col += larg.length + 8;
+				}
+			);
 		}
 		if (col < descColumn) {
 			writeWS(descColumn - col);
diff --git a/source/dub/internal/sdlang/ast.d b/source/dub/internal/sdlang/ast.d
index deda488..d892ca2 100644
--- a/source/dub/internal/sdlang/ast.d
+++ b/source/dub/internal/sdlang/ast.d
@@ -23,6 +23,8 @@
 import dub.internal.sdlang.token;
 import dub.internal.sdlang.util;
 
+import dub.internal.dyaml.stdsumtype;
+
 class Attribute
 {
 	Value    value;
@@ -1102,7 +1104,10 @@
 
 		// Values
 		foreach(val; values)
-			buf.put("    (%s): %s\n".format(.toString(val.type), val));
+			buf.put("    (%s): %s\n".format(
+				val.match!(v => typeof(v).stringof),
+				val
+			));
 
 		// Attributes
 		foreach(attrNamespace; _attributes.keys.sort())
@@ -1116,7 +1121,9 @@
 
 			buf.put(
 				"    %s%s(%s): %s\n".format(
-					namespaceStr, attr._name, .toString(attr.value.type), attr.value
+					namespaceStr, attr._name,
+					attr.value.match!(v => typeof(v).stringof),
+					attr.value
 				)
 			);
 		}
diff --git a/source/dub/internal/sdlang/parser.d b/source/dub/internal/sdlang/parser.d
index 24bb16e..7fd1f38 100644
--- a/source/dub/internal/sdlang/parser.d
+++ b/source/dub/internal/sdlang/parser.d
@@ -7,7 +7,6 @@
 else:
 
 import std.file;
-import std.variant : Algebraic;
 
 import dub.internal.libInputVisitor;
 
@@ -18,6 +17,8 @@
 import dub.internal.sdlang.token;
 import dub.internal.sdlang.util;
 
+import dub.internal.dyaml.stdsumtype;
+
 /// Returns root tag.
 Tag parseFile(string filename)
 {
@@ -119,7 +120,7 @@
 }
 
 /// The element of the InputRange returned by pullParseFile and pullParseSource:
-alias ParserEvent = Algebraic!(
+alias ParserEvent = SumType!(
 	FileStartEvent,
 	FileEndEvent,
 	TagStartEvent,
@@ -483,41 +484,41 @@
 		auto eventRange = inputVisitor!ParserEvent( parser );
 		foreach(event; eventRange)
 		{
-			if(auto e = event.peek!TagStartEvent())
-			{
-				auto newTag = new Tag(currTag, e.namespace, e.name);
-				newTag.location = e.location;
+			event.match!(
+				(TagStartEvent e)
+				{
+					auto newTag = new Tag(currTag, e.namespace, e.name);
+					newTag.location = e.location;
 
-				currTag = newTag;
-			}
-			else if(event.peek!TagEndEvent())
-			{
-				currTag = currTag.parent;
+					currTag = newTag;
+				},
+				(TagEndEvent _)
+				{
+					currTag = currTag.parent;
 
-				if(!currTag)
-					parser.error("Internal Error: Received an extra TagEndEvent");
-			}
-			else if(auto e = event.peek!ValueEvent())
-			{
-				currTag.add(e.value);
-			}
-			else if(auto e = event.peek!AttributeEvent())
-			{
-				auto attr = new Attribute(e.namespace, e.name, e.value, e.location);
-				currTag.add(attr);
-			}
-			else if(event.peek!FileStartEvent())
-			{
-				// Do nothing
-			}
-			else if(event.peek!FileEndEvent())
-			{
-				// There shouldn't be another parent.
-				if(currTag.parent)
-					parser.error("Internal Error: Unexpected end of file, not enough TagEndEvent");
-			}
-			else
-				parser.error("Internal Error: Received unknown parser event");
+					if(!currTag)
+						parser.error("Internal Error: Received an extra TagEndEvent");
+				},
+				(ValueEvent e)
+				{
+					currTag.add(e.value);
+				},
+				(AttributeEvent e)
+				{
+					auto attr = new Attribute(e.namespace, e.name, e.value, e.location);
+					currTag.add(attr);
+				},
+				(FileStartEvent _)
+				{
+					// Do nothing
+				},
+				(FileEndEvent _)
+				{
+					// There shouldn't be another parent.
+					if(currTag.parent)
+						parser.error("Internal Error: Unexpected end of file, not enough TagEndEvent");
+				}
+			);
 		}
 
 		return currTag;
diff --git a/source/dub/internal/sdlang/token.d b/source/dub/internal/sdlang/token.d
index a86ae5d..c308ef0 100644
--- a/source/dub/internal/sdlang/token.d
+++ b/source/dub/internal/sdlang/token.d
@@ -13,7 +13,8 @@
 import std.range;
 import std.string;
 import std.typetuple;
-import std.variant;
+
+import dub.internal.dyaml.stdsumtype;
 
 import dub.internal.sdlang.symbol;
 import dub.internal.sdlang.util;
@@ -80,16 +81,16 @@
 Date Time (with an unknown timezone): DateTimeFracUnknownZone
 +/
 alias TypeTuple!(
+	typeof(null),
 	bool,
 	string, dchar,
 	int, long,
 	float, double, real,
 	Date, DateTimeFrac, SysTime, DateTimeFracUnknownZone, Duration,
 	ubyte[],
-	typeof(null),
 ) ValueTypes;
 
-alias Algebraic!( ValueTypes ) Value; ///ditto
+alias SumType!ValueTypes Value; /// ditto
 
 template isSDLSink(T)
 {
@@ -124,16 +125,7 @@
 
 void toSDLString(Sink)(Value value, ref Sink sink) if(isOutputRange!(Sink,char))
 {
-	foreach(T; ValueTypes)
-	{
-		if(value.type == typeid(T))
-		{
-			toSDLString( value.get!T(), sink );
-			return;
-		}
-	}
-
-	throw new Exception("Internal SDLang-D error: Unhandled type of Value. Contains: "~value.toString());
+	value.match!(v => toSDLString(v, sink));
 }
 
 void toSDLString(Sink)(typeof(null) value, ref Sink sink) if(isOutputRange!(Sink,char))
@@ -362,7 +354,6 @@
 	{
 		if(
 			this.symbol     != b.symbol     ||
-			this.value.type != b.value.type ||
 			this.value      != b.value
 		)
 			return false;
diff --git a/source/dub/recipe/sdl.d b/source/dub/recipe/sdl.d
index facc603..9e10e49 100644
--- a/source/dub/recipe/sdl.d
+++ b/source/dub/recipe/sdl.d
@@ -9,6 +9,7 @@
 
 import dub.compilers.compiler;
 import dub.dependency;
+import dub.internal.dyaml.stdsumtype;
 import dub.internal.logging;
 import dub.internal.sdlang;
 import dub.internal.vibecompat.inet.path;
@@ -17,8 +18,7 @@
 import std.algorithm : map;
 import std.array : array;
 import std.conv;
-import std.string : startsWith;
-
+import std.string : startsWith, format;
 
 void parseSDL(ref PackageRecipe recipe, string sdl, string parent_name, string filename)
 {
@@ -180,29 +180,29 @@
 {
 	enforceSDL(t.values.length != 0, "Missing dependency name.", t);
 	enforceSDL(t.values.length == 1, "Multiple dependency names.", t);
-	auto pkg = expandPackageName(t.values[0].get!string, package_name, t);
+	auto pkg = expandPackageName(t.values[0].expect!string(t), package_name, t);
 	enforceSDL(pkg !in bs.dependencies, "The dependency '"~pkg~"' is specified more than once.", t);
 
 	Dependency dep = Dependency.any;
 	auto attrs = t.attributes;
 
 	if ("path" in attrs) {
-		dep = Dependency(NativePath(attrs["path"][0].value.get!string));
+		dep = Dependency(NativePath(attrs["path"][0].value.expect!string(t, t.fullName ~ " path")));
 	} else if ("repository" in attrs) {
 		enforceSDL("version" in attrs, "Missing version specification.", t);
 
-		dep = Dependency(Repository(attrs["repository"][0].value.get!string,
-                                    attrs["version"][0].value.get!string));
+		dep = Dependency(Repository(attrs["repository"][0].value.expect!string(t, t.fullName ~ " repository"),
+                                    attrs["version"][0].value.expect!string(t, t.fullName ~ " version")));
 	} else {
 		enforceSDL("version" in attrs, "Missing version specification.", t);
-		dep = Dependency(attrs["version"][0].value.get!string);
+		dep = Dependency(attrs["version"][0].value.expect!string(t, t.fullName ~ " version"));
 	}
 
 	if ("optional" in attrs)
-		dep.optional = attrs["optional"][0].value.get!bool;
+		dep.optional = attrs["optional"][0].value.expect!bool(t, t.fullName ~ " optional");
 
 	if ("default" in attrs)
-		dep.default_ = attrs["default"][0].value.get!bool;
+		dep.default_ = attrs["default"][0].value.expect!bool(t, t.fullName ~ " default");
 
 	bs.dependencies[pkg] = dep;
 
@@ -317,7 +317,7 @@
 private void parseToolchainRequirements(ref ToolchainRequirements tr, Tag tag)
 {
 	foreach (attr; tag.attributes)
-		tr.addRequirement(attr.name, attr.value.get!string);
+		tr.addRequirement(attr.name, attr.value.expect!string(tag));
 }
 
 private Tag toSDL(const ref ToolchainRequirements tr)
@@ -334,7 +334,6 @@
 private string expandPackageName(string name, string parent_name, Tag tag)
 {
 	import std.algorithm : canFind;
-	import std.string : format;
 	if (name.startsWith(":")) {
 		enforceSDL(!parent_name.canFind(':'), format("Short-hand packages syntax not allowed within sub packages: %s -> %s", parent_name, name), tag);
 		return parent_name ~ name;
@@ -343,64 +342,79 @@
 
 private string stringTagValue(Tag t, bool allow_child_tags = false)
 {
-	import std.string : format;
 	enforceSDL(t.values.length > 0, format("Missing string value for '%s'.", t.fullName), t);
 	enforceSDL(t.values.length == 1, format("Expected only one value for '%s'.", t.fullName), t);
-	enforceSDL(t.values[0].peek!string !is null, format("Expected value of type string for '%s'.", t.fullName), t);
 	enforceSDL(allow_child_tags || t.tags.length == 0, format("No child tags allowed for '%s'.", t.fullName), t);
 	// Q: should attributes be disallowed, or just ignored for forward compatibility reasons?
 	//enforceSDL(t.attributes.length == 0, format("No attributes allowed for '%s'.", t.fullName), t);
-	return t.values[0].get!string;
+	return t.values[0].expect!string(t);
+}
+
+private T expect(T)(
+	Value value,
+	Tag errorInfo,
+	string customFieldName = null,
+	string file = __FILE__,
+	int line = __LINE__
+)
+{
+	return value.match!(
+		(T v) => v,
+		(fallback)
+		{
+			enforceSDL(false, format("Expected value of type " ~ T.stringof ~ " for '%s', but got %s.",
+				customFieldName.length ? customFieldName : errorInfo.fullName,
+				typeof(fallback).stringof),
+				errorInfo, file, line);
+			return T.init;
+		}
+	);
 }
 
 private string[] stringArrayTagValue(Tag t, bool allow_child_tags = false)
 {
-	import std.string : format;
 	enforceSDL(allow_child_tags || t.tags.length == 0, format("No child tags allowed for '%s'.", t.fullName), t);
 	// Q: should attributes be disallowed, or just ignored for forward compatibility reasons?
 	//enforceSDL(t.attributes.length == 0, format("No attributes allowed for '%s'.", t.fullName), t);
 
 	string[] ret;
-	foreach (v; t.values) {
-		enforceSDL(t.values[0].peek!string !is null, format("Values for '%s' must be strings.", t.fullName), t);
-		ret ~= v.get!string;
+	foreach (i, v; t.values) {
+		ret ~= v.expect!string(t, text(t.fullName, "[", i, "]"));
 	}
 	return ret;
 }
 
-private void parsePlatformStringArray(Tag t, ref string[][string] dst)
+private string getPlatformSuffix(Tag t, string file = __FILE__, int line = __LINE__)
 {
 	string platform;
 	if ("platform" in t.attributes)
-		platform = t.attributes["platform"][0].value.get!string;
-	dst[platform] ~= t.values.map!(v => v.get!string).array;
+		platform = t.attributes["platform"][0].value.expect!string(t, t.fullName ~ " platform", file, line);
+	return platform;
+}
+
+private void parsePlatformStringArray(Tag t, ref string[][string] dst)
+{
+	string platform = t.getPlatformSuffix;
+	dst[platform] ~= t.values.map!(v => v.expect!string(t)).array;
 }
 private void parsePlatformStringAA(Tag t, ref string[string][string] dst)
 {
-	import std.string : format;
-	string platform;
-	if ("platform" in t.attributes)
-		platform = t.attributes["platform"][0].value.get!string;
+	string platform = t.getPlatformSuffix;
 	enforceSDL(t.values.length == 2, format("Values for '%s' must be 2 required.", t.fullName), t);
-	enforceSDL(t.values[0].peek!string !is null, format("Values for '%s' must be strings.", t.fullName), t);
-	enforceSDL(t.values[1].peek!string !is null, format("Values for '%s' must be strings.", t.fullName), t);
-	dst[platform][t.values[0].get!string] = t.values[1].get!string;
+	dst[platform][t.values[0].expect!string(t)] = t.values[1].expect!string(t);
 }
 
 private void parsePlatformEnumArray(E, Es)(Tag t, ref Es[string] dst)
 {
-	string platform;
-	if ("platform" in t.attributes)
-		platform = t.attributes["platform"][0].value.get!string;
+	string platform = t.getPlatformSuffix;
 	foreach (v; t.values) {
 		if (platform !in dst) dst[platform] = Es.init;
-		dst[platform] |= v.get!string.to!E;
+		dst[platform] |= v.expect!string(t).to!E;
 	}
 }
 
 private void enforceSDL(bool condition, lazy string message, Tag tag, string file = __FILE__, int line = __LINE__)
 {
-	import std.string : format;
 	if (!condition) {
 		throw new Exception(format("%s(%s): Error: %s", tag.location.file, tag.location.line + 1, message), file, line);
 	}