From ca6cda13a517e81a2d03e8d1d1de8d3a3d34180e Mon Sep 17 00:00:00 2001 From: ian Date: Mon, 17 Mar 2025 21:48:22 +1300 Subject: [PATCH 1/5] RequireQualifiedAccess section --- docs/fsharp-cheatsheet.md | 42 +++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/fsharp-cheatsheet.md b/docs/fsharp-cheatsheet.md index 587ae7d..b4f161b 100644 --- a/docs/fsharp-cheatsheet.md +++ b/docs/fsharp-cheatsheet.md @@ -1151,12 +1151,46 @@ module Groceries = let fruit = Banana ``` -*However*, `AutoOpen` should be used cautiously. When an `open` or `AutoOpen` is used, all declarations in the containing element -will be brought into scope. This can lead to [shadowing](https://en.wikipedia.org/wiki/Variable_shadowing); where the last -named declaration replaces all prior identically-named declarations. There is *no* error - or even a warning - in F#, when shadowing occurs. -A [coding convention (MS Learn)](https://learn.microsoft.com/en-us/dotnet/fsharp/style-guide/conventions#sort-open-statements-topologically) exists for `open` +**`AutoOpen` should be used with caution!** Functions with identical names from different modules will silently "shadow" each other, causing the most recently imported definition to be used instead of the one you might expect. The compiler will _not_ warn you when this happens. A [coding convention (MS Learn)](https://learn.microsoft.com/en-us/dotnet/fsharp/style-guide/conventions#sort-open-statements-topologically) exists for `open` statements to avoid pitfalls; `AutoOpen` would sidestep this. +### RequireQualifiedAccess Attribute + +This attribute can be used on modules, records, and discriminated unions as a precaution against unexpected shadowing when opening modules. It can also make your code more explicit and readable. + +```fsharp +[] +module Math = + let add x y = x + y + let subtract x y = x - y + +// Must use fully qualified names for module members +let sum = Math.add 5 3 // Works +let diff = subtract 5 3 // Error! + +[] +type Shape = + | Circle of float + | Rectangle of float * float + +// Must qualify union case constructors +let circle = Shape.Circle 3.0 // Works +let rectangle = Rectangle (4.0, 5.0) // Error! + +[] +type Person = + { Name: string; Age: int } + static member Create name age = { Name = name; Age = age } + +// Must qualify static methods +let person = Person.Create "John" 30 // Works +let person2 = Create "John" 30 // Error! + +// Normal record creation requires qualified access +let person3 = { Person.Name = "Alice"; Person.Age = 25 } // Works +let person4 = { Name = "Alice"; Age = 25 } // Error! +``` + ## Accessibility Modifiers F# supports `public`, `private` (limiting access to its containing `type` or `module`) and `internal` (limiting access to its containing assembly). From 907ef3cd5ba3d0d63573c4c11f6aa39bab1ca1c7 Mon Sep 17 00:00:00 2001 From: amuletofyendor Date: Mon, 17 Mar 2025 11:33:20 +1300 Subject: [PATCH 2/5] Edit to the Open chapter --- docs/fsharp-cheatsheet.md | 56 ++++++++++++++------------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/docs/fsharp-cheatsheet.md b/docs/fsharp-cheatsheet.md index b4f161b..4fc36d4 100644 --- a/docs/fsharp-cheatsheet.md +++ b/docs/fsharp-cheatsheet.md @@ -1117,7 +1117,7 @@ module MyNamespace.SubNamespace.Functions
-## Open and AutoOpen +## Open The `open` keyword can be used on `module`, `namespace`, and `type`. ```fsharp @@ -1139,24 +1139,9 @@ open type System.Text.RegularExpressions.Regex // type let isHttp url = IsMatch("^https?:", url) // Regex.IsMatch directly accessible ``` -Available to `module` declarations only, is the `AutoOpen` attribute, which alleviates the need for an `open`. - -```fsharp -[] -module Groceries = - type Fruit = - | Apple - | Banana - -let fruit = Banana -``` - -**`AutoOpen` should be used with caution!** Functions with identical names from different modules will silently "shadow" each other, causing the most recently imported definition to be used instead of the one you might expect. The compiler will _not_ warn you when this happens. A [coding convention (MS Learn)](https://learn.microsoft.com/en-us/dotnet/fsharp/style-guide/conventions#sort-open-statements-topologically) exists for `open` -statements to avoid pitfalls; `AutoOpen` would sidestep this. - ### RequireQualifiedAccess Attribute -This attribute can be used on modules, records, and discriminated unions as a precaution against unexpected shadowing when opening modules. It can also make your code more explicit and readable. +This attribute can be used on modules, records, and discriminated unions to avoid namespace collisions and unexpected "shadowing". It can also make your code more explicit and readable. ```fsharp [] @@ -1164,33 +1149,32 @@ module Math = let add x y = x + y let subtract x y = x - y -// Must use fully qualified names for module members +open Math // Error! Although you may open the containing namespace, if allowed. + +// As you can't open the module, you must use qualified names for module members. let sum = Math.add 5 3 // Works let diff = subtract 5 3 // Error! +``` -[] -type Shape = - | Circle of float - | Rectangle of float * float - -// Must qualify union case constructors -let circle = Shape.Circle 3.0 // Works -let rectangle = Rectangle (4.0, 5.0) // Error! +### AutoOpen Attribute -[] -type Person = - { Name: string; Age: int } - static member Create name age = { Name = name; Age = age } +Applicable to `module` declarations only, the `AutoOpen` attribute alleviates the need for an `open`. -// Must qualify static methods -let person = Person.Create "John" 30 // Works -let person2 = Create "John" 30 // Error! +```fsharp +[] +module Groceries = + type Fruit = + | Apple + | Banana -// Normal record creation requires qualified access -let person3 = { Person.Name = "Alice"; Person.Age = 25 } // Works -let person4 = { Name = "Alice"; Age = 25 } // Error! +let fruit = Banana ``` +#### Use `AutoOpen` and/or ignore `RequireQualifiedAccess` best practice with caution! + +Functions with identical names from different modules will silently "shadow" each other, causing the most recently imported definition to be used instead of the one you might expect. **The compiler will _not_ warn you that this has happened.** Even if your code works, you may encounter compiler errors if the `open` order changes. A [coding convention (MS Learn)](https://learn.microsoft.com/en-us/dotnet/fsharp/style-guide/conventions#sort-open-statements-topologically) exists for `open` statements to avoid pitfalls. + + ## Accessibility Modifiers F# supports `public`, `private` (limiting access to its containing `type` or `module`) and `internal` (limiting access to its containing assembly). From 50e0fd6bb4bad2ab6f8f86f203bdeea23fa68c28 Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 18 Apr 2025 11:09:02 +1200 Subject: [PATCH 3/5] Change module name to not confuse with built in Math module --- docs/fsharp-cheatsheet.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/fsharp-cheatsheet.md b/docs/fsharp-cheatsheet.md index 4fc36d4..e7ca15b 100644 --- a/docs/fsharp-cheatsheet.md +++ b/docs/fsharp-cheatsheet.md @@ -1145,14 +1145,14 @@ This attribute can be used on modules, records, and discriminated unions to avoi ```fsharp [] -module Math = +module Calcs = let add x y = x + y let subtract x y = x - y -open Math // Error! Although you may open the containing namespace, if allowed. +open Calcs // Error! Although you may open the containing namespace, if allowed. // As you can't open the module, you must use qualified names for module members. -let sum = Math.add 5 3 // Works +let sum = Calcs.add 5 3 // Works let diff = subtract 5 3 // Error! ``` From 45dc4d562926802a0d0ec7e9c3afba123c497e4c Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Sat, 5 Apr 2025 12:35:39 +0300 Subject: [PATCH 4/5] Fix example indentation (#40) --- docs/fsharp-cheatsheet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fsharp-cheatsheet.md b/docs/fsharp-cheatsheet.md index e7ca15b..33c98b6 100644 --- a/docs/fsharp-cheatsheet.md +++ b/docs/fsharp-cheatsheet.md @@ -37,7 +37,7 @@ let verbatimXml = @"" We don't even have to escape `"` with *triple-quoted strings*. ```fsharp - let tripleXml = """""" +let tripleXml = """""" ``` *Backslash strings* indent string contents by stripping leading spaces. From aef300ce976c676f6d304777575f537c2574256b Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 18 Apr 2025 11:14:40 +1200 Subject: [PATCH 5/5] rebase --- docs/fsharp-cheatsheet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fsharp-cheatsheet.md b/docs/fsharp-cheatsheet.md index 33c98b6..e7ca15b 100644 --- a/docs/fsharp-cheatsheet.md +++ b/docs/fsharp-cheatsheet.md @@ -37,7 +37,7 @@ let verbatimXml = @"" We don't even have to escape `"` with *triple-quoted strings*. ```fsharp -let tripleXml = """""" + let tripleXml = """""" ``` *Backslash strings* indent string contents by stripping leading spaces.