sumtype: add overload-set matching example

This is a useful technique, and non-obvious enough that other D
community members were surprised when I showed it to them.

The unusual formatting and code layout used here achieves the following
goals:

1. Have this example appear below "Basic usage".
2. Have the overload set appear as it would at module level.
3. Have as much of the example code unit-tested as possible.

Goal (2), in particular, rules out the use of a "wrapper" struct to
create an overload set of static methods, which is the technique that's
normally used to include an overload set in a unittest block.
This commit is contained in:
Paul Backus 2024-05-30 23:26:04 -04:00
parent e13bd7dec6
commit d1dfd094f4
4 changed files with 54 additions and 1 deletions

View file

@ -404,7 +404,7 @@ redundant_attributes_check="-std.concurrency,-std.digest.md,-std.digest.ripemd,-
style_check="+disabled"
;style_check="-etc.c.curl,-etc.c.odbc.sqlext,-etc.c.odbc.sqltypes,-etc.c.odbc.sqlucode,-etc.c.sqlite3,-etc.c.zlib,-std.algorithm.comparison,-std.algorithm.internal,-std.algorithm.iteration,-std.algorithm.mutation,-std.algorithm.sorting,-std.array,-std.base64,-std.bitmanip,-std.c.linux.linux,-std.compiler,-std.container.array,-std.conv,-std.datetime.date,-std.datetime.interval,-std.datetime.systime,-std.digest,-std.digest.murmurhash,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bucketizer,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.null_allocator,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.building_blocks.segregator,-std.experimental.allocator.common,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.allocator.mmap_allocator,-std.experimental.checkedint,-std.format,-std.functional,-std.getopt,-std.internal.digest.sha_SSSE3,-std.internal.math.errorfunction,-std.internal.math.gammafunction,-std.internal.test.dummyrange,-std.internal.unicode_tables,-std.json,-std.math,-std.meta,-std.numeric,-std.parallelism,-std.path,-std.process,-std.random,-std.range,-std.range.primitives,-std.regex,-std.regex.internal.ir,-std.regex.internal.kickstart,-std.signals,-std.socket,-std.stdio,-std.string,-std.uni,-std.uri,-std.utf,-std.uuid,-std.variant,-std.zlib"
; Checks for undocumented public declarations
undocumented_declaration_check="-etc.c.curl,-etc.c.odbc.sql,-etc.c.odbc.sqlext,-etc.c.odbc.sqltypes,-etc.c.odbc.sqlucode,-etc.c.sqlite3,-etc.c.zlib,-std.algorithm.sorting,-std.array,-std.ascii,-std.base64,-std.bitmanip,-std.c.linux.linux,-std.c.linux.socket,-std.c.osx.socket,-std.c.process,-std.compiler,-std.complex,-std.concurrency,-std.container,-std.container.array,-std.container.binaryheap,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.conv,-std.csv,-std.datetime,-std.datetime.date,-std.digest,-std.digest.hmac,-std.digest.md,-std.digest.murmurhash,-std.digest.ripemd,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bitmapped_block,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.allocator.building_blocks.quantizer,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.building_blocks.segregator,-std.experimental.allocator.building_blocks.stats_collector,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.checkedint,-std.logger.core,-std.file,-std.format,-std.functional,-std.internal.digest.sha_SSSE3,-std.internal.math.biguintcore,-std.internal.math.biguintnoasm,-std.internal.math.biguintx86,-std.internal.math.errorfunction,-std.internal.math.gammafunction,-std.internal.test.dummyrange,-std.internal.test.uda,-std.internal.windows.advapi32,-std.json,-std.math,-std.mmfile,-std.numeric,-std.outbuffer,-std.parallelism,-std.path,-std.process,-std.regex,-std.regex.internal.parser,-std.signals,-std.socket,-std.stdio,-std.string,-std.system,-std.traits,-std.uni,-std.utf,-std.variant,-std.windows.charset,-std.windows.registry,-std.windows.syserror,-std.zip,-std.zlib,-std.int128"
undocumented_declaration_check="-etc.c.curl,-etc.c.odbc.sql,-etc.c.odbc.sqlext,-etc.c.odbc.sqltypes,-etc.c.odbc.sqlucode,-etc.c.sqlite3,-etc.c.zlib,-std.algorithm.sorting,-std.array,-std.ascii,-std.base64,-std.bitmanip,-std.c.linux.linux,-std.c.linux.socket,-std.c.osx.socket,-std.c.process,-std.compiler,-std.complex,-std.concurrency,-std.container,-std.container.array,-std.container.binaryheap,-std.container.dlist,-std.container.rbtree,-std.container.slist,-std.conv,-std.csv,-std.datetime,-std.datetime.date,-std.digest,-std.digest.hmac,-std.digest.md,-std.digest.murmurhash,-std.digest.ripemd,-std.digest.sha,-std.encoding,-std.exception,-std.experimental.allocator,-std.experimental.allocator.building_blocks.affix_allocator,-std.experimental.allocator.building_blocks.allocator_list,-std.experimental.allocator.building_blocks.bitmapped_block,-std.experimental.allocator.building_blocks.fallback_allocator,-std.experimental.allocator.building_blocks.free_list,-std.experimental.allocator.building_blocks.free_tree,-std.experimental.allocator.building_blocks.kernighan_ritchie,-std.experimental.allocator.building_blocks.quantizer,-std.experimental.allocator.building_blocks.region,-std.experimental.allocator.building_blocks.segregator,-std.experimental.allocator.building_blocks.stats_collector,-std.experimental.allocator.gc_allocator,-std.experimental.allocator.mallocator,-std.experimental.checkedint,-std.logger.core,-std.file,-std.format,-std.functional,-std.internal.digest.sha_SSSE3,-std.internal.math.biguintcore,-std.internal.math.biguintnoasm,-std.internal.math.biguintx86,-std.internal.math.errorfunction,-std.internal.math.gammafunction,-std.internal.test.dummyrange,-std.internal.test.sumtype_example_overloads,-std.internal.test.uda,-std.internal.windows.advapi32,-std.json,-std.math,-std.mmfile,-std.numeric,-std.outbuffer,-std.parallelism,-std.path,-std.process,-std.regex,-std.regex.internal.parser,-std.signals,-std.socket,-std.stdio,-std.string,-std.system,-std.traits,-std.uni,-std.utf,-std.variant,-std.windows.charset,-std.windows.registry,-std.windows.syserror,-std.zip,-std.zlib,-std.int128"
; Checks for unused labels
unused_label_check="-std.conv,-std.internal.math.biguintx86,-std.regex.internal.thompson,-std.signals,-std.uni"
; Checks for unused function parameters

View file

@ -272,6 +272,7 @@ EXTRA_MODULES_INTERNAL := $(addprefix std/, \
$(addprefix math/, biguintcore biguintnoasm biguintx86 \
errorfunction gammafunction ) \
scopebuffer test/dummyrange test/range \
test/sumtype_example_overloads \
$(addprefix unicode_, comp decomp grapheme norm tables) \
windows/advapi32 \
) \

View file

@ -0,0 +1,17 @@
/++
For testing only.
Overload set used in std.sumtype example. Needs its own internal module so that
it can be available for `make publictests` without polluting the public API.
+/
module std.internal.test.sumtype_example_overloads;
import std.sumtype;
@safe
{
string handle(int) { return "got an int"; }
string handle(string) { return "got a string"; }
string handle(double) { return "got a double"; }
alias handle = match!handle;
}

View file

@ -77,6 +77,37 @@ version (D_BetterC) {} else
assert(!isFahrenheit(t3));
}
/** $(DIVID matching-with-an-overload-set, $(H3 Matching with an overload set))
*
* Instead of writing `match` handlers inline as lambdas, you can write them as
* overloads of a function. An `alias` can be used to create an additional
* overload for the `SumType` itself.
*
* For example, with this overload set:
*
* ---
* string handle(int n) { return "got an int"; }
* string handle(string s) { return "got a string"; }
* string handle(double d) { return "got a double"; }
* alias handle = match!handle;
* ---
*
* Usage would look like this:
*/
version (D_BetterC) {} else
@safe unittest
{
alias ExampleSumType = SumType!(int, string, double);
ExampleSumType a = 123;
ExampleSumType b = "hello";
ExampleSumType c = 3.14;
assert(a.handle == "got an int");
assert(b.handle == "got a string");
assert(c.handle == "got a double");
}
/** $(DIVID arithmetic-expression-evaluator, $(H3 Arithmetic expression evaluator))
*
* This example makes use of the special placeholder type `This` to define a
@ -182,6 +213,10 @@ version (D_BetterC) {} else
assert(pprint(*myExpr) == "(a + (2 * b))");
}
// For the "Matching with an overload set" example above
// Needs public import to work with `make publictests`
version (unittest) public import std.internal.test.sumtype_example_overloads;
import std.format.spec : FormatSpec, singleSpec;
import std.meta : AliasSeq, Filter, IndexOf = staticIndexOf, Map = staticMap;
import std.meta : NoDuplicates;