From 1a407720a9eca7f11ae396fd444c0134b7ee874fa685e63e2808800286117f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Bj=C3=B6rnstam?= Date: Thu, 22 Oct 2026 11:27:57 +0200 Subject: [PATCH] Unions: two may have a case of one name; which name wins, as it is MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A case of one name in two unions is chosen by type, then by scope, and the manual says so in place of "cannot both have a case called Red". Writing programs said two imports offering one name, a type among them, resolved to the later import; a use is refused until the program says which, except for cases of different unions. 🤖 Generated with [ECA](https://eca.dev) Co-Authored-By: eca-agent --- _01.Language-basics.sz | 24 +++++++++++++++++++++++- _02_.Writing-programs.sz | 4 ++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/_01.Language-basics.sz b/_01.Language-basics.sz index 2be4d4b..31942df 100644 --- a/_01.Language-basics.sz +++ b/_01.Language-basics.sz @@ -453,10 +453,32 @@ Unions can be generic. The type variables go after the type name, just like for You never have to write these two, since the prelude already has them under the names @code{(Option %a)}, with the cases @code{None} and @code{Some}, and @code{(Result %e %a)}, with the cases @code{Err} and @code{Ok}. They are ordinary unions, and they get a section of their own further down. +Two unions can each have a case of the same name, in one module or in two. Which one a use of the name means is decided by its type: the type wanted where it is written, the type of the value a @code{match} takes apart, or the number of values it is given, when only one of the cases takes that many. + +@codeblock[#:lang "bjolang"]|{ +(type (: Light (Union Red Amber Green))) +(type (: Colour (Union Red Blue (: Grey int)))) + +(: stop (-> Light)) +(defun (stop) Red) ;; a Light, because stop returns one + +(: colour-name (-> Colour string)) +(defun (colour-name c) + (match c + (Red "red") ;; Colour's, because c is a Colour + (Blue "blue") + ((Grey n) "grey"))) + +(list Red Amber) ;; Lights, because Amber is +(println (: Red Colour)) ;; written out +}| + +When nothing in the definition says which union is meant, a case your module declares wins over an imported one, and an imported one over the standard library's. If that does not decide it either, as for two @code{Red}s of your own in @code{(println Red)}, the compiler asks you to write the type: @code{(: Red Light)}. + A few things to know about unions: @read-list{ -- Case names share a namespace with everything else in the module, so two unions in the same module cannot both have a case called @code{Red}. +- A case cannot have the name of a record or a newtype of the same module, because those are built by their names too. - A union case cannot have a @code{#:mutable} field. - @code{println} on a value of your own type prints it as you would build it, for example @code{(Card (rank Queen) (suit Hearts))} or @code{(Pip 7)}. The compiler writes this for each type you declare. To print a type another way, implement @code{Displayed} (see the traits section). } diff --git a/_02_.Writing-programs.sz b/_02_.Writing-programs.sz index f4d5701..10ed9a8 100644 --- a/_02_.Writing-programs.sz +++ b/_02_.Writing-programs.sz @@ -195,7 +195,7 @@ Note that @code{only} and @code{except} apply exclusively to functions and macro 0) }| -Types are strictly scoped to the module that declared them. If two imported libraries each declare their own @code{Card} type, they remain distinct types; the unqualified name @code{Card} simply resolves to whichever import came last. To disambiguate between them, use @code{prefix-types}: +Types are strictly scoped to the module that declared them. If two imported libraries each declare their own @code{Card} type, they remain distinct types, and writing the unqualified name @code{Card} is an error until you say which one you mean. To tell them apart, use @code{prefix-types}: @codeblock[#:lang "bjolang"]|{ (import (prefix-types "poker.bjo" "P/") @@ -210,7 +210,7 @@ When multiple definitions share the same identifier, name resolution follows thr @read-list{ - Local definitions take precedence over imported names. -- For collisions between two imports, the later import shadows the earlier one. This shadowing behavior is how modules can freely override names from the prelude, which is loaded as the very first import. +- A name the prelude offers is overridden by any other import's, which is how a module can freely replace a prelude name. Between two other imports that offer one name as two different things, importing both is fine, but writing the name is an error until you say which one you mean, with @code{only}, @code{except} or a prefix. Two cases of different unions are the exception: which one a use means is decided by its type, as described for unions in Part 1. - Explicit renames or aliases created by import modifiers or @code{:alias} must be unique. If an alias collides with an existing name, the compiler raises an error to prevent ambiguity. }