From d1dfd094f4dffd15e215e44a4a83b19c7e531d10 Mon Sep 17 00:00:00 2001 From: Paul Backus Date: Thu, 30 May 2024 23:26:04 -0400 Subject: [PATCH] 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. --- .dscanner.ini | 2 +- Makefile | 1 + std/internal/test/sumtype_example_overloads.d | 17 +++++++++ std/sumtype.d | 35 +++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 std/internal/test/sumtype_example_overloads.d diff --git a/.dscanner.ini b/.dscanner.ini index 5f9364447..03cf7e6ee 100644 --- a/.dscanner.ini +++ b/.dscanner.ini @@ -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 diff --git a/Makefile b/Makefile index 89704a431..76a6130d9 100644 --- a/Makefile +++ b/Makefile @@ -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 \ ) \ diff --git a/std/internal/test/sumtype_example_overloads.d b/std/internal/test/sumtype_example_overloads.d new file mode 100644 index 000000000..235659dc8 --- /dev/null +++ b/std/internal/test/sumtype_example_overloads.d @@ -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; +} diff --git a/std/sumtype.d b/std/sumtype.d index 916f984c3..c510e1599 100644 --- a/std/sumtype.d +++ b/std/sumtype.d @@ -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;