├── .config └── dotnet-tools.json ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .editorconfig ├── .fantomasignore ├── .git-blame-ignore-revs ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── copilot-instructions.md └── workflows │ ├── build.yml │ ├── fsdocs-gh-pages.yml │ ├── publish_ci.yml │ └── publish_release.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── Directory.Build.props ├── Directory.Build.targets ├── Directory.Packages.props ├── FSharp.Collections.Immutable.slnf ├── FSharp.Collections.Immutable.slnx ├── LICENSE ├── README.md ├── build.cmd ├── build.sh ├── build ├── Changelog.fs ├── FsDocs.fs ├── Properties │ └── launchSettings.json ├── build.fs └── build.fsproj ├── docsSrc ├── Explanations │ └── Background.md ├── How_Tos │ ├── Doing_A_Thing.md │ └── Doing_Another_Thing.md ├── Tutorials │ └── Getting_Started.md ├── _menu-item_template.html ├── _menu_template.html ├── _template.html ├── content │ ├── fsdocs-custom.css │ ├── fsdocs-dark.css │ ├── fsdocs-light.css │ ├── fsdocs-main.css │ ├── logo.pdn │ ├── logo.png │ ├── logo.svg │ ├── navbar-fixed-left.css │ └── theme-toggle.js └── index.md ├── global.json ├── src └── FSharp.Collections.Immutable │ ├── FSharp.Collections.Immutable.fsproj │ ├── FlatList.fs │ ├── ImmutableCollectionUtil.fs │ ├── ImmutableList.fs │ ├── IndexedSeq.fs │ ├── Maps.fs │ ├── Queue.fs │ ├── Seq.fs │ ├── Sets.fs │ └── Stack.fs └── tests ├── Directory.Build.props └── FSharp.Collections.Immutable.Tests ├── Attributes.fs ├── FSharp.Collections.Immutable.Tests.fsproj ├── FlatList.fs ├── ImmutableList.fs ├── IndexedSeq.fs ├── Maps.fs ├── Queue.fs ├── Sets.fs └── Stack.fs /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-reportgenerator-globaltool": { 6 | "version": "5.4.7", 7 | "commands": [ 8 | "reportgenerator" 9 | ], 10 | "rollForward": false 11 | }, 12 | "fsharp-analyzers": { 13 | "version": "0.31.0", 14 | "commands": [ 15 | "fsharp-analyzers" 16 | ], 17 | "rollForward": false 18 | }, 19 | "fantomas": { 20 | "version": "7.0.2", 21 | "commands": [ 22 | "fantomas" 23 | ], 24 | "rollForward": false 25 | }, 26 | "fsdocs-tool": { 27 | "version": "20.0.1", 28 | "commands": [ 29 | "fsdocs" 30 | ], 31 | "rollForward": false 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] Debian version (use bullseye on local arm64/Apple Silicon): bookworm, bullseye, buster 2 | ARG VARIANT="bookworm" 3 | FROM buildpack-deps:${VARIANT}-curl 4 | 5 | 6 | ENV \ 7 | # Enable detection of running in a container 8 | DOTNET_RUNNING_IN_CONTAINER=true \ 9 | DOTNET_ROOT=/usr/share/dotnet/ \ 10 | DOTNET_NOLOGO=true \ 11 | DOTNET_CLI_TELEMETRY_OPTOUT=false\ 12 | DOTNET_USE_POLLING_FILE_WATCHER=true 13 | 14 | 15 | # [Optional] Uncomment this section to install additional OS packages. 16 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 17 | # && apt-get -y install --no-install-recommends 18 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dotnet", 3 | // Set the build context one level higher so we can grab metadata like global.json 4 | "context": "..", 5 | "dockerFile": "Dockerfile", 6 | "forwardPorts": [ 7 | 0 8 | ], 9 | "features": { 10 | // https://github.com/devcontainers/features/blob/main/src/common-utils/README.md 11 | "ghcr.io/devcontainers/features/common-utils:2": { 12 | "installZsh": true, 13 | "installOhMyZshConfig": true, 14 | "configureZshAsDefaultShell": true, 15 | "username": "vscode", 16 | "userUid": "1000", 17 | "userGid": "1000", 18 | "upgradePackages": true 19 | }, 20 | // https://github.com/devcontainers/features/blob/main/src/github-cli/README.md 21 | "ghcr.io/devcontainers/features/github-cli:1": {}, 22 | // https://github.com/devcontainers/features/blob/main/src/dotnet/README.md 23 | "ghcr.io/devcontainers/features/dotnet:2": { 24 | "version": "9.0", 25 | "additionalVersions": "8.0" 26 | } 27 | }, 28 | "overrideFeatureInstallOrder": [ 29 | "ghcr.io/devcontainers/features/common-utils", 30 | "ghcr.io/devcontainers/features/github-cli", 31 | "ghcr.io/devcontainers/features/dotnet" 32 | ], 33 | "customizations": { 34 | "vscode": { 35 | // Add the IDs of extensions you want installed when the container is created. 36 | "extensions": [ 37 | "ms-dotnettools.csharp", 38 | "Ionide.Ionide-fsharp", 39 | "tintoy.msbuild-project-tools", 40 | "ionide.ionide-paket", 41 | "usernamehw.errorlens", 42 | "alefragnani.Bookmarks", 43 | "oderwat.indent-rainbow", 44 | "vscode-icons-team.vscode-icons", 45 | "EditorConfig.EditorConfig", 46 | "ms-azuretools.vscode-docker", 47 | "GitHub.vscode-pull-request-github", 48 | "github.vscode-github-actions" 49 | ], 50 | "settings": { 51 | "terminal.integrated.defaultProfile.linux": "zsh", 52 | "csharp.suppressDotnetInstallWarning": true 53 | } 54 | } 55 | }, 56 | "remoteUser": "vscode", 57 | "containerUser": "vscode", 58 | "containerEnv": { 59 | // Expose the local environment variable to the container 60 | // They are used for releasing and publishing from the container 61 | "GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}" 62 | }, 63 | "postAttachCommand": { 64 | "restore": "dotnet tool restore && dotnet restore" 65 | }, 66 | "waitFor": "updateContentCommand" 67 | } 68 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: 2 | http://EditorConfig.org 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | ############################### 8 | # Core EditorConfig Options # 9 | ############################### 10 | # All files 11 | [*] # Do not apply to all files not to break something 12 | guidelines = 120 dashed, 130 13 | # Either crlf | lf, default is system-dependent (when not specified at all) 14 | # end_of_line=crlf 15 | # Remove whitespace at the end of any line 16 | 17 | # Visual Studio Solution Files 18 | [*.sln] 19 | indent_style = tab 20 | 21 | # Code files 22 | [*.{cs,csx,fs,fsi,fsx}] 23 | trim_trailing_whitespace = true 24 | insert_final_newline = true 25 | indent_style = space # default=space 26 | indent_size = 4 # default=4 27 | charset = utf-8 28 | 29 | # Project files and app specific XML files 30 | [*.{csproj,fsproj,shproj,sfproj,projitems,props,xaml,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] 31 | trim_trailing_whitespace = true 32 | insert_final_newline = true 33 | indent_style = space 34 | indent_size = 2 35 | 36 | # XML configuration files 37 | [{app.config,nuget.config,packages.config,web.config}] 38 | trim_trailing_whitespace = true 39 | insert_final_newline = true 40 | indent_style = space 41 | indent_size = 2 42 | 43 | # XML files 44 | [*.xml] 45 | trim_trailing_whitespace = false # do not trim as it affects CData 46 | insert_final_newline = true 47 | indent_style = space 48 | indent_size = 2 49 | 50 | # JSON and YAML files 51 | [*.{json,yml,yaml}] 52 | trim_trailing_whitespace = true 53 | insert_final_newline = true 54 | indent_style = space 55 | indent_size = 2 56 | 57 | # Proto files 58 | [*.proto] 59 | trim_trailing_whitespace = true 60 | insert_final_newline = true 61 | indent_style = space 62 | indent_size = 4 63 | 64 | # Markdown Files 65 | [*.{md,mdx}] 66 | trim_trailing_whitespace = false 67 | 68 | # Bash Files 69 | [*.{sh}] 70 | end_of_line = lf 71 | 72 | # Batch Files 73 | [*.{cmd,bat}] 74 | end_of_line = crlf 75 | 76 | # Powershell Files 77 | [*.{ps1, psm1}] 78 | end_of_line = crlf 79 | 80 | # Paket files 81 | [paket.*] 82 | trim_trailing_whitespace = true 83 | indent_size = 2 84 | 85 | [*.paket.references] 86 | trim_trailing_whitespace = true 87 | indent_size = 2 88 | 89 | ############################### 90 | # F# Coding Conventions # 91 | ############################### 92 | # https://fsprojects.github.io/fantomas/docs/end-users/Configuration.html 93 | 94 | # filetypes that need to be formatted by Fantomas: 95 | [*.{fs,fsi,fsx}] 96 | 97 | # files to be ignored for Fantomas may go into this file, if present: 98 | # .fantomasignore 99 | 100 | # indentation size, default=4 101 | indent_size=4 102 | 103 | # line length before it gets broken down into multiple lines 104 | # default 120 105 | max_line_length=130 106 | 107 | # Either crlf | lf, default is system-dependent (when not specified at all) 108 | # end_of_line=crlf 109 | 110 | # Whether end-of-file has a newline, default=true 111 | insert_final_newline=true 112 | 113 | # false: someLineOfCode 114 | # true: someLineOfCode; 115 | # default false 116 | fsharp_semicolon_at_end_of_line=false 117 | 118 | # false: f(1,2) 119 | # true: f(1, 2) 120 | # default true 121 | fsharp_space_before_parameter=true 122 | 123 | # false: Option.map(fun x -> x) 124 | # true: Option.map (fun x -> x) 125 | # default true 126 | fsharp_space_before_lowercase_invocation=true 127 | 128 | # false: x.ToString() 129 | # true: x.ToString () 130 | # default false 131 | fsharp_space_before_uppercase_invocation=true 132 | 133 | # false: new Ship(withBeans) 134 | # true: new Ship (withBeans) 135 | # default false 136 | fsharp_space_before_class_constructor=true 137 | 138 | # false: __.member Foo(x) = x 139 | # true: __.member Foo (x) = x 140 | # default false 141 | fsharp_space_before_member=true 142 | 143 | # false: type Point = { x: int; y: int } 144 | # true: type Point = { x : int; y : int } 145 | # default false 146 | fsharp_space_before_colon=true 147 | 148 | # false: (a,b,c) 149 | # true: (a, b, c) 150 | # default true 151 | fsharp_space_after_comma=true 152 | 153 | # false: [a; b; 42] 154 | # true: [a ; b ; 42] 155 | # default false 156 | fsharp_space_before_semicolon=false 157 | 158 | # false: [a;b;42] 159 | # true: [a; b; 42] 160 | # default true 161 | fsharp_space_after_semicolon=true 162 | 163 | # false: let a = [1;2;3] 164 | # true: let a = [ 1;2;3 ] 165 | # default true 166 | fsharp_space_around_delimiter=true 167 | 168 | # breaks an if-then-else in smaller parts if it is on one line 169 | # default 40 170 | fsharp_max_if_then_else_short_width=60 171 | 172 | # breaks an infix operator expression if it is on one line 173 | # infix: a + b + c 174 | # default 50 175 | fsharp_max_infix_operator_expression=60 176 | 177 | # breaks a single-line record declaration 178 | # i.e. if this gets too wide: { X = 10; Y = 12 } 179 | # default 40 180 | fsharp_max_record_width=80 181 | 182 | # breaks a record into one item per line if items exceed this number 183 | # i.e. if set to 1, this will be on three lines: { X = 10; Y = 12 } 184 | # requires fsharp_record_multiline_formatter=number_of_items to take effect 185 | # default 1 186 | fsharp_max_record_number_of_items=1 187 | 188 | # whether to use line-length (by counting chars) or items (by counting fields) 189 | # for the record settings above 190 | # either number_of_items or character_width 191 | # default character_width 192 | fsharp_record_multiline_formatter=character_width 193 | 194 | # breaks a single line array or list if it exceeds this size 195 | # default 40 196 | fsharp_max_array_or_list_width=100 197 | 198 | # breaks an array or list into one item per line if items exceeds this number 199 | # i.e. if set to 1, this will be shown on three lines [1; 2; 3] 200 | # requires fsharp_array_or_list_multiline_formatter=number_of_items to take effect 201 | # default 1 202 | fsharp_max_array_or_list_number_of_items=1 203 | 204 | # whether to use line-length (by counting chars) or items (by counting fields) 205 | # for the list and array settings above 206 | # either number_of_items or character_width 207 | # default character_width 208 | fsharp_array_or_list_multiline_formatter=character_width 209 | 210 | # maximum with of a value binding, does not include keyword "let" 211 | # default 80 212 | fsharp_max_value_binding_width=100 213 | 214 | # maximum width for function and member binding (rh-side) 215 | # default 40 216 | fsharp_max_function_binding_width=80 217 | 218 | # maximum width for expressions like X.DoY().GetZ(10).Help() 219 | # default 50 220 | fsharp_max_dot_get_expression_width=80 221 | 222 | # whether open/close brackets go on the same column 223 | # cramped: type Range = 224 | # { From: float 225 | # To: float } 226 | # aligned: type Range = 227 | # { 228 | # From: float 229 | # To: float 230 | # } 231 | # stroustrup: type Range = { 232 | # From: float 233 | # To: float 234 | # } 235 | # default cramped 236 | fsharp_multiline_bracket_style=stroustrup 237 | 238 | # whether to move the beginning of compuitation expression to the new line 239 | # true: let x = 240 | # computation { 241 | # ... 242 | # } 243 | # false: let x = computation { 244 | # .. 245 | # } 246 | fsharp_newline_before_multiline_computation_expression=false 247 | 248 | # whether a newline should be placed before members 249 | # false: type Range = 250 | # { From: float } 251 | # member this.Length = this.To - this.From 252 | # true: type Range = 253 | # { From: float } 254 | # 255 | # member this.Length = this.To - this.From 256 | # default false 257 | fsharp_newline_between_type_definition_and_members=true 258 | 259 | # if a function sign exceeds max_line_length, then: 260 | # false: do not place the equal-sign on a single line 261 | # true: place the equal-sign on a single line 262 | # default false 263 | fsharp_align_function_signature_to_indentation=false 264 | 265 | # see docs: https://github.com/fsprojects/fantomas/blob/master/docs/Documentation.md#fsharp_alternative_long_member_definitions 266 | # default false 267 | fsharp_alternative_long_member_definitions=true 268 | 269 | # places closing paren in lambda on a newline in multiline lambdas 270 | # default false 271 | fsharp_multi_line_lambda_closing_newline=true 272 | 273 | # allows the 'else'-branch to be aligned at same level as 'else' if the ret type allows it 274 | # false: match x with 275 | # | null -> () 276 | # | _ -> () 277 | # true: match x with 278 | # | null -> () 279 | # | _ -> 280 | # () 281 | # default false 282 | fsharp_keep_indent_in_branch=true 283 | 284 | # whether a bar is placed before DU 285 | # false: type MyDU = Short of int 286 | # true: type MyDU = | Short of int 287 | # default false 288 | fsharp_bar_before_discriminated_union_declaration=false 289 | 290 | # multiline, nested expressions must be surrounded by blank lines 291 | # default true 292 | fsharp_blank_lines_around_nested_multiline_expressions=false 293 | 294 | # set maximal number of consecutive blank lines to keep from original source 295 | # it doesn't change number of new blank lines generated by Fantomas 296 | fsharp_keep_max_number_of_blank_lines=2 297 | -------------------------------------------------------------------------------- /.fantomasignore: -------------------------------------------------------------------------------- 1 | # Ignore AssemblyInfo files 2 | AssemblyInfo.fs 3 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # This file contains a list of git hashes of revisions to be ignored by git 2 | # These revisions are considered "unimportant" in 3 | # that they are unlikely to be what you are interested in when blaming. 4 | # Like formatting with Fantomas 5 | # https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view 6 | # Add formatting commits here 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp text=auto eol=lf 6 | *.vb diff=csharp text=auto eol=lf 7 | *.fs diff=csharp text=auto eol=lf 8 | *.fsi diff=csharp text=auto eol=lf 9 | *.fsx diff=csharp text=auto eol=lf 10 | *.sln text eol=crlf merge=union 11 | *.csproj merge=union 12 | *.vbproj merge=union 13 | *.fsproj merge=union 14 | *.dbproj merge=union 15 | *.sh text eol=lf 16 | 17 | # Standard to msysgit 18 | *.doc diff=astextplain 19 | *.DOC diff=astextplain 20 | *.docx diff=astextplain 21 | *.DOCX diff=astextplain 22 | *.dot diff=astextplain 23 | *.DOT diff=astextplain 24 | *.pdf diff=astextplain 25 | *.PDF diff=astextplain 26 | *.rtf diff=astextplain 27 | *.RTF diff=astextplain 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Please insert a description of your problem or question. 4 | 5 | ## Error messages, screenshots 6 | 7 | Please add any error logs or screenshots if available. 8 | 9 | ## Failing test, failing GitHub repo, or reproduction steps 10 | 11 | Please add either a failing test, a GitHub repo of the problem or detailed reproduction steps. 12 | 13 | ## Expected Behavior 14 | 15 | Please define what you would expect the behavior to be like. 16 | 17 | ## Known workarounds 18 | 19 | Please provide a description of any known workarounds. 20 | 21 | ## Other information 22 | 23 | * Operating System: 24 | - [ ] windows [insert version here] 25 | - [ ] macOs [insert version] 26 | - [ ] linux [insert flavor/version here] 27 | * Platform 28 | - [ ] dotnet core 29 | - [ ] dotnet full 30 | - [ ] mono 31 | * Branch or release version: 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Proposed Changes 2 | 3 | Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. 4 | 5 | ## Types of changes 6 | 7 | What types of changes does your code introduce to FSharp.Azure.Cosmos? 8 | _Put an `x` in the boxes that apply_ 9 | 10 | - [ ] Bugfix (non-breaking change which fixes an issue) 11 | - [ ] New feature (non-breaking change which adds functionality) 12 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 13 | 14 | 15 | ## Checklist 16 | 17 | _Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code._ 18 | 19 | - [ ] Build and tests pass locally 20 | - [ ] I have added tests that prove my fix is effective or that my feature works (if appropriate) 21 | - [ ] I have added necessary documentation (if appropriate) 22 | 23 | ## Further comments 24 | 25 | If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... 26 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | We prefer the latest F# 9 features over the old syntax 2 | 3 | Prefer `voption` over `option` 4 | 5 | Prefer `task` CE over `async` CE 6 | 7 | This is how you define a non-default F# class constructor: 8 | ```fsharp 9 | type DerivedClass = 10 | inherit BaseClass 11 | 12 | new (``arguments here``) as ``created object`` 13 | = 14 | // create any objects used in the base class constructor 15 | let fieldValue = "" 16 | { 17 | inherit 18 | BaseClass (``arguments here``) 19 | } 20 | then 21 | ``created object``.otherField <- fieldValue 22 | 23 | [] 24 | val mutable otherField : FieldType 25 | ``` 26 | 27 | Always prefer F# class initializers over property assignment! **You absolutely must use F# class initializers instead of property assignment**! 28 | 29 | Class declaration: 30 | ``` F# 31 | type MyClass (someConstructorParam : string) = 32 | member ReadOnlyProperty = someConstructorParam 33 | 34 | member val MutableProperty1 = "" with get, set 35 | member val MutableProperty2 = "" with get, set 36 | ``` 37 | 38 | Wrong: 39 | ``` F# 40 | let myClass = MyClass("some value") 41 | myClass.MutableProperty1 <- "new value" 42 | myClass.MutableProperty2 <- "new value" 43 | ``` 44 | 45 | Right: 46 | ``` F# 47 | let myClass = 48 | MyClass( 49 | // constructor parameters go first without names 50 | "some value", 51 | // then mutable properties go next with names 52 | MutableProperty1 = "new value", 53 | MutableProperty2 = 54 | // operations must be placed into parentheses 55 | (5 |> string) 56 | ) 57 | ``` 58 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build main 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | configuration: [Debug, Release] 16 | os: [ubuntu-latest, windows-latest, macOS-latest] 17 | runs-on: ${{ matrix.os }} 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Setup necessary dotnet SDKs 22 | uses: actions/setup-dotnet@v3 23 | with: 24 | global-json-file: global.json 25 | dotnet-version: | 26 | 9.x 27 | 8.x 28 | 29 | - name: Build via Bash 30 | if: runner.os != 'Windows' 31 | run: | 32 | chmod +x ./build.sh 33 | ./build.sh 34 | env: 35 | CI: true 36 | CONFIGURATION: ${{ matrix.configuration }} 37 | ENABLE_COVERAGE: true 38 | - name: Build via Windows 39 | if: runner.os == 'Windows' 40 | run: ./build.cmd 41 | env: 42 | CI: true 43 | CONFIGURATION: ${{ matrix.configuration }} 44 | ENABLE_COVERAGE: true 45 | # Builds the project in a dev container 46 | build-devcontainer: 47 | runs-on: ubuntu-latest 48 | steps: 49 | 50 | - uses: actions/checkout@v3 51 | 52 | - name: Build and run dev container task 53 | uses: devcontainers/ci@v0.3 54 | with: 55 | runCmd: | 56 | chmod +x ./build.sh 57 | ./build.sh 58 | -------------------------------------------------------------------------------- /.github/workflows/fsdocs-gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Docs 2 | 3 | on: 4 | # Runs on pushes targeting the default branch 5 | push: 6 | branches: ["main"] 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 12 | permissions: 13 | contents: read 14 | pages: write 15 | id-token: write 16 | 17 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 18 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: false 22 | 23 | jobs: 24 | # Build job 25 | build: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | 31 | - name: Setup Pages 32 | uses: actions/configure-pages@v4 33 | 34 | - name: Setup necessary dotnet SDKs 35 | uses: actions/setup-dotnet@v4 36 | with: 37 | global-json-file: global.json 38 | dotnet-version: | 39 | 9.x 40 | 41 | - name: Build Docs 42 | run: | 43 | chmod +x ./build.sh 44 | ./build.sh builddocs 45 | 46 | - name: Upload artifact 47 | uses: actions/upload-pages-artifact@v3 48 | with: 49 | path: docs/ 50 | 51 | # Deployment job 52 | deploy: 53 | environment: 54 | name: github-pages 55 | url: ${{ steps.deployment.outputs.page_url }} 56 | runs-on: ubuntu-latest 57 | needs: build 58 | steps: 59 | - name: Deploy to GitHub Pages 60 | id: deployment 61 | uses: actions/deploy-pages@v4 62 | -------------------------------------------------------------------------------- /.github/workflows/publish_ci.yml: -------------------------------------------------------------------------------- 1 | name: Publish to GitHub 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | env: 9 | CONFIGURATION: Release 10 | 11 | jobs: 12 | build: 13 | # Sets permissions of the GITHUB_TOKEN to allow release creating 14 | permissions: 15 | packages: write 16 | environment: 17 | name: nuget 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - name: Setup necessary dotnet SDKs 24 | uses: actions/setup-dotnet@v3 25 | with: 26 | global-json-file: global.json 27 | dotnet-version: | 28 | 9.x 29 | 8.x 30 | 31 | - name: Add the GitHub source 32 | run: dotnet nuget add source --name "github.com" "https://nuget.pkg.github.com/fsprojects/index.json" 33 | 34 | - name: Ensure NuGet package source mapping 35 | shell: pwsh 36 | run: | 37 | $nugetConfigPath = "$HOME/.nuget/NuGet/NuGet.Config" 38 | [xml]$nugetConfig = Get-Content $nugetConfigPath 39 | 40 | $packageSourceMapping = $nugetConfig.configuration.packageSourceMapping 41 | if ($packageSourceMapping -ne $null) { 42 | $packageSourceMapping.RemoveAll() 43 | } else { 44 | $packageSourceMapping = $nugetConfig.CreateElement("packageSourceMapping") 45 | $nugetConfig.configuration.AppendChild($packageSourceMapping) 46 | } 47 | 48 | $nugetSource = $nugetConfig.CreateElement("packageSource") 49 | $nugetSource.SetAttribute("key", "nuget.org") 50 | $nugetPattern = $nugetConfig.CreateElement("package") 51 | $nugetPattern.SetAttribute("pattern", "*") 52 | $nugetSource.AppendChild($nugetPattern) 53 | $packageSourceMapping.AppendChild($nugetSource) 54 | 55 | $nugetConfig.Save($nugetConfigPath) 56 | 57 | - name: Publish to GitHub 58 | env: 59 | NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} 60 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 61 | FAKE_DETAILED_ERRORS: true 62 | ENABLE_COVERAGE: false # AltCover doesn't work with Release builds, reports lower coverage than actual 63 | run: | 64 | chmod +x ./build.sh 65 | ./build.sh "PublishToGitHub" 66 | -------------------------------------------------------------------------------- /.github/workflows/publish_release.yml: -------------------------------------------------------------------------------- 1 | name: Publish to NuGet 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'releases/*' 7 | 8 | env: 9 | CONFIGURATION: Release 10 | jobs: 11 | build: 12 | # Sets permissions of the GITHUB_TOKEN to allow release creating 13 | permissions: 14 | contents: write 15 | environment: 16 | name: nuget 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Setup necessary dotnet SDKs 21 | uses: actions/setup-dotnet@v3 22 | with: 23 | global-json-file: global.json 24 | dotnet-version: | 25 | 9.x 26 | 8.x 27 | 28 | - name: Publish to NuGet 29 | env: 30 | NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | FAKE_DETAILED_ERRORS: true 33 | ENABLE_COVERAGE: false # AltCover doesn't work with Release builds, reports lower coverage than actual 34 | run: | 35 | chmod +x ./build.sh 36 | ./build.sh Publish 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # JustCode is a .NET coding add-in 136 | .JustCode 137 | 138 | # TeamCity is a build add-in 139 | _TeamCity* 140 | 141 | # DotCover is a Code Coverage Tool 142 | *.dotCover 143 | 144 | # AxoCover is a Code Coverage Tool 145 | .axoCover/* 146 | !.axoCover/settings.json 147 | 148 | # Coverlet is a free, cross platform Code Coverage Tool 149 | coverage*.json 150 | coverage*.xml 151 | coverage*.info 152 | 153 | # Visual Studio code coverage results 154 | *.coverage 155 | *.coveragexml 156 | 157 | # NCrunch 158 | _NCrunch_* 159 | .*crunch*.local.xml 160 | nCrunchTemp_* 161 | 162 | # MightyMoose 163 | *.mm.* 164 | AutoTest.Net/ 165 | 166 | # Web workbench (sass) 167 | .sass-cache/ 168 | 169 | # Installshield output folder 170 | [Ee]xpress/ 171 | 172 | # DocProject is a documentation generator add-in 173 | DocProject/buildhelp/ 174 | DocProject/Help/*.HxT 175 | DocProject/Help/*.HxC 176 | DocProject/Help/*.hhc 177 | DocProject/Help/*.hhk 178 | DocProject/Help/*.hhp 179 | DocProject/Help/Html2 180 | DocProject/Help/html 181 | 182 | # Click-Once directory 183 | publish/ 184 | 185 | # Publish Web Output 186 | *.[Pp]ublish.xml 187 | *.azurePubxml 188 | # Note: Comment the next line if you want to checkin your web deploy settings, 189 | # but database connection strings (with potential passwords) will be unencrypted 190 | *.pubxml 191 | *.publishproj 192 | 193 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 194 | # checkin your Azure Web App publish settings, but sensitive information contained 195 | # in these scripts will be unencrypted 196 | PublishScripts/ 197 | 198 | # NuGet Packages 199 | *.nupkg 200 | # NuGet Symbol Packages 201 | *.snupkg 202 | # The packages folder can be ignored because of Package Restore 203 | **/[Pp]ackages/* 204 | # except build/, which is used as an MSBuild target. 205 | !**/[Pp]ackages/build/ 206 | # Uncomment if necessary however generally it will be regenerated when needed 207 | #!**/[Pp]ackages/repositories.config 208 | # NuGet v3's project.json files produces more ignorable files 209 | *.nuget.props 210 | *.nuget.targets 211 | 212 | # Microsoft Azure Build Output 213 | csx/ 214 | *.build.csdef 215 | 216 | # Microsoft Azure Emulator 217 | ecf/ 218 | rcf/ 219 | 220 | # Windows Store app package directories and files 221 | AppPackages/ 222 | BundleArtifacts/ 223 | Package.StoreAssociation.xml 224 | _pkginfo.txt 225 | *.appx 226 | *.appxbundle 227 | *.appxupload 228 | 229 | # Visual Studio cache files 230 | # files ending in .cache can be ignored 231 | *.[Cc]ache 232 | # but keep track of directories ending in .cache 233 | !?*.[Cc]ache/ 234 | 235 | # Others 236 | ClientBin/ 237 | ~$* 238 | *~ 239 | *.dbmdl 240 | *.dbproj.schemaview 241 | *.jfm 242 | *.pfx 243 | *.publishsettings 244 | orleans.codegen.cs 245 | 246 | # Including strong name files can present a security risk 247 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 248 | #*.snk 249 | 250 | # Since there are multiple workflows, uncomment next line to ignore bower_components 251 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 252 | #bower_components/ 253 | 254 | # RIA/Silverlight projects 255 | Generated_Code/ 256 | 257 | # Backup & report files from converting an old project file 258 | # to a newer Visual Studio version. Backup files are not needed, 259 | # because we have git ;-) 260 | _UpgradeReport_Files/ 261 | Backup*/ 262 | UpgradeLog*.XML 263 | UpgradeLog*.htm 264 | ServiceFabricBackup/ 265 | *.rptproj.bak 266 | 267 | # SQL Server files 268 | *.mdf 269 | *.ldf 270 | *.ndf 271 | 272 | # Business Intelligence projects 273 | *.rdl.data 274 | *.bim.layout 275 | *.bim_*.settings 276 | *.rptproj.rsuser 277 | *- [Bb]ackup.rdl 278 | *- [Bb]ackup ([0-9]).rdl 279 | *- [Bb]ackup ([0-9][0-9]).rdl 280 | 281 | # Microsoft Fakes 282 | FakesAssemblies/ 283 | 284 | # GhostDoc plugin setting file 285 | *.GhostDoc.xml 286 | 287 | # Node.js Tools for Visual Studio 288 | .ntvs_analysis.dat 289 | node_modules/ 290 | 291 | # Visual Studio 6 build log 292 | *.plg 293 | 294 | # Visual Studio 6 workspace options file 295 | *.opt 296 | 297 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 298 | *.vbw 299 | 300 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 301 | *.vbp 302 | 303 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 304 | *.dsw 305 | *.dsp 306 | 307 | # Visual Studio 6 technical files 308 | *.ncb 309 | *.aps 310 | 311 | # Visual Studio LightSwitch build output 312 | **/*.HTMLClient/GeneratedArtifacts 313 | **/*.DesktopClient/GeneratedArtifacts 314 | **/*.DesktopClient/ModelManifest.xml 315 | **/*.Server/GeneratedArtifacts 316 | **/*.Server/ModelManifest.xml 317 | _Pvt_Extensions 318 | 319 | # Paket dependency manager 320 | .paket/paket.exe 321 | paket-files/ 322 | 323 | # FAKE - F# Make 324 | .fake/ 325 | 326 | # CodeRush personal settings 327 | .cr/personal 328 | 329 | # Python Tools for Visual Studio (PTVS) 330 | __pycache__/ 331 | *.pyc 332 | 333 | # Cake - Uncomment if you are using it 334 | # tools/** 335 | # !tools/packages.config 336 | 337 | # Tabs Studio 338 | *.tss 339 | 340 | # Telerik's JustMock configuration file 341 | *.jmconfig 342 | 343 | # BizTalk build output 344 | *.btp.cs 345 | *.btm.cs 346 | *.odx.cs 347 | *.xsd.cs 348 | 349 | # OpenCover UI analysis results 350 | OpenCover/ 351 | 352 | # Azure Stream Analytics local run output 353 | ASALocalRun/ 354 | 355 | # MSBuild Binary and Structured Log 356 | *.binlog 357 | 358 | # NVidia Nsight GPU debugger configuration file 359 | *.nvuser 360 | 361 | # MFractors (Xamarin productivity tool) working folder 362 | .mfractor/ 363 | 364 | # Local History for Visual Studio 365 | .localhistory/ 366 | 367 | # Visual Studio History (VSHistory) files 368 | .vshistory/ 369 | 370 | # BeatPulse healthcheck temp database 371 | healthchecksdb 372 | 373 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 374 | MigrationBackup/ 375 | 376 | # Ionide (cross platform F# VS Code tools) working folder 377 | .ionide/ 378 | 379 | # Fody - auto-generated XML schema 380 | FodyWeavers.xsd 381 | 382 | # VS Code files for those working on multiple tools 383 | .vscode/* 384 | !.vscode/settings.json 385 | !.vscode/tasks.json 386 | !.vscode/launch.json 387 | !.vscode/extensions.json 388 | *.code-workspace 389 | 390 | # Local History for Visual Studio Code 391 | .history/ 392 | 393 | # Windows Installer files from build outputs 394 | *.cab 395 | *.msi 396 | *.msix 397 | *.msm 398 | *.msp 399 | 400 | # JetBrains Rider 401 | *.sln.iml 402 | 403 | # fsdocs generated 404 | tmp/ 405 | temp/ 406 | .fsdocs 407 | docs/ 408 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ionide.ionide-paket", 4 | "ionide.ionide-fsharp", 5 | "ionide.ionide-fake", 6 | "ms-dotnettools.csharp", 7 | "editorConfig.editorConfig" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "FSharp.fsacRuntime":"netcore", 3 | "FSharp.enableAnalyzers": false, 4 | "FSharp.analyzersPath": [ 5 | "./packages/analyzers" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.0.0] - 2020-02-23 9 | 10 | First release 11 | 12 | ### Implemented type abbreviations and modules with functions for: 13 | * `FlatList` (`ImmutableArray`) 14 | * `ImmutableList` 15 | * `Stack` (`ImmutableStack`) 16 | * `Queue` (`ImmutableQueue`) 17 | * `HashMap` (`ImmutableDictionary`) 18 | * `SortedMap` (`ImmutableSortedDictionary`) 19 | * `HashSet` (`ImmutableHashSet`) 20 | * `SortedSet` (`ImmutableSortedSet`) 21 | * `IIndexedSeq` (`IReadOnlyList`) 22 | 23 | [Unreleased]: https://github.com/fsprojects/FSharp.Collections.Immutable/compare/releases/1.0.0...HEAD 24 | [1.0.0]: https://github.com/fsprojects/FSharp.Collections.Immutable/releases/tag/releases/1.0.0 25 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | F#;FSharp;Collections;Immutable 9 | https://github.com/fsprojects/FSharp.Collections.Immutable 10 | false 11 | LICENSE 12 | README.md 13 | logo.png 14 | 15 | git 16 | fsprojects, EventHelix;vilinski;anthony-mi;dim-37 17 | https://github.com/fsprojects/FSharp.Collections.Immutable 18 | 19 | true 20 | 21 | true 22 | snupkg 23 | true 24 | 25 | 26 | 27 | FSharp.Collections.Immutable 28 | 9.0 29 | enable 30 | true 31 | false 32 | true 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | <_BuildProjBaseIntermediateOutputPath>$(MSBuildThisFileDirectory)build/obj/ 9 | <_DotnetToolManifestFile>$(MSBuildThisFileDirectory).config/dotnet-tools.json 10 | <_DotnetToolRestoreOutputFile>$(_BuildProjBaseIntermediateOutputPath)/dotnet-tool-restore-$(NETCoreSdkVersion)-$(OS) 11 | <_DotnetFantomasOutputFile>$(BaseIntermediateOutputPath)dotnet-fantomas-msbuild-$(NETCoreSdkVersion)-$(OS) 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /FSharp.Collections.Immutable.slnf: -------------------------------------------------------------------------------- 1 | { 2 | "solution": { 3 | "path": "FSharp.Collections.Immutable.slnx", 4 | "projects": [ 5 | "src\\FSharp.Collections.Immutable\\FSharp.Collections.Immutable.fsproj", 6 | "tests\\FSharp.Collections.Immutable.Tests\\FSharp.Collections.Immutable.Tests.fsproj" 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /FSharp.Collections.Immutable.slnx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 2 | 3 | EventHelix.com Inc. (https://www.EventHelix.com) 4 | Andrii Chebukin (https://www.LinkedIn.com/in/XperiAndri) 5 | Anton Mynchenko (https://www.linkedin.com/in/anthony-minchenko-aa3b431a9/) 6 | Andreas Vilinski (https://www.linkedin.com/in/andreas-vilinski-68415155/) 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FSharp.Collections.Immutable [![NuGet Status](http://img.shields.io/nuget/v/FSharp.Collections.Immutable.svg?style=flat)](https://www.nuget.org/packages/FSharp.Collections.Immutable/) [![GitHub Actions](https://github.com/fsprojects/FSharp.Collections.Immutable/workflows/Build%20main/badge.svg)](https://github.com/fsprojects/FSharp.Collections.Immutable/actions?query=branch%3Amain) 2 | 3 | F# bindings for System.Collections.Immutable 4 | 5 | ![Build status](https://github.com/fsprojects/FSharp.Collections.Immutable/workflows/.NET%20Core/badge.svg) 6 | 7 | **FSharp.Collections.Immutable** is a collection of F# bindings for [System.Collections.Immutable](https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable). 8 | 9 | Please contribute to this project. Don't ask for permission, just fork the repository and send pull requests. 10 | 11 | Please also join the [F# Open Source Group](http://fsharp.github.com) 12 | 13 | ## Features 14 | 15 | * `FlatList` (`ImmutableArray`) 16 | * `ImmutableList` 17 | * `Stack` (`ImmutableStack`) 18 | * `Queue` (`ImmutableQueue`) 19 | * `HashMap` (`ImmutableDictionary`) 20 | * `SortedMap` (`ImmutableSortedDictionary`) 21 | * `HashSet` (`ImmutableHashSet`) 22 | * `SortedSet` (`ImmutableSortedSet`) 23 | * `IIndexedSeq` (`IReadOnlyList`) 24 | 25 | [Documentation](https://fsprojects.github.io/FSharp.Collections.Immutable/) 26 | 27 | --- 28 | 29 | ## Builds 30 | 31 | GitHub Actions | 32 | :---: | 33 | [![GitHub Actions](https://github.com/fsprojects/FSharp.Collections.Immutable/workflows/Build%20main/badge.svg)](https://github.com/fsprojects/FSharp.Collections.Immutable/actions?query=branch%3Amain) | 34 | 35 | ## NuGet 36 | 37 | Package | Stable | Prerelease 38 | --- | --- | --- 39 | FSharp.Collections.Immutable | [![NuGet Badge](https://img.shields.io/nuget/v/FSharp.Collections.Immutable.svg)](https://www.nuget.org/packages/FSharp.Collections.Immutable/) | [![NuGet Badge](https://img.shields.io/nuget/vpre/FSharp.Collections.Immutable.svg)](https://www.nuget.org/packages/FSharp.Collections.Immutable/) 40 | 41 | --- 42 | 43 | ### Developing 44 | 45 | Make sure the following **requirements** are installed on your system: 46 | 47 | - [dotnet SDK](https://www.microsoft.com/net/download/core) 8.0 or higher 48 | 49 | or 50 | 51 | - [VSCode Dev Container](https://code.visualstudio.com/docs/remote/containers) 52 | 53 | 54 | --- 55 | 56 | ### Environment Variables 57 | 58 | - `CONFIGURATION` will set the [configuration](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-build?tabs=netcore2x#options) of the dotnet commands. If not set, it will default to Release. 59 | - `CONFIGURATION=Debug ./build.sh` will result in `-c` additions to commands such as in `dotnet build -c Debug` 60 | - `ENABLE_COVERAGE` Will enable running code coverage metrics. AltCover can have [severe performance degradation](https://github.com/SteveGilham/altcover/issues/57) so code coverage evaluation are disabled by default to speed up the feedback loop. 61 | - `ENABLE_COVERAGE=1 ./build.sh` will enable code coverage evaluation 62 | 63 | 64 | --- 65 | 66 | ### Building 67 | > build.cmd // on windows 68 | 69 | > ./build.sh // on unix 70 | --- 71 | 72 | ### Build Targets 73 | 74 | - `Clean` - Cleans artifact and temp directories. 75 | - `DotnetRestore` - Runs [dotnet restore](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-restore?tabs=netcore2x) on the [solution file](https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/solution-dot-sln-file?view=vs-2019). 76 | - [`DotnetBuild`](#Building) - Runs [dotnet build](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-build?tabs=netcore2x) on the [solution file](https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/solution-dot-sln-file?view=vs-2019). 77 | - `FSharpAnalyzers` - Runs [BinaryDefense.FSharp.Analyzers](https://github.com/BinaryDefense/BinaryDefense.FSharp.Analyzers). 78 | - `DotnetTest` - Runs [dotnet test](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test?tabs=netcore21) on the [solution file](https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/solution-dot-sln-file?view=vs-2019). 79 | - `GenerateCoverageReport` - Code coverage is run during `DotnetTest` and this generates a report via [ReportGenerator](https://github.com/danielpalme/ReportGenerator). 80 | - `ShowCoverageReport` - Shows the report generated in `GenerateCoverageReport`. 81 | - `WatchTests` - Runs [dotnet watch](https://docs.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-3.0) with the test projects. Useful for rapid feedback loops. 82 | - `GenerateAssemblyInfo` - Generates [AssemblyInfo](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualbasic.applicationservices.assemblyinfo?view=netframework-4.8) for libraries. 83 | - `DotnetPack` - Runs [dotnet pack](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-pack). This includes running [Source Link](https://github.com/dotnet/sourcelink). 84 | - `SourceLinkTest` - Runs a Source Link test tool to verify Source Links were properly generated. 85 | - `PublishToNuGet` - Publishes the NuGet packages generated in `DotnetPack` to NuGet via [paket push](https://fsprojects.github.io/Paket/paket-push.html). Runs only from `Github Actions`. 86 | - `GitRelease` - Creates a commit message with the [Release Notes](https://fake.build/apidocs/v5/fake-core-releasenotes.html) and a git tag via the version in the `Release Notes`. 87 | - `GitHubRelease` - Publishes a [GitHub Release](https://help.github.com/en/articles/creating-releases) with the Release Notes and any NuGet packages. Runs only from `Github Actions`. 88 | - `FormatCode` - Runs [Fantomas](https://github.com/fsprojects/fantomas) on the solution file. 89 | - `CheckFormatCode` - Runs [Fantomas --check](https://fsprojects.github.io/fantomas/docs/end-users/FormattingCheck.html) on the solution file. 90 | - `BuildDocs` - Generates [Documentation](https://fsprojects.github.io/FSharp.Formatting) from `docsSrc` and the [XML Documentation Comments](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/) from your libraries in `src`. 91 | - `WatchDocs` - Generates documentation and starts a webserver locally. It will rebuild and hot reload if it detects any changes made to `docsSrc` files, or libraries in `src`. 92 | 93 | --- 94 | 95 | 96 | ### Releasing 97 | 98 | - [Start a git repo with a remote](https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/) 99 | git init 100 | git add . 101 | git commit -m "Scaffold" 102 | git branch -M main 103 | git remote add origin https://github.com/fsprojects/FSharp.Collections.Immutable.git 104 | git push -u origin main 105 | - [Create an Environment](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#creating-an-environment) on your repository named `nuget`. 106 | - [Create a NuGet API key](https://learn.microsoft.com/en-us/nuget/nuget-org/publish-a-package#create-an-api-key) 107 | - Add your `NUGET_TOKEN` to the [Environment Secrets](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#environment-secrets) of your newly created environment. 108 | - Then update the `CHANGELOG.md` with an "Unreleased" section containing release notes for this version, in [KeepAChangelog](https://keepachangelog.com/en/1.1.0/) format. 109 | 110 | NOTE: Its highly recommend to add a link to the Pull Request next to the release note that it affects. The reason for this is when the `RELEASE` target is run, it will add these new notes into the body of git commit. GitHub will notice the links and will update the Pull Request with what commit referenced it saying ["added a commit that referenced this pull request"](https://github.com/TheAngryByrd/MiniScaffold/pull/179#ref-commit-837ad59). Since the build script automates the commit message, it will say "Bump Version to x.y.z". The benefit of this is when users goto a Pull Request, it will be clear when and which version those code changes released. Also when reading the `CHANGELOG`, if someone is curious about how or why those changes were made, they can easily discover the work and discussions. 111 | 112 | ### Releasing Documentation 113 | 114 | - Set Source for "Build and deployment" on [GitHub Pages](https://github.com/fsprojects/FSharp.Collections.Immutable/settings/pages) to `GitHub Actions`. 115 | - Documentation is auto-deployed via [GitHub Action](https://github.com/fsprojects/FSharp.Collections.Immutable/blob/main/.github/workflows/fsdocs-gh-pages.yml) to [Your GitHub Page](https://fsprojects.github.io/FSharp.Collections.Immutable/) 116 | 117 | # Maintainer(s) 118 | 119 | - [@xperiandri](https://github.com/xperiandri) 120 | - [@eventhelix](https://github.com/eventhelix) 121 | 122 | The default maintainer account for projects under "fsprojects" is [@fsprojectsgit](https://github.com/fsprojectsgit) - F# Community Project Incubation Space (repo management) 123 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | dotnet run --project ./build/build.fsproj -- -t %* 2 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | set -o pipefail 5 | 6 | FAKE_DETAILED_ERRORS=true dotnet run --project ./build/build.fsproj -- -t "$@" 7 | -------------------------------------------------------------------------------- /build/Changelog.fs: -------------------------------------------------------------------------------- 1 | module Changelog 2 | 3 | open System 4 | open Fake.Core 5 | open Fake.IO 6 | 7 | let isEmptyChange = 8 | function 9 | | Changelog.Change.Added s 10 | | Changelog.Change.Changed s 11 | | Changelog.Change.Deprecated s 12 | | Changelog.Change.Fixed s 13 | | Changelog.Change.Removed s 14 | | Changelog.Change.Security s 15 | | Changelog.Change.Custom (_, s) -> String.IsNullOrWhiteSpace s.CleanedText 16 | 17 | let tagFromVersionNumber versionNumber = sprintf "releases/%s" versionNumber 18 | 19 | let failOnEmptyChangelog (latestEntry : Changelog.ChangelogEntry) = 20 | let isEmpty = 21 | (latestEntry.Changes |> Seq.forall isEmptyChange) 22 | || latestEntry.Changes |> Seq.isEmpty 23 | 24 | if isEmpty then 25 | failwith "No changes in CHANGELOG. Please add your changes under a heading specified in https://keepachangelog.com/" 26 | 27 | let mkLinkReference (newVersion : SemVerInfo) (changelog : Changelog.Changelog) (gitHubRepoUrl : string) = 28 | if changelog.Entries |> List.isEmpty then 29 | // No actual changelog entries yet: link reference will just point to the Git tag 30 | sprintf "[%s]: %s/releases/tag/%s" newVersion.AsString gitHubRepoUrl (tagFromVersionNumber newVersion.AsString) 31 | else 32 | let versionTuple version = (version.Major, version.Minor, version.Patch) 33 | // Changelog entries come already sorted, most-recent first, by the Changelog module 34 | let prevEntry = 35 | changelog.Entries 36 | |> List.skipWhile (fun entry -> 37 | entry.SemVer.PreRelease.IsSome 38 | || versionTuple entry.SemVer = versionTuple newVersion 39 | ) 40 | |> List.tryHead 41 | 42 | let linkTarget = 43 | match prevEntry with 44 | | Some entry -> 45 | sprintf 46 | "%s/compare/%s...%s" 47 | gitHubRepoUrl 48 | (tagFromVersionNumber entry.SemVer.AsString) 49 | (tagFromVersionNumber newVersion.AsString) 50 | | None -> sprintf "%s/releases/tag/%s" gitHubRepoUrl (tagFromVersionNumber newVersion.AsString) 51 | 52 | sprintf "[%s]: %s" newVersion.AsString linkTarget 53 | 54 | let mkReleaseNotes changelog (latestEntry : Changelog.ChangelogEntry) gitHubRepoUrl = 55 | let linkReference = mkLinkReference latestEntry.SemVer changelog gitHubRepoUrl 56 | 57 | if String.isNullOrEmpty linkReference then 58 | latestEntry.ToString () 59 | else 60 | // Add link reference target to description before building release notes, since in main changelog file it's at the bottom of the file 61 | let description = 62 | match latestEntry.Description with 63 | | None -> linkReference 64 | | Some desc when desc.Contains (linkReference) -> desc 65 | | Some desc -> sprintf "%s\n\n%s" (desc.Trim ()) linkReference 66 | 67 | { latestEntry with Description = Some description }.ToString () 68 | 69 | let getVersionNumber envVarName ctx = 70 | let args = ctx.Context.Arguments 71 | 72 | let verArg = 73 | args 74 | |> List.tryHead 75 | |> Option.defaultWith (fun () -> Environment.environVarOrDefault envVarName "") 76 | 77 | if SemVer.isValid verArg then 78 | verArg 79 | elif verArg.StartsWith ("v") && SemVer.isValid verArg.[1..] then 80 | let target = ctx.Context.FinalTarget 81 | 82 | Trace.traceImportantfn 83 | "Please specify a version number without leading 'v' next time, e.g. \"./build.sh %s %s\" rather than \"./build.sh %s %s\"" 84 | target 85 | verArg.[1..] 86 | target 87 | verArg 88 | 89 | verArg.[1..] 90 | elif String.isNullOrEmpty verArg then 91 | let target = ctx.Context.FinalTarget 92 | 93 | Trace.traceErrorfn 94 | "Please specify a version number, either at the command line (\"./build.sh %s 1.0.0\") or in the %s environment variable" 95 | target 96 | envVarName 97 | 98 | failwith "No version number found" 99 | else 100 | Trace.traceErrorfn "Please specify a valid version number: %A could not be recognized as a version number" verArg 101 | 102 | failwith "Invalid version number" 103 | 104 | let mutable changelogBackupFilename = "" 105 | 106 | let updateChangelog changelogPath (changelog : Fake.Core.Changelog.Changelog) gitHubRepoUrl ctx = 107 | 108 | let verStr = ctx |> getVersionNumber "RELEASE_VERSION" 109 | 110 | let description, unreleasedChanges = 111 | match changelog.Unreleased with 112 | | None -> None, [] 113 | | Some u -> u.Description, u.Changes 114 | 115 | let newVersion = SemVer.parse verStr 116 | 117 | changelog.Entries 118 | |> List.tryFind (fun entry -> entry.SemVer = newVersion) 119 | |> Option.iter (fun entry -> 120 | Trace.traceErrorfn 121 | "Version %s already exists in %s, released on %s" 122 | verStr 123 | changelogPath 124 | (if entry.Date.IsSome then 125 | entry.Date.Value.ToString ("yyyy-MM-dd") 126 | else 127 | "(no date specified)") 128 | 129 | failwith "Can't release with a duplicate version number" 130 | ) 131 | 132 | changelog.Entries 133 | |> List.tryFind (fun entry -> entry.SemVer > newVersion) 134 | |> Option.iter (fun entry -> 135 | Trace.traceErrorfn 136 | "You're trying to release version %s, but a later version %s already exists, released on %s" 137 | verStr 138 | entry.SemVer.AsString 139 | (if entry.Date.IsSome then 140 | entry.Date.Value.ToString ("yyyy-MM-dd") 141 | else 142 | "(no date specified)") 143 | 144 | failwith "Can't release with a version number older than an existing release" 145 | ) 146 | 147 | let versionTuple version = (version.Major, version.Minor, version.Patch) 148 | 149 | let prereleaseEntries = 150 | changelog.Entries 151 | |> List.filter (fun entry -> 152 | entry.SemVer.PreRelease.IsSome 153 | && versionTuple entry.SemVer = versionTuple newVersion 154 | ) 155 | 156 | let prereleaseChanges = 157 | prereleaseEntries 158 | |> List.collect (fun entry -> entry.Changes |> List.filter (not << isEmptyChange)) 159 | |> List.distinct 160 | 161 | let assemblyVersion, nugetVersion = Changelog.parseVersions newVersion.AsString 162 | 163 | let newEntry = 164 | Changelog.ChangelogEntry.New ( 165 | assemblyVersion.Value, 166 | nugetVersion.Value, 167 | Some System.DateTime.Today, 168 | description, 169 | unreleasedChanges @ prereleaseChanges, 170 | false 171 | ) 172 | 173 | let newChangelog = 174 | Changelog.Changelog.New (changelog.Header, changelog.Description, None, newEntry :: changelog.Entries) 175 | 176 | // Save changelog to temporary file before making any edits 177 | changelogBackupFilename <- System.IO.Path.GetTempFileName () 178 | 179 | changelogPath |> Shell.copyFile changelogBackupFilename 180 | 181 | Target.activateFinal "DeleteChangelogBackupFile" 182 | 183 | newChangelog |> Changelog.save changelogPath 184 | 185 | // Now update the link references at the end of the file 186 | let linkReferenceForLatestEntry = mkLinkReference newVersion changelog gitHubRepoUrl 187 | 188 | let linkReferenceForUnreleased = 189 | sprintf "[Unreleased]: %s/compare/%s...%s" gitHubRepoUrl (tagFromVersionNumber newVersion.AsString) "HEAD" 190 | 191 | let tailLines = File.read changelogPath |> List.ofSeq |> List.rev 192 | 193 | let isRef (line : string) = 194 | System.Text.RegularExpressions.Regex.IsMatch (line, @"^\[.+?\]:\s?[a-z]+://.*$") 195 | 196 | let linkReferenceTargets = 197 | tailLines 198 | |> List.skipWhile String.isNullOrWhiteSpace 199 | |> List.takeWhile isRef 200 | |> List.rev // Now most recent entry is at the head of the list 201 | 202 | let newLinkReferenceTargets = 203 | match linkReferenceTargets with 204 | | [] -> [ linkReferenceForUnreleased; linkReferenceForLatestEntry ] 205 | | first :: rest when first |> String.startsWith "[Unreleased]:" -> 206 | linkReferenceForUnreleased 207 | :: linkReferenceForLatestEntry 208 | :: rest 209 | | first :: rest -> 210 | linkReferenceForUnreleased 211 | :: linkReferenceForLatestEntry 212 | :: first 213 | :: rest 214 | 215 | let blankLineCount = 216 | tailLines 217 | |> Seq.takeWhile String.isNullOrWhiteSpace 218 | |> Seq.length 219 | 220 | let linkRefCount = linkReferenceTargets |> List.length 221 | 222 | let skipCount = blankLineCount + linkRefCount 223 | 224 | let updatedLines = 225 | List.rev (tailLines |> List.skip skipCount) 226 | @ newLinkReferenceTargets 227 | 228 | File.write false changelogPath updatedLines 229 | 230 | // If build fails after this point but before we commit changes, undo our modifications 231 | Target.activateBuildFailure "RevertChangelog" 232 | 233 | newEntry 234 | -------------------------------------------------------------------------------- /build/FsDocs.fs: -------------------------------------------------------------------------------- 1 | namespace Fake.DotNet 2 | 3 | open Fake.Core 4 | open Fake.IO 5 | open Fake.IO.FileSystemOperators 6 | 7 | /// 8 | /// Contains tasks to interact with fsdocs tool to 9 | /// process F# script files, markdown and for generating API documentation. 10 | /// 11 | [] 12 | module Fsdocs = 13 | 14 | /// 15 | /// Fsdocs build command parameters and options 16 | /// 17 | type BuildCommandParams = { 18 | /// Input directory of content (default: docs) 19 | Input : string option 20 | 21 | /// Project files to build API docs for outputs, defaults to all packable projects 22 | Projects : seq option 23 | 24 | /// Output Directory (default output for build and tmp/watch for watch) 25 | Output : string option 26 | 27 | /// Disable generation of API docs 28 | NoApiDocs : bool option 29 | 30 | /// Evaluate F# fragments in scripts 31 | Eval : bool option 32 | 33 | /// Save images referenced in docs 34 | SaveImages : bool option 35 | 36 | /// Add line numbers 37 | LineNumbers : bool option 38 | 39 | /// Additional substitution parameters for templates 40 | Parameters : seq option 41 | 42 | /// Disable project cracking. 43 | IgnoreProjects : bool option 44 | 45 | /// In API doc generation qualify the output by the collection name, e.g. 'reference/FSharp.Core/...' instead of 'reference/...' . 46 | Qualify : bool option 47 | 48 | /// The tool will also generate documentation for non-public members 49 | NoPublic : bool option 50 | 51 | /// Do not copy default content styles, javascript or use default templates 52 | NoDefaultContent : bool option 53 | 54 | /// Clean the output directory 55 | Clean : bool option 56 | 57 | /// Display version information 58 | Version : bool option 59 | 60 | /// Provide properties to dotnet msbuild, e.g. --properties Configuration=Release Version=3.4 61 | Properties : string option 62 | 63 | /// Additional arguments passed down as otherflags to the F# compiler when the API is being generated. 64 | /// Note that these arguments are trimmed, this is to overcome a limitation in the command line argument 65 | /// processing. A typical use-case would be to pass an addition assembly reference. 66 | /// Example --fscoptions " -r:MyAssembly.dll" 67 | FscOptions : string option 68 | 69 | /// Fail if docs are missing or can't be generated 70 | Strict : bool option 71 | 72 | /// Source folder at time of component build (<FsDocsSourceFolder>) 73 | SourceFolder : string option 74 | 75 | /// Source repository for github links (<FsDocsSourceRepository>) 76 | SourceRepository : string option 77 | 78 | /// Assume comments in F# code are markdown (<UsesMarkdownComments>) 79 | MdComments : bool option 80 | } with 81 | 82 | /// Parameter default values. 83 | static member Default = { 84 | Input = None 85 | Projects = None 86 | Output = None 87 | NoApiDocs = None 88 | Eval = None 89 | SaveImages = None 90 | LineNumbers = None 91 | Parameters = None 92 | IgnoreProjects = None 93 | Qualify = None 94 | NoPublic = None 95 | NoDefaultContent = None 96 | Clean = None 97 | Version = None 98 | Properties = None 99 | FscOptions = None 100 | Strict = None 101 | SourceFolder = None 102 | SourceRepository = None 103 | MdComments = None 104 | } 105 | 106 | /// 107 | /// Fsdocs watch command parameters and options 108 | /// 109 | type WatchCommandParams = { 110 | /// Do not serve content when watching. 111 | NoServer : bool option 112 | 113 | /// Do not launch a browser window. 114 | NoLaunch : bool option 115 | 116 | /// URL extension to launch http://localhost:/%s. 117 | Open : string option 118 | 119 | /// Port to serve content for http://localhost serving. 120 | Port : int option 121 | 122 | /// Build Commands 123 | BuildCommandParams : BuildCommandParams option 124 | } with 125 | 126 | /// Parameter default values. 127 | static member Default = { 128 | NoServer = None 129 | NoLaunch = None 130 | Open = None 131 | Port = None 132 | BuildCommandParams = None 133 | } 134 | 135 | let internal buildBuildCommandParams (buildParams : BuildCommandParams) = 136 | let buildSubstitutionParameters (subParameters : seq) = 137 | seq { 138 | yield "--parameters" 139 | for (key, value) in subParameters do 140 | yield key 141 | yield value 142 | } 143 | |> String.concat " " 144 | 145 | System.Text.StringBuilder () 146 | |> StringBuilder.appendIfSome buildParams.Input (sprintf "--input %s") 147 | |> StringBuilder.appendIfSome buildParams.Projects (fun projects -> $"""--projects %s{projects |> String.concat " "}""") 148 | |> StringBuilder.appendIfSome buildParams.Output (sprintf "--output %s") 149 | |> StringBuilder.appendIfSome buildParams.NoApiDocs (fun _ -> "--noapidocs") 150 | |> StringBuilder.appendIfSome buildParams.Eval (fun _ -> "--eval") 151 | |> StringBuilder.appendIfSome buildParams.SaveImages (fun _ -> "--saveimages") 152 | |> StringBuilder.appendIfSome buildParams.LineNumbers (fun _ -> "--linenumbers") 153 | |> StringBuilder.appendIfSome buildParams.Parameters (fun parameters -> buildSubstitutionParameters parameters) 154 | |> StringBuilder.appendIfSome buildParams.IgnoreProjects (fun _ -> "--ignoreprojects") 155 | |> StringBuilder.appendIfSome buildParams.Qualify (fun _ -> "--qualify") 156 | |> StringBuilder.appendIfSome buildParams.NoPublic (fun _ -> "--nonpublic") 157 | |> StringBuilder.appendIfSome buildParams.NoDefaultContent (fun _ -> "--nodefaultcontent") 158 | |> StringBuilder.appendIfSome buildParams.Clean (fun _ -> "--clean") 159 | |> StringBuilder.appendIfSome buildParams.Version (fun _ -> "--version") 160 | |> StringBuilder.appendIfSome buildParams.Properties (sprintf "--properties %s") 161 | |> StringBuilder.appendIfSome buildParams.FscOptions (sprintf "--fscoptions %s") 162 | |> StringBuilder.appendIfSome buildParams.Strict (fun _ -> "--strict") 163 | |> StringBuilder.appendIfSome buildParams.SourceFolder (sprintf "--sourcefolder %s") 164 | |> StringBuilder.appendIfSome buildParams.SourceRepository (sprintf "--sourcerepo %s") 165 | |> StringBuilder.appendIfSome buildParams.MdComments (fun _ -> "--mdcomments") 166 | |> StringBuilder.toText 167 | |> String.trim 168 | 169 | let internal buildWatchCommandParams (watchParams : WatchCommandParams) = 170 | System.Text.StringBuilder () 171 | |> StringBuilder.appendIfSome watchParams.NoServer (fun _ -> "--noserver") 172 | |> StringBuilder.appendIfSome watchParams.NoLaunch (fun _ -> "--nolaunch") 173 | |> StringBuilder.appendIfSome watchParams.Open (sprintf "--open %s") 174 | |> StringBuilder.appendIfSome watchParams.Port (sprintf "--port %i") 175 | |> StringBuilder.appendIfSome watchParams.BuildCommandParams buildBuildCommandParams 176 | |> StringBuilder.toText 177 | |> String.trim 178 | 179 | 180 | let cleanCache (workingDirectory) = Shell.cleanDirs [ workingDirectory ".fsdocs" ] 181 | 182 | /// 183 | /// Build documentation using fsdocs build command 184 | /// 185 | /// 186 | /// Function used to overwrite the dotnetOptions. 187 | /// Function used to overwrite the build command default parameters. 188 | /// 189 | /// 190 | /// 191 | /// Fsdocs.build (fun p -> { p with Clean = Some(true); Strict = Some(true) }) 192 | /// 193 | /// 194 | let build dotnetOptions setBuildParams = 195 | let buildParams = setBuildParams BuildCommandParams.Default 196 | let formattedParameters = buildBuildCommandParams buildParams 197 | 198 | // let dotnetOptions = (fun (buildOptions: DotNet.Options) -> buildOptions) 199 | let result = DotNet.exec dotnetOptions "fsdocs build" formattedParameters 200 | 201 | if 0 <> result.ExitCode then 202 | failwithf "fsdocs build failed with exit code '%d'" result.ExitCode 203 | 204 | /// 205 | /// Watch documentation using fsdocs watch command 206 | /// 207 | /// 208 | /// Function used to overwrite the dotnetOptions. 209 | /// Function used to overwrite the watch command default parameters. 210 | /// 211 | /// 212 | /// 213 | /// Fsdocs.watch (fun p -> { p with Port = Some(3005) }) 214 | /// 215 | /// 216 | let watch dotnetOptions setWatchParams = 217 | let watchParams = setWatchParams WatchCommandParams.Default 218 | let formattedParameters = buildWatchCommandParams watchParams 219 | 220 | // let dotnetOptions = (fun (buildOptions: DotNet.Options) -> buildOptions) 221 | let result = DotNet.exec dotnetOptions "fsdocs watch" formattedParameters 222 | 223 | if 0 <> result.ExitCode then 224 | failwithf "fsdocs watch failed with exit code '%d'" result.ExitCode 225 | -------------------------------------------------------------------------------- /build/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "BuildAndTest": { 4 | "commandName": "Project", 5 | "commandLineArgs": "--target DotnetTest" 6 | }, 7 | "Publish": { 8 | "commandName": "Project", 9 | "commandLineArgs": "--target Publish" 10 | }, 11 | "PublishToGitHub": { 12 | "commandName": "Project", 13 | "commandLineArgs": "--target PublishToGitHub" 14 | }, 15 | "BuildDocs": { 16 | "commandName": "Project", 17 | "commandLineArgs": "--target BuildDocs" 18 | }, 19 | "Release": { 20 | "commandName": "Project", 21 | "commandLineArgs": "--target Release 1.0.0" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /build/build.fs: -------------------------------------------------------------------------------- 1 | open System 2 | open Fake.Core 3 | open Fake.DotNet 4 | open Fake.Tools 5 | open Fake.IO 6 | open Fake.IO.FileSystemOperators 7 | open Fake.IO.Globbing.Operators 8 | open Fake.Core.TargetOperators 9 | open Fake.Api 10 | open Fake.BuildServer 11 | open Argu 12 | 13 | let environVarAsBoolOrDefault varName defaultValue = 14 | let truthyConsts = [ "1"; "Y"; "YES"; "T"; "TRUE" ] 15 | Environment.environVar varName 16 | |> ValueOption.ofObj 17 | |> ValueOption.map (fun envvar -> 18 | truthyConsts 19 | |> List.exists (fun ``const`` -> String.Equals (``const``, envvar, StringComparison.InvariantCultureIgnoreCase)) 20 | ) 21 | |> ValueOption.defaultValue defaultValue 22 | 23 | //----------------------------------------------------------------------------- 24 | // Metadata and Configuration 25 | //----------------------------------------------------------------------------- 26 | 27 | let rootDirectory = __SOURCE_DIRECTORY__ ".." 28 | 29 | let productName = "FSharp.Collections.Immutable" 30 | 31 | let sln = rootDirectory "FSharp.Collections.Immutable.slnf" 32 | 33 | let srcCodeGlob = 34 | !!(rootDirectory "src/**/*.fs") 35 | ++ (rootDirectory "src/**/*.fsx") 36 | -- (rootDirectory "src/**/obj/**/*.fs") 37 | 38 | let testsCodeGlob = 39 | !!(rootDirectory "tests/**/*.fs") 40 | ++ (rootDirectory "tests/**/*.fsx") 41 | -- (rootDirectory "tests/**/obj/**/*.fs") 42 | 43 | let srcGlob = rootDirectory "src/**/*.??proj" 44 | 45 | let testsGlob = rootDirectory "tests/**/*.??proj" 46 | 47 | let srcAndTest = !!srcGlob ++ testsGlob 48 | 49 | let distDir = rootDirectory "dist" 50 | 51 | let distGlob = distDir "*.nupkg" 52 | 53 | let testResultsDir = rootDirectory "TestResults" 54 | 55 | let coverageReportDir = rootDirectory "docs" "coverage" 56 | 57 | let docsDir = rootDirectory "docs" 58 | 59 | let docsSrcDir = rootDirectory "docsSrc" 60 | 61 | let temp = rootDirectory "temp" 62 | 63 | let watchDocsDir = temp "watch-docs" 64 | 65 | let gitOwner = "fsprojects" 66 | let gitRepoName = "FSharp.Collections.Immutable" 67 | 68 | let gitHubRepoUrl = $"https://github.com/%s{gitOwner}/%s{gitRepoName}" 69 | 70 | let documentationRootUrl = $"https://%s{gitOwner}.github.io/%s{gitRepoName}" 71 | 72 | let releaseBranch = "main" 73 | let readme = "README.md" 74 | let changelogFile = "CHANGELOG.md" 75 | 76 | let READMElink = Uri (Uri (gitHubRepoUrl), $"blob/{releaseBranch}/{readme}") 77 | let CHANGELOGlink = Uri (Uri (gitHubRepoUrl), $"blob/{releaseBranch}/{changelogFile}") 78 | 79 | let changelogPath = rootDirectory changelogFile 80 | 81 | let changelog = Fake.Core.Changelog.load changelogPath 82 | 83 | let mutable latestEntry = 84 | if Seq.isEmpty changelog.Entries then 85 | Changelog.ChangelogEntry.New ("0.0.1", "0.0.1-alpha.1", Some DateTime.Today, None, [], false) 86 | else 87 | changelog.LatestEntry 88 | 89 | let mutable changelogBackupFilename = "" 90 | 91 | let publishUrl = "https://www.nuget.org" 92 | 93 | let enableCodeCoverage = environVarAsBoolOrDefault "ENABLE_COVERAGE" false 94 | 95 | let githubToken = Environment.environVarOrNone "GITHUB_TOKEN" 96 | 97 | let nugetToken = Environment.environVarOrNone "NUGET_TOKEN" 98 | 99 | //----------------------------------------------------------------------------- 100 | // Helpers 101 | //----------------------------------------------------------------------------- 102 | 103 | 104 | let isRelease (targets : Target list) = 105 | targets 106 | |> Seq.map (fun t -> t.Name) 107 | |> Seq.exists ((=) "PublishToNuGet") 108 | 109 | let invokeAsync f = async { f () } 110 | 111 | let configuration (targets : Target list) = 112 | let defaultVal = if isRelease targets then "Release" else "Debug" 113 | 114 | match Environment.environVarOrDefault "CONFIGURATION" defaultVal with 115 | | "Debug" -> DotNet.BuildConfiguration.Debug 116 | | "Release" -> DotNet.BuildConfiguration.Release 117 | | config -> DotNet.BuildConfiguration.Custom config 118 | 119 | let failOnBadExitAndPrint (p : ProcessResult) = 120 | if p.ExitCode <> 0 then 121 | p.Errors |> Seq.iter Trace.traceError 122 | 123 | failwithf "failed with exitcode %d" p.ExitCode 124 | 125 | let isPublishToGitHub ctx = ctx.Context.FinalTarget = "PublishToGitHub" 126 | 127 | let isCI = lazy environVarAsBoolOrDefault "CI" false 128 | 129 | // CI Servers can have bizarre failures that have nothing to do with your code 130 | let rec retryIfInCI times fn = 131 | match isCI.Value with 132 | | true -> 133 | if times > 1 then 134 | try 135 | fn () 136 | with _ -> 137 | retryIfInCI (times - 1) fn 138 | else 139 | fn () 140 | | _ -> fn () 141 | 142 | let failOnWrongBranch () = 143 | if Git.Information.getBranchName "" <> releaseBranch then 144 | failwithf "Not on %s. If you want to release please switch to this branch." releaseBranch 145 | 146 | 147 | module dotnet = 148 | let watch cmdParam program args = DotNet.exec cmdParam (sprintf "watch %s" program) args 149 | 150 | let run cmdParam args = DotNet.exec cmdParam "run" args 151 | 152 | let tool optionConfig command args = 153 | DotNet.exec optionConfig (sprintf "%s" command) args 154 | |> failOnBadExitAndPrint 155 | 156 | let reportgenerator optionConfig args = tool optionConfig "reportgenerator" args 157 | 158 | let sourcelink optionConfig args = tool optionConfig "sourcelink" args 159 | 160 | let fcswatch optionConfig args = tool optionConfig "fcswatch" args 161 | 162 | let fsharpAnalyzer optionConfig args = tool optionConfig "fsharp-analyzers" args 163 | 164 | let fantomas args = DotNet.exec id "fantomas" args 165 | 166 | module FSharpAnalyzers = 167 | type Arguments = 168 | | Project of string 169 | | Analyzers_Path of string 170 | | Fail_On_Warnings of string list 171 | | Ignore_Files of string list 172 | | Verbose 173 | 174 | interface IArgParserTemplate with 175 | member s.Usage = "" 176 | 177 | 178 | module DocsTool = 179 | let quoted s = $"\"%s{s}\"" 180 | 181 | let fsDocsDotnetOptions (o : DotNet.Options) = { o with WorkingDirectory = rootDirectory } 182 | 183 | let fsDocsBuildParams configuration (p : Fsdocs.BuildCommandParams) = { 184 | p with 185 | Clean = Some true 186 | Input = Some (quoted docsSrcDir) 187 | Output = Some (quoted docsDir) 188 | Eval = Some true 189 | Projects = Some (Seq.map quoted (!!srcGlob)) 190 | Properties = Some ($"Configuration=%s{configuration}") 191 | Parameters = 192 | Some [ 193 | // https://fsprojects.github.io/FSharp.Formatting/content.html#Templates-and-Substitutions 194 | "root", quoted $"{documentationRootUrl}/" 195 | "fsdocs-collection-name", quoted productName 196 | "fsdocs-repository-branch", quoted releaseBranch 197 | "fsdocs-package-version", quoted latestEntry.NuGetVersion 198 | "fsdocs-readme-link", quoted (READMElink.ToString ()) 199 | "fsdocs-release-notes-link", quoted (CHANGELOGlink.ToString ()) 200 | "fsdocs-logo-src", 201 | quoted ( 202 | "https://raw.githubusercontent.com/fsprojects/FSharp.Collections.Immutable/refs/heads/main/docsSrc/content/logo.png" 203 | ) 204 | ] 205 | Strict = Some true 206 | } 207 | 208 | let cleanDocsCache () = Fsdocs.cleanCache rootDirectory 209 | 210 | let build (configuration) = Fsdocs.build fsDocsDotnetOptions (fsDocsBuildParams configuration) 211 | 212 | 213 | let watch (configuration) = 214 | let buildParams bp = 215 | let bp = 216 | Option.defaultValue Fsdocs.BuildCommandParams.Default bp 217 | |> fsDocsBuildParams configuration 218 | 219 | { bp with Output = Some watchDocsDir; Strict = None } 220 | 221 | Fsdocs.watch fsDocsDotnetOptions (fun p -> { p with BuildCommandParams = Some (buildParams p.BuildCommandParams) }) 222 | 223 | let allReleaseChecks () = failOnWrongBranch () 224 | //Changelog.failOnEmptyChangelog latestEntry 225 | 226 | 227 | let failOnLocalBuild () = 228 | if not isCI.Value then 229 | failwith "Not on CI. If you want to publish, please use CI." 230 | 231 | let failOnCIBuild () = 232 | if isCI.Value then 233 | failwith "On CI. If you want to run this target, please use a local build." 234 | 235 | let allPublishChecks () = failOnLocalBuild () 236 | //Changelog.failOnEmptyChangelog latestEntry 237 | 238 | //----------------------------------------------------------------------------- 239 | // Target Implementations 240 | //----------------------------------------------------------------------------- 241 | 242 | /// So we don't require always being on the latest MSBuild.StructuredLogger 243 | let disableBinLog (p : MSBuild.CliArguments) = { p with DisableInternalBinLog = true } 244 | 245 | let clean _ = 246 | [ "bin"; "temp"; distDir; coverageReportDir; testResultsDir ] 247 | |> Shell.cleanDirs 248 | 249 | !!srcGlob ++ testsGlob 250 | |> Seq.collect (fun p -> 251 | [ "bin"; "obj" ] 252 | |> Seq.map (fun sp -> IO.Path.GetDirectoryName p sp) 253 | ) 254 | |> Shell.cleanDirs 255 | 256 | let dotnetRestore _ = 257 | [ sln ] 258 | |> Seq.map (fun dir -> 259 | fun () -> 260 | let args = [] |> String.concat " " 261 | 262 | DotNet.restore 263 | (fun c -> { 264 | c with 265 | MSBuildParams = disableBinLog c.MSBuildParams 266 | Common = c.Common |> DotNet.Options.withCustomParams (Some (args)) 267 | }) 268 | dir 269 | ) 270 | |> Seq.iter (retryIfInCI 10) 271 | 272 | let dotnetToolRestore _ = 273 | let result = 274 | fun () -> DotNet.exec id "tool" "restore" 275 | |> (retryIfInCI 10) 276 | 277 | 278 | if not result.OK then 279 | failwithf "Failed to restore .NET tools: %A" result.Errors 280 | 281 | let updateChangelog ctx = 282 | latestEntry <- 283 | if not <| isPublishToGitHub ctx then 284 | Changelog.updateChangelog changelogPath changelog gitHubRepoUrl ctx 285 | elif Seq.isEmpty changelog.Entries then 286 | latestEntry 287 | else 288 | let latest = changelog.LatestEntry 289 | let semVer = { 290 | latest.SemVer with 291 | Original = None 292 | Patch = latest.SemVer.Patch + 1u 293 | PreRelease = PreRelease.TryParse "ci" 294 | } 295 | { 296 | latest with 297 | SemVer = semVer 298 | NuGetVersion = semVer.AsString 299 | AssemblyVersion = semVer.AsString 300 | } 301 | 302 | let revertChangelog _ = 303 | if String.isNotNullOrEmpty Changelog.changelogBackupFilename then 304 | Changelog.changelogBackupFilename 305 | |> Shell.copyFile changelogPath 306 | 307 | let deleteChangelogBackupFile _ = 308 | if String.isNotNullOrEmpty Changelog.changelogBackupFilename then 309 | Shell.rm Changelog.changelogBackupFilename 310 | 311 | let getPackageVersionProperty publishToGitHub = 312 | if publishToGitHub then 313 | let runId = Environment.environVar "GITHUB_RUN_ID" 314 | $"/p:PackageVersion=%s{latestEntry.NuGetVersion}-%s{runId}" 315 | else 316 | $"/p:PackageVersion=%s{latestEntry.NuGetVersion}" 317 | 318 | let dotnetBuild ctx = 319 | 320 | let publishToGitHub = isPublishToGitHub ctx 321 | 322 | let args = [ getPackageVersionProperty publishToGitHub; "--no-restore" ] 323 | 324 | DotNet.build 325 | (fun c -> { 326 | c with 327 | Configuration = configuration (ctx.Context.AllExecutingTargets) 328 | Common = c.Common |> DotNet.Options.withAdditionalArgs args 329 | MSBuildParams = { 330 | (disableBinLog c.MSBuildParams) with 331 | Properties = [ 332 | if publishToGitHub then 333 | ("DebugType", "embedded") 334 | ("EmbedAllSources", "true") 335 | ] 336 | } 337 | }) 338 | sln 339 | 340 | let fsharpAnalyzers _ = 341 | let argParser = ArgumentParser.Create (programName = "fsharp-analyzers") 342 | 343 | !!srcGlob 344 | |> Seq.iter (fun proj -> 345 | let args = 346 | [ 347 | FSharpAnalyzers.Analyzers_Path (rootDirectory "packages/analyzers") 348 | FSharpAnalyzers.Arguments.Project proj 349 | FSharpAnalyzers.Arguments.Fail_On_Warnings [ "BDH0002" ] 350 | FSharpAnalyzers.Arguments.Ignore_Files [ "*AssemblyInfo.fs" ] 351 | FSharpAnalyzers.Verbose 352 | ] 353 | |> argParser.PrintCommandLineArgumentsFlat 354 | 355 | dotnet.fsharpAnalyzer id args 356 | ) 357 | 358 | let dotnetTest ctx = 359 | // Create test results directory if it doesn't exist 360 | Directory.create testResultsDir 361 | 362 | let args = [ 363 | "--no-build" 364 | if enableCodeCoverage then 365 | "--collect:\"Code Coverage\"" 366 | "--results-directory" 367 | testResultsDir 368 | "--logger:trx" // Enable TRX report generation 369 | ] 370 | 371 | DotNet.test 372 | (fun c -> { 373 | c with 374 | MSBuildParams = disableBinLog c.MSBuildParams 375 | Configuration = configuration (ctx.Context.AllExecutingTargets) 376 | Common = c.Common |> DotNet.Options.withAdditionalArgs args 377 | }) 378 | sln 379 | 380 | let generateCoverageReport _ = 381 | 382 | let coverageFiles = !!(testResultsDir "*/coverage.cobertura.xml") 383 | 384 | let sourceDirs = !!srcGlob |> Seq.map Path.getDirectory |> String.concat ";" 385 | 386 | let independentArgs = [ 387 | sprintf "-reports:\"%s\"" (coverageFiles |> String.concat ";") 388 | sprintf "-targetdir:\"%s\"" coverageReportDir 389 | // Add source dir 390 | sprintf "-sourcedirs:\"%s\"" sourceDirs 391 | // Ignore test assemblies 392 | sprintf "-assemblyfilters:\"%s\"" "-*.Tests" 393 | // Generate HTML and Cobertura reports 394 | sprintf "-reporttypes:%s" "Html;Cobertura" 395 | ] 396 | 397 | let args = independentArgs |> String.concat " " 398 | 399 | dotnet.reportgenerator id args 400 | 401 | let showCoverageReport _ = 402 | failOnCIBuild () 403 | 404 | coverageReportDir "index.html" 405 | |> Command.ShellCommand 406 | |> CreateProcess.fromCommand 407 | |> Proc.start 408 | |> ignore 409 | 410 | 411 | let watchTests _ = 412 | !!testsGlob 413 | |> Seq.map (fun proj -> 414 | fun () -> 415 | dotnet.watch 416 | (fun opt -> 417 | opt 418 | |> DotNet.Options.withWorkingDirectory (IO.Path.GetDirectoryName proj) 419 | ) 420 | "test" 421 | "" 422 | |> ignore 423 | ) 424 | |> Seq.iter (invokeAsync >> Async.Catch >> Async.Ignore >> Async.Start) 425 | 426 | printfn "Press Ctrl+C (or Ctrl+Break) to stop..." 427 | 428 | let cancelEvent = 429 | Console.CancelKeyPress 430 | |> Async.AwaitEvent 431 | |> Async.RunSynchronously 432 | 433 | cancelEvent.Cancel <- true 434 | 435 | let generateAssemblyInfo _ = 436 | 437 | let (|Fsproj|Csproj|Vbproj|) (projFileName : string) = 438 | match projFileName with 439 | | f when f.EndsWith ("fsproj") -> Fsproj 440 | | f when f.EndsWith ("csproj") -> Csproj 441 | | f when f.EndsWith ("vbproj") -> Vbproj 442 | | _ -> failwith (sprintf "Project file %s not supported. Unknown project type." projFileName) 443 | 444 | let releaseChannel = 445 | match latestEntry.SemVer.PreRelease with 446 | | Some pr -> pr.Name 447 | | _ -> "release" 448 | 449 | let getAssemblyInfoAttributes projectName = [ 450 | AssemblyInfo.Title (projectName) 451 | AssemblyInfo.Product productName 452 | AssemblyInfo.Version latestEntry.AssemblyVersion 453 | AssemblyInfo.Metadata ("ReleaseDate", latestEntry.Date.Value.ToString ("o")) 454 | AssemblyInfo.FileVersion latestEntry.AssemblyVersion 455 | AssemblyInfo.InformationalVersion latestEntry.AssemblyVersion 456 | AssemblyInfo.Metadata ("ReleaseChannel", releaseChannel) 457 | AssemblyInfo.Metadata ("GitHash", Git.Information.getCurrentSHA1 (null)) 458 | ] 459 | 460 | let getProjectDetails (projectPath : string) = 461 | let projectName = IO.Path.GetFileNameWithoutExtension (projectPath) 462 | 463 | (projectPath, projectName, IO.Path.GetDirectoryName (projectPath), (getAssemblyInfoAttributes projectName)) 464 | 465 | !!srcGlob 466 | |> Seq.map getProjectDetails 467 | |> Seq.iter (fun (projFileName, _, folderName, attributes) -> 468 | match projFileName with 469 | | Fsproj -> AssemblyInfoFile.createFSharp (folderName "AssemblyInfo.fs") attributes 470 | | Csproj -> AssemblyInfoFile.createCSharp ((folderName "Properties") "AssemblyInfo.cs") attributes 471 | | Vbproj -> AssemblyInfoFile.createVisualBasic ((folderName "My Project") "AssemblyInfo.vb") attributes 472 | ) 473 | 474 | let dotnetPack ctx = 475 | // Get release notes with properly-linked version number 476 | let releaseNotes = Changelog.mkReleaseNotes changelog latestEntry gitHubRepoUrl 477 | 478 | let args = [ getPackageVersionProperty (isPublishToGitHub ctx); $"/p:PackageReleaseNotes=\"{releaseNotes}\"" ] 479 | 480 | DotNet.pack 481 | (fun c -> { 482 | c with 483 | MSBuildParams = disableBinLog c.MSBuildParams 484 | Configuration = configuration (ctx.Context.AllExecutingTargets) 485 | OutputPath = Some distDir 486 | Common = c.Common |> DotNet.Options.withAdditionalArgs args 487 | }) 488 | sln 489 | 490 | let sourceLinkTest _ = 491 | !!distGlob 492 | |> Seq.iter (fun nupkg -> dotnet.sourcelink id $"test %s{nupkg}") 493 | 494 | type PushSource = 495 | | NuGet 496 | | GitHub 497 | 498 | let publishTo (source : PushSource) _ = 499 | allPublishChecks () 500 | 501 | distGlob 502 | |> DotNet.nugetPush (fun o -> { 503 | o with 504 | Common = { 505 | o.Common with 506 | WorkingDirectory = "dist" 507 | CustomParams = Some "--skip-duplicate" 508 | } 509 | PushParams = { 510 | o.PushParams with 511 | // TODO: Uncomment when migrated to F# 9 512 | //NoSymbols = source.IsGitHub 513 | Source = 514 | match source with 515 | | NuGet -> Some "nuget.org" 516 | | GitHub -> Some "github.com" 517 | ApiKey = 518 | match source with 519 | | NuGet -> nugetToken 520 | | GitHub -> githubToken 521 | } 522 | }) 523 | 524 | let gitRelease _ = 525 | allReleaseChecks () 526 | 527 | let releaseNotesGitCommitFormat = latestEntry.ToString () 528 | 529 | Git.Staging.stageFile "" (rootDirectory "CHANGELOG.md") 530 | |> ignore 531 | 532 | !!(rootDirectory "src/**/AssemblyInfo.fs") 533 | ++ (rootDirectory "tests/**/AssemblyInfo.fs") 534 | |> Seq.iter (Git.Staging.stageFile "" >> ignore) 535 | 536 | let msg = $"Bump version to `%s{latestEntry.NuGetVersion}`\n\n%s{releaseNotesGitCommitFormat}" 537 | 538 | Git.Commit.exec "" msg 539 | 540 | Target.deactivateBuildFailure "RevertChangelog" 541 | 542 | Git.Branches.push "" 543 | 544 | let tag = Changelog.tagFromVersionNumber latestEntry.NuGetVersion 545 | 546 | Git.Branches.tag "" tag 547 | Git.Branches.pushTag "" "origin" tag 548 | 549 | let githubRelease _ = 550 | allPublishChecks () 551 | 552 | let token = 553 | match githubToken with 554 | | Some s -> s 555 | | _ -> failwith "please set the `GITHUB_TOKEN` environment variable to a github personal access token with repo access." 556 | 557 | let files = !!distGlob 558 | // Get release notes with properly-linked version number 559 | let releaseNotes = Changelog.mkReleaseNotes changelog latestEntry gitHubRepoUrl 560 | 561 | GitHub.createClientWithToken token 562 | |> GitHub.draftNewRelease 563 | gitOwner 564 | gitRepoName 565 | (Changelog.tagFromVersionNumber latestEntry.NuGetVersion) 566 | (latestEntry.SemVer.PreRelease <> None) 567 | (releaseNotes |> Seq.singleton) 568 | |> GitHub.uploadFiles files 569 | |> GitHub.publishDraft 570 | |> Async.RunSynchronously 571 | 572 | let formatCode _ = 573 | let result = dotnet.fantomas $"{rootDirectory}" 574 | 575 | if not result.OK then 576 | printfn "Errors while formatting all files: %A" result.Messages 577 | 578 | let checkFormatCode ctx = 579 | let result = dotnet.fantomas $"{rootDirectory} --check" 580 | 581 | if result.ExitCode = 0 then 582 | Trace.log "No files need formatting" 583 | elif result.ExitCode = 99 then 584 | failwith "Some files need formatting, check output for more info" 585 | else 586 | Trace.logf "Errors while formatting: %A" result.Errors 587 | 588 | 589 | let cleanDocsCache _ = DocsTool.cleanDocsCache () 590 | 591 | let buildDocs ctx = 592 | let configuration = configuration (ctx.Context.AllExecutingTargets) 593 | DocsTool.build (string configuration) 594 | 595 | let watchDocs ctx = 596 | let configuration = configuration (ctx.Context.AllExecutingTargets) 597 | DocsTool.watch (string configuration) 598 | 599 | 600 | let initTargets (ctx : Context.FakeExecutionContext) = 601 | BuildServer.install [ GitHubActions.Installer ] 602 | 603 | let isPublishToGitHub = 604 | ctx.Arguments 605 | |> Seq.pairwise 606 | |> Seq.exists (fun (arg, value) -> 607 | (String.Equals (arg, "-t", StringComparison.OrdinalIgnoreCase) 608 | || String.Equals (arg, "--target", StringComparison.OrdinalIgnoreCase)) 609 | && String.Equals (value, "PublishToGitHub", StringComparison.OrdinalIgnoreCase) 610 | ) 611 | 612 | /// Defines a dependency - y is dependent on x. Finishes the chain. 613 | let (==>!) x y = x ==> y |> ignore 614 | 615 | /// Defines a soft dependency. x must run before y, if it is present, but y does not require x to be run. Finishes the chain. 616 | let (?=>!) x y = x ?=> y |> ignore 617 | //----------------------------------------------------------------------------- 618 | // Hide Secrets in Logger 619 | //----------------------------------------------------------------------------- 620 | Option.iter (TraceSecrets.register "") githubToken 621 | Option.iter (TraceSecrets.register "") nugetToken 622 | //----------------------------------------------------------------------------- 623 | // Target Declaration 624 | //----------------------------------------------------------------------------- 625 | 626 | Target.create "Clean" clean 627 | Target.create "DotnetRestore" dotnetRestore 628 | Target.create "DotnetToolRestore" dotnetToolRestore 629 | Target.create "UpdateChangelog" updateChangelog 630 | Target.createBuildFailure "RevertChangelog" revertChangelog // Do NOT put this in the dependency chain 631 | Target.createFinal "DeleteChangelogBackupFile" deleteChangelogBackupFile // Do NOT put this in the dependency chain 632 | Target.create "DotnetBuild" dotnetBuild 633 | Target.create "FSharpAnalyzers" fsharpAnalyzers 634 | Target.create "DotnetTest" dotnetTest 635 | Target.create "GenerateCoverageReport" generateCoverageReport 636 | Target.create "ShowCoverageReport" showCoverageReport 637 | Target.create "WatchTests" watchTests 638 | Target.create "GenerateAssemblyInfo" generateAssemblyInfo 639 | Target.create "DotnetPack" dotnetPack 640 | Target.create "SourceLinkTest" sourceLinkTest 641 | Target.create "PublishToNuGet" (publishTo NuGet) 642 | Target.create "PublishToGitHub" (publishTo GitHub) 643 | Target.create "GitRelease" gitRelease 644 | Target.create "GitHubRelease" githubRelease 645 | Target.create "FormatCode" formatCode 646 | Target.create "CheckFormatCode" checkFormatCode 647 | Target.create "Release" ignore // For local 648 | Target.create "Publish" ignore //For CI 649 | Target.create "CleanDocsCache" cleanDocsCache 650 | Target.create "BuildDocs" buildDocs 651 | Target.create "WatchDocs" watchDocs 652 | 653 | //----------------------------------------------------------------------------- 654 | // Target Dependencies 655 | //----------------------------------------------------------------------------- 656 | 657 | 658 | // Only call Clean if DotnetPack was in the call chain 659 | // Ensure Clean is called before DotnetRestore 660 | "Clean" ?=>! "DotnetRestore" 661 | 662 | "Clean" ==>! "DotnetPack" 663 | 664 | // Only call GenerateAssemblyInfo if GitRelease was in the call chain 665 | // Ensure GenerateAssemblyInfo is called after DotnetRestore and before DotnetBuild 666 | "DotnetRestore" ?=>! "GenerateAssemblyInfo" 667 | 668 | "GenerateAssemblyInfo" ?=>! "DotnetBuild" 669 | 670 | // Ensure UpdateChangelog is called after DotnetRestore 671 | "DotnetRestore" ?=>! "UpdateChangelog" 672 | 673 | "UpdateChangelog" ?=>! "GenerateAssemblyInfo" 674 | 675 | "CleanDocsCache" ==>! "BuildDocs" 676 | 677 | "DotnetBuild" ?=>! "BuildDocs" 678 | 679 | "DotnetBuild" ==>! "BuildDocs" 680 | 681 | 682 | "DotnetBuild" ==>! "WatchDocs" 683 | 684 | "DotnetTest" ==> "GenerateCoverageReport" 685 | ==>! "ShowCoverageReport" 686 | 687 | "UpdateChangelog" 688 | ==> "GenerateAssemblyInfo" 689 | ==> "GitRelease" 690 | ==>! "Release" 691 | 692 | 693 | "DotnetRestore" =?> ("CheckFormatCode", isCI.Value) 694 | ==> "DotnetBuild" 695 | ==> "DotnetTest" 696 | ==> "DotnetPack" 697 | ==> "PublishToNuGet" 698 | ==> "GitHubRelease" 699 | ==>! "Publish" 700 | 701 | "DotnetRestore" 702 | =?> ("CheckFormatCode", isCI.Value) 703 | =?> ("GenerateAssemblyInfo", isPublishToGitHub) 704 | ==> "DotnetBuild" 705 | ==> "DotnetTest" 706 | ==> "DotnetPack" 707 | ==>! "PublishToGitHub" 708 | 709 | "DotnetRestore" ==>! "WatchTests" 710 | 711 | //"DotnetToolRestore" ?=>! "DotnetRestore" 712 | "DotnetToolRestore" ==>! "BuildDocs" 713 | "DotnetToolRestore" ?=>! "CheckFormatCode" 714 | "DotnetToolRestore" ?=>! "FormatCode" 715 | 716 | //----------------------------------------------------------------------------- 717 | // Target Start 718 | //----------------------------------------------------------------------------- 719 | [] 720 | let main argv = 721 | 722 | let ctx = 723 | argv 724 | |> Array.toList 725 | |> Context.FakeExecutionContext.Create false "build.fsx" 726 | 727 | Context.setExecutionContext (Context.RuntimeContext.Fake ctx) 728 | initTargets ctx 729 | Target.runOrDefaultWithArguments "DotnetPack" 730 | 731 | 0 // return an integer exit code 732 | -------------------------------------------------------------------------------- /build/build.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 3390;$(WarnOn) 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /docsSrc/Explanations/Background.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Background 3 | category: Explanations 4 | categoryindex: 3 5 | index: 1 6 | --- 7 | 8 | # Background 9 | 10 | ## System.Collections.Immutable and F# 11 | 12 | [System.Collections.Immutable](https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable) is a high-performance .NET library providing a suite of immutable collection types, such as arrays, lists, stacks, queues, dictionaries, and sets. These collections are designed for scenarios where data structures need to be shared safely across threads or require non-destructive updates, making them ideal for functional programming patterns. 13 | 14 | While F# has its own built-in immutable collections, System.Collections.Immutable collections are engineered for performance and scalability, especially in concurrent and multi-threaded environments. They use advanced algorithms to minimize memory allocations and maximize efficiency when creating modified copies of collections. 15 | 16 | **FSharp.Collections.Immutable** provides idiomatic F# bindings for these .NET collections, allowing F# developers to leverage their performance and safety benefits with a familiar F#-style API. 17 | 18 | ## Why Use System.Collections.Immutable in F#? 19 | 20 | - **Performance**: Optimized for fast structural sharing and minimal memory overhead compared to standard F# collections in certain scenarios. 21 | - **Thread Safety**: Immutable by design, making them safe for concurrent access without locks. 22 | - **Rich API**: Feature-rich and consistent with .NET ecosystem standards. 23 | - **Interoperability**: Seamless integration with C# and other .NET languages. 24 | 25 | ## Available Collections 26 | 27 | - `FlatList` (`ImmutableArray`) 28 | - `ImmutableList` 29 | - `Stack` (`ImmutableStack`) 30 | - `Queue` (`ImmutableQueue`) 31 | - `HashMap` (`ImmutableDictionary`) 32 | - `SortedMap` (`ImmutableSortedDictionary`) 33 | - `HashSet` (`ImmutableHashSet`) 34 | - `SortedSet` (`ImmutableSortedSet`) 35 | - `IIndexedSeq` (`IReadOnlyList`) 36 | 37 | --- 38 | 39 | FSharp.Collections.Immutable enables F# developers to use these performant, thread-safe, and feature-rich immutable collections in a natural and idiomatic way. 40 | -------------------------------------------------------------------------------- /docsSrc/How_Tos/Doing_A_Thing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How To do a first thing 3 | category: How To Guides 4 | categoryindex: 2 5 | index: 1 6 | --- 7 | 8 | # How To do a first thing 9 | 10 | The best way to use IObservable is to use one .Subscribe()-method which will take function parameters (which will be "injected" to the right place). 11 | 12 | Use Rx (or R3) when you need async events to communicate with each other, e.g.: 13 | - Events, WebServices, Threads, Timers, AutoComplete, Drag & Drop, ... 14 | 15 | -------------------------------------------------------------------------------- /docsSrc/How_Tos/Doing_Another_Thing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How To do a second thing 3 | category: How To Guides 4 | categoryindex: 2 5 | index: 2 6 | --- 7 | 8 | # How To do a second thing 9 | 10 | -------------------------------------------------------------------------------- /docsSrc/Tutorials/Getting_Started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | category: Tutorials 4 | categoryindex: 1 5 | index: 1 6 | --- 7 | 8 | # Getting Started 9 | 10 | ## Installation 11 | 12 | First, add the NuGet package to your project: 13 | ``` 14 | dotnet add package FSharp.Collections.Immutable 15 | ``` 16 | ## Basic Setup 17 | 18 | Here's a minimal example to get started: 19 | ``` F# 20 | open FSharp.Collections.Immutable 21 | ``` 22 | 23 | ``` F# 24 | // Create an immutable list 25 | let list1 = ImmutableList.ofSeq [1; 2; 3] 26 | 27 | // Create an immutable hash set 28 | let set1 = HashSet.ofSeq ["a"; "b"] 29 | 30 | // Create an immutable dictionary 31 | let dict1 = HashMap.ofSeq [KeyValuePair ("key1", 42); KeyValuePair ("key2", 100)] 32 | 33 | 34 | printfn "List: %A" (list1 |> Seq.toList) 35 | printfn "Set: %A" (set1 |> Seq.toList) 36 | printfn "Dict: %A" (dict1 |> Seq.toList) 37 | ## Next Steps 38 | 39 | - Check out the [How-To Guides](../How_Tos/Doing_A_Thing.html) for common scenarios 40 | - Read the [Background](../Explanations/Background.html) for deeper understanding 41 | - Browse the [API Reference](../reference/index.html) for detailed documentation 42 | -------------------------------------------------------------------------------- /docsSrc/_menu-item_template.html: -------------------------------------------------------------------------------- 1 |
  • {{fsdocs-menu-item-content}}
  • -------------------------------------------------------------------------------- /docsSrc/_menu_template.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docsSrc/_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{fsdocs-page-title}} 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 25 | 26 | {{fsdocs-watch-script}} 27 | 28 | 29 | 30 | 125 | 126 | 136 |
    137 | {{fsdocs-content}} 138 | {{fsdocs-tooltips}} 139 |
    140 | 141 | 142 | 144 | 147 | 148 | 149 | 151 | 152 | 155 | 156 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /docsSrc/content/fsdocs-custom.css: -------------------------------------------------------------------------------- 1 | .fsharp-icon-logo { 2 | width: 25px; 3 | margin-top: -2px; 4 | -webkit-filter: grayscale(100%) brightness(1) invert(1); /* Safari 6.0 - 9.0 */ 5 | filter: grayscale(100%) brightness(1) invert(1); 6 | } 7 | 8 | 9 | body .navbar .dropdown-menu .active .bi { 10 | display: block !important; 11 | } 12 | 13 | nav.navbar .dropdown-item img.fsharp-icon-logo { 14 | margin-right: 0px; 15 | } 16 | -------------------------------------------------------------------------------- /docsSrc/content/fsdocs-dark.css: -------------------------------------------------------------------------------- 1 | @import url('https://raw.githubusercontent.com/tonsky/FiraCode/fixed/distr/fira_code.css'); 2 | @import url('https://fonts.googleapis.com/css2?family=Hind+Vadodara&family=Roboto+Mono&display=swap'); 3 | /*-------------------------------------------------------------------------- 4 | Formatting for page & standard document content 5 | /*--------------------------------------------------------------------------*/ 6 | 7 | :root { 8 | --fsdocs-text-color:#d1d1d1; 9 | --fsdocs-pre-border-color: #000000; 10 | --fsdocs-pre-border-color-top: #070707; 11 | --fsdocs-pre-background-color: #1E1E1E; 12 | --fsdocs-pre-color: #e2e2e2; 13 | --fsdocs-table-pre-background-color: #1d1d1d; 14 | --fsdocs-table-pre-color: #c9c9c9; 15 | 16 | --fsdocs-code-strings-color: #ea9a75; 17 | --fsdocs-code-printf-color: #E0C57F; 18 | --fsdocs-code-escaped-color: #EA8675; 19 | --fsdocs-code-identifiers-color: var(--fsdocs-text-color); 20 | --fsdocs-code-module-color: #43AEC6; 21 | --fsdocs-code-reference-color: #6a8dd8; 22 | --fsdocs-code-value-color: #43AEC6; 23 | --fsdocs-code-interface-color: #43AEC6; 24 | --fsdocs-code-typearg-color: #43AEC6; 25 | --fsdocs-code-disposable-color: #2f798a; 26 | --fsdocs-code-property-color: #43AEC6; 27 | --fsdocs-code-punctuation-color: #43AEC6; 28 | --fsdocs-code-punctuation2-color: #e1e1e1; 29 | --fsdocs-code-function-color: #e1e1e1; 30 | --fsdocs-code-function2-color: #43AEC6; 31 | --fsdocs-code-activepattern-color: #4ec9b0; 32 | --fsdocs-code-unioncase-color: #4ec9b0; 33 | --fsdocs-code-enumeration-color: #4ec9b0; 34 | --fsdocs-code-keywords-color: #2248c4; 35 | --fsdocs-code-comment-color: #329215; 36 | --fsdocs-code-operators-color: #af75c1; 37 | --fsdocs-code-numbers-color: #96C71D; 38 | --fsdocs-code-linenumbers-color: #80b0b0; 39 | --fsdocs-code-mutable-color: #997f0c; 40 | --fsdocs-code-inactive-color: #808080; 41 | --fsdocs-code-preprocessor-color: #af75c1; 42 | --fsdocs-code-fsioutput-color: #808080; 43 | --fsdocs-code-tooltip-color: #d1d1d1; 44 | } 45 | 46 | 47 | .fsdocs-source-link img { 48 | -webkit-filter: grayscale(100%) brightness(1) invert(1); /* Safari 6.0 - 9.0 */ 49 | filter: grayscale(100%) brightness(1) invert(1); 50 | } -------------------------------------------------------------------------------- /docsSrc/content/fsdocs-light.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Hind+Vadodara&family=Roboto+Mono&display=swap'); 2 | /*-------------------------------------------------------------------------- 3 | Formatting for page & standard document content 4 | /*--------------------------------------------------------------------------*/ 5 | 6 | :root { 7 | --fsdocs-text-color:#262626; 8 | --fsdocs-pre-border-color: #d8d8d8; 9 | --fsdocs-pre-border-color-top: #e3e3e3; 10 | --fsdocs-pre-background-color: #f3f4f7; 11 | --fsdocs-pre-color: #8e0e2b; 12 | --fsdocs-table-pre-background-color: #fff7ed; 13 | --fsdocs-table-pre-color: #837b79; 14 | 15 | --fsdocs-code-strings-color: #dd1144; 16 | --fsdocs-code-printf-color: #E0C57F; 17 | --fsdocs-code-escaped-color: #EA8675; 18 | --fsdocs-code-identifiers-color: var(--fsdocs-text-color); 19 | --fsdocs-code-module-color: #009999; 20 | --fsdocs-code-reference-color: #4974D1; 21 | --fsdocs-code-value-color: #43AEC6; 22 | --fsdocs-code-interface-color: #43AEC6; 23 | --fsdocs-code-typearg-color: #43AEC6; 24 | --fsdocs-code-disposable-color: #43AEC6; 25 | --fsdocs-code-property-color: #43AEC6; 26 | --fsdocs-code-punctuation-color: #43AEC6; 27 | --fsdocs-code-punctuation2-color: #var(--fsdocs-text-color); 28 | --fsdocs-code-function-color: #e1e1e1; 29 | --fsdocs-code-function2-color: #990000; 30 | --fsdocs-code-activepattern-color: #4ec9b0; 31 | --fsdocs-code-unioncase-color: #4ec9b0; 32 | --fsdocs-code-enumeration-color: #4ec9b0; 33 | --fsdocs-code-keywords-color: #b68015; 34 | --fsdocs-code-comment-color: #808080; 35 | --fsdocs-code-operators-color: #af75c1; 36 | --fsdocs-code-numbers-color: #009999; 37 | --fsdocs-code-linenumbers-color: #80b0b0; 38 | --fsdocs-code-mutable-color: #d1d1d1; 39 | --fsdocs-code-inactive-color: #808080; 40 | --fsdocs-code-preprocessor-color: #af75c1; 41 | --fsdocs-code-fsioutput-color: #808080; 42 | --fsdocs-code-tooltip-color: #d1d1d1; 43 | } 44 | -------------------------------------------------------------------------------- /docsSrc/content/fsdocs-main.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Hind+Vadodara&family=Roboto+Mono&display=swap'); 2 | /*-------------------------------------------------------------------------- 3 | Formatting for page & standard document content 4 | /*--------------------------------------------------------------------------*/ 5 | 6 | body { 7 | font-family: 'Hind Vadodara', sans-serif; 8 | /* padding-top: 0px; 9 | padding-bottom: 40px; 10 | */ 11 | } 12 | 13 | blockquote { 14 | margin: 0 1em 0 0.25em; 15 | margin-top: 0px; 16 | margin-right: 1em; 17 | margin-bottom: 0px; 18 | margin-left: 0.25em; 19 | padding: 0 .75em 0 1em; 20 | border-left: 1px solid #777; 21 | border-right: 0px solid #777; 22 | } 23 | 24 | /* Format the heading - nicer spacing etc. */ 25 | .masthead { 26 | overflow: hidden; 27 | } 28 | 29 | .masthead .muted a { 30 | text-decoration: none; 31 | color: #999999; 32 | } 33 | 34 | .masthead ul, .masthead li { 35 | margin-bottom: 0px; 36 | } 37 | 38 | .masthead .nav li { 39 | margin-top: 15px; 40 | font-size: 110%; 41 | } 42 | 43 | .masthead h3 { 44 | margin-top: 15px; 45 | margin-bottom: 5px; 46 | font-size: 170%; 47 | } 48 | 49 | /*-------------------------------------------------------------------------- 50 | Formatting fsdocs-content 51 | /*--------------------------------------------------------------------------*/ 52 | 53 | /* Change font sizes for headings etc. */ 54 | #fsdocs-content h1 { 55 | margin: 30px 0px 15px 0px; 56 | /* font-weight: 400; */ 57 | font-size: 2rem; 58 | letter-spacing: 1.78px; 59 | line-height: 2.5rem; 60 | font-weight: 400; 61 | } 62 | 63 | #fsdocs-content h2 { 64 | font-size: 1.6rem; 65 | margin: 20px 0px 10px 0px; 66 | font-weight: 400; 67 | } 68 | 69 | #fsdocs-content h3 { 70 | font-size: 1.2rem; 71 | margin: 15px 0px 10px 0px; 72 | font-weight: 400; 73 | } 74 | 75 | #fsdocs-content hr { 76 | margin: 0px 0px 20px 0px; 77 | } 78 | 79 | #fsdocs-content li { 80 | font-size: 1.0rem; 81 | line-height: 1.375rem; 82 | letter-spacing: 0.01px; 83 | font-weight: 500; 84 | margin: 0px 0px 15px 0px; 85 | } 86 | 87 | #fsdocs-content p { 88 | font-size: 1.0rem; 89 | line-height: 1.375rem; 90 | letter-spacing: 0.01px; 91 | font-weight: 500; 92 | color: var(--fsdocs-text-color);; 93 | } 94 | 95 | #fsdocs-content a:not(.btn) { 96 | color: #4974D1; 97 | } 98 | /* remove the default bootstrap bold on dt elements */ 99 | #fsdocs-content dt { 100 | font-weight: normal; 101 | } 102 | 103 | 104 | 105 | /*-------------------------------------------------------------------------- 106 | Formatting tables in fsdocs-content, using learn.microsoft.com tables 107 | /*--------------------------------------------------------------------------*/ 108 | 109 | #fsdocs-content .table { 110 | table-layout: auto; 111 | width: 100%; 112 | font-size: 0.875rem; 113 | } 114 | 115 | #fsdocs-content .table caption { 116 | font-size: 0.8rem; 117 | font-weight: 600; 118 | letter-spacing: 2px; 119 | text-transform: uppercase; 120 | padding: 1.125rem; 121 | border-width: 0 0 1px; 122 | border-style: solid; 123 | border-color: #e3e3e3; 124 | text-align: right; 125 | } 126 | 127 | #fsdocs-content .table td, 128 | #fsdocs-content .table th { 129 | display: table-cell; 130 | word-wrap: break-word; 131 | padding: 0.75rem 1rem 0.75rem 0rem; 132 | line-height: 1.5; 133 | vertical-align: top; 134 | border-top: 1px solid #e3e3e3; 135 | border-right: 0; 136 | border-left: 0; 137 | border-bottom: 0; 138 | border-style: solid; 139 | } 140 | 141 | /* suppress the top line on inner lists such as tables of exceptions */ 142 | #fsdocs-content .table .fsdocs-exception-list td, 143 | #fsdocs-content .table .fsdocs-exception-list th { 144 | border-top: 0 145 | } 146 | 147 | #fsdocs-content .table td p:first-child, 148 | #fsdocs-content .table th p:first-child { 149 | margin-top: 0; 150 | } 151 | 152 | #fsdocs-content .table td.nowrap, 153 | #fsdocs-content .table th.nowrap { 154 | white-space: nowrap; 155 | } 156 | 157 | #fsdocs-content .table td.is-narrow, 158 | #fsdocs-content .table th.is-narrow { 159 | width: 15%; 160 | } 161 | 162 | #fsdocs-content .table th:not([scope='row']) { 163 | border-top: 0; 164 | border-bottom: 1px; 165 | } 166 | 167 | #fsdocs-content .table > caption + thead > tr:first-child > td, 168 | #fsdocs-content .table > colgroup + thead > tr:first-child > td, 169 | #fsdocs-content .table > thead:first-child > tr:first-child > td { 170 | border-top: 0; 171 | } 172 | 173 | #fsdocs-content .table table-striped > tbody > tr:nth-of-type(odd) { 174 | background-color: var(--box-shadow-light); 175 | } 176 | 177 | #fsdocs-content .table.min { 178 | width: unset; 179 | } 180 | 181 | #fsdocs-content .table.is-left-aligned td:first-child, 182 | #fsdocs-content .table.is-left-aligned th:first-child { 183 | padding-left: 0; 184 | } 185 | 186 | #fsdocs-content .table.is-left-aligned td:first-child a, 187 | #fsdocs-content .table.is-left-aligned th:first-child a { 188 | outline-offset: -0.125rem; 189 | } 190 | 191 | @media screen and (max-width: 767px), screen and (min-resolution: 120dpi) and (max-width: 767.9px) { 192 | #fsdocs-content .table.is-stacked-mobile td:nth-child(1) { 193 | display: block; 194 | width: 100%; 195 | padding: 1rem 0; 196 | } 197 | 198 | #fsdocs-content .table.is-stacked-mobile td:not(:nth-child(1)) { 199 | display: block; 200 | border-width: 0; 201 | padding: 0 0 1rem; 202 | } 203 | } 204 | 205 | #fsdocs-content .table.has-inner-borders th, 206 | #fsdocs-content .table.has-inner-borders td { 207 | border-right: 1px solid #e3e3e3; 208 | } 209 | 210 | #fsdocs-content .table.has-inner-borders th:last-child, 211 | #fsdocs-content .table.has-inner-borders td:last-child { 212 | border-right: none; 213 | } 214 | 215 | .fsdocs-entity-list .fsdocs-entity-name { 216 | width: 25%; 217 | font-weight: bold; 218 | } 219 | 220 | .fsdocs-member-list .fsdocs-member-usage { 221 | width: 35%; 222 | } 223 | 224 | /*-------------------------------------------------------------------------- 225 | Formatting xmldoc sections in fsdocs-content 226 | /*--------------------------------------------------------------------------*/ 227 | 228 | .fsdocs-xmldoc, .fsdocs-entity-xmldoc, .fsdocs-member-xmldoc { 229 | font-size: 1.0rem; 230 | line-height: 1.375rem; 231 | letter-spacing: 0.01px; 232 | font-weight: 500; 233 | color: var(--fsdocs-text-color);; 234 | } 235 | 236 | .fsdocs-xmldoc h1 { 237 | font-size: 1.2rem; 238 | margin: 10px 0px 0px 0px; 239 | } 240 | 241 | .fsdocs-xmldoc h2 { 242 | font-size: 1.2rem; 243 | margin: 10px 0px 0px 0px; 244 | } 245 | 246 | .fsdocs-xmldoc h3 { 247 | font-size: 1.1rem; 248 | margin: 10px 0px 0px 0px; 249 | } 250 | 251 | /* #fsdocs-nav .searchbox { 252 | margin-top: 30px; 253 | margin-bottom: 30px; 254 | } */ 255 | 256 | #fsdocs-nav img.logo{ 257 | width:90%; 258 | /* height:140px; */ 259 | /* margin:10px 0px 0px 20px; */ 260 | margin-top:40px; 261 | border-style:none; 262 | } 263 | 264 | #fsdocs-nav input{ 265 | /* margin-left: 20px; */ 266 | margin-right: 20px; 267 | margin-top: 20px; 268 | margin-bottom: 20px; 269 | width: 93%; 270 | -webkit-border-radius: 0; 271 | border-radius: 0; 272 | } 273 | 274 | #fsdocs-nav { 275 | /* margin-left: -5px; */ 276 | /* width: 90%; */ 277 | font-size:0.95rem; 278 | } 279 | 280 | #fsdocs-nav li.nav-header{ 281 | /* margin-left: -5px; */ 282 | /* width: 90%; */ 283 | padding-left: 0; 284 | color: var(--fsdocs-text-color);; 285 | text-transform: none; 286 | font-size:16px; 287 | margin-top: 9px; 288 | font-weight: bold; 289 | } 290 | 291 | #fsdocs-nav a{ 292 | padding-left: 0; 293 | color: #6c6c6d; 294 | /* margin-left: 5px; */ 295 | /* width: 90%; */ 296 | } 297 | 298 | /*-------------------------------------------------------------------------- 299 | Formatting pre and code sections in fsdocs-content (code highlighting is 300 | further below) 301 | /*--------------------------------------------------------------------------*/ 302 | 303 | #fsdocs-content code { 304 | /* font-size: 0.83rem; */ 305 | font: 0.85rem 'Roboto Mono', monospace; 306 | background-color: #f7f7f900; 307 | border: 0px; 308 | padding: 0px; 309 | /* word-wrap: break-word; */ 310 | /* white-space: pre; */ 311 | } 312 | 313 | /* omitted */ 314 | #fsdocs-content span.omitted { 315 | background: #3c4e52; 316 | border-radius: 5px; 317 | color: #808080; 318 | padding: 0px 0px 1px 0px; 319 | } 320 | 321 | #fsdocs-content pre .fssnip code { 322 | font: 0.86rem 'Roboto Mono', monospace; 323 | } 324 | 325 | #fsdocs-content table.pre, 326 | #fsdocs-content pre.fssnip, 327 | #fsdocs-content pre { 328 | line-height: 13pt; 329 | border: 0px solid var(--fsdocs-pre-border-color); 330 | border-top: 0px solid var(--fsdocs-pre-border-color-top); 331 | border-collapse: separate; 332 | white-space: pre; 333 | font: 0.86rem 'Roboto Mono', monospace; 334 | width: 100%; 335 | margin: 10px 0px 20px 0px; 336 | background-color: var(--fsdocs-pre-background-color); 337 | padding: 10px; 338 | border-radius: 5px; 339 | color: var(--fsdocs-pre-color); 340 | max-width: none; 341 | box-sizing: border-box; 342 | } 343 | 344 | #fsdocs-content pre.fssnip code { 345 | font: 0.86rem 'Roboto Mono', monospace; 346 | font-weight: 600; 347 | } 348 | 349 | #fsdocs-content table.pre { 350 | background-color: var(--fsdocs-table-pre-background-color);; 351 | } 352 | 353 | #fsdocs-content table.pre pre { 354 | padding: 0px; 355 | margin: 0px; 356 | border-radius: 0px; 357 | width: 100%; 358 | background-color: var(--fsdocs-table-pre-background-color); 359 | color: var(--fsdocs-table-pre-color); 360 | } 361 | 362 | #fsdocs-content table.pre td { 363 | padding: 0px; 364 | white-space: normal; 365 | margin: 0px; 366 | width: 100%; 367 | } 368 | 369 | #fsdocs-content table.pre td.lines { 370 | width: 30px; 371 | } 372 | 373 | 374 | #fsdocs-content pre { 375 | word-wrap: inherit; 376 | } 377 | 378 | .fsdocs-example-header { 379 | font-size: 1.0rem; 380 | line-height: 1.375rem; 381 | letter-spacing: 0.01px; 382 | font-weight: 700; 383 | color: var(--fsdocs-text-color);; 384 | } 385 | 386 | /*-------------------------------------------------------------------------- 387 | Formatting github source links 388 | /*--------------------------------------------------------------------------*/ 389 | 390 | .fsdocs-source-link { 391 | float: right; 392 | text-decoration: none; 393 | } 394 | 395 | .fsdocs-source-link img { 396 | border-style: none; 397 | margin-left: 10px; 398 | width: auto; 399 | height: 1.4em; 400 | } 401 | 402 | .fsdocs-source-link .hover { 403 | display: none; 404 | } 405 | 406 | .fsdocs-source-link:hover .hover { 407 | display: block; 408 | } 409 | 410 | .fsdocs-source-link .normal { 411 | display: block; 412 | } 413 | 414 | .fsdocs-source-link:hover .normal { 415 | display: none; 416 | } 417 | 418 | /*-------------------------------------------------------------------------- 419 | Formatting logo 420 | /*--------------------------------------------------------------------------*/ 421 | 422 | #fsdocs-logo { 423 | width:40px; 424 | height:40px; 425 | margin:10px 0px 0px 0px; 426 | border-style:none; 427 | } 428 | 429 | /*-------------------------------------------------------------------------- 430 | 431 | /*--------------------------------------------------------------------------*/ 432 | 433 | #fsdocs-content table.pre pre { 434 | padding: 0px; 435 | margin: 0px; 436 | border: none; 437 | } 438 | 439 | /*-------------------------------------------------------------------------- 440 | Remove formatting from links 441 | /*--------------------------------------------------------------------------*/ 442 | 443 | #fsdocs-content h1 a, 444 | #fsdocs-content h1 a:hover, 445 | #fsdocs-content h1 a:focus, 446 | #fsdocs-content h2 a, 447 | #fsdocs-content h2 a:hover, 448 | #fsdocs-content h2 a:focus, 449 | #fsdocs-content h3 a, 450 | #fsdocs-content h3 a:hover, 451 | #fsdocs-content h3 a:focus, 452 | #fsdocs-content h4 a, 453 | #fsdocs-content h4 a:hover, #fsdocs-content 454 | #fsdocs-content h4 a:focus, 455 | #fsdocs-content h5 a, 456 | #fsdocs-content h5 a:hover, 457 | #fsdocs-content h5 a:focus, 458 | #fsdocs-content h6 a, 459 | #fsdocs-content h6 a:hover, 460 | #fsdocs-content h6 a:focus { 461 | color: var(--fsdocs-text-color);; 462 | text-decoration: none; 463 | text-decoration-style: none; 464 | /* outline: none */ 465 | } 466 | 467 | /*-------------------------------------------------------------------------- 468 | Formatting for F# code snippets 469 | /*--------------------------------------------------------------------------*/ 470 | 471 | .fsdocs-param-name, 472 | .fsdocs-return-name, 473 | .fsdocs-param { 474 | font-weight: 900; 475 | font-size: 0.85rem; 476 | font-family: 'Roboto Mono', monospace; 477 | } 478 | /* strings --- and stlyes for other string related formats */ 479 | #fsdocs-content span.s { 480 | color: var(--fsdocs-code-strings-color); 481 | } 482 | /* printf formatters */ 483 | #fsdocs-content span.pf { 484 | color: var(--fsdocs-code-printf-color); 485 | } 486 | /* escaped chars */ 487 | #fsdocs-content span.e { 488 | color: var(--fsdocs-code-escaped-color); 489 | } 490 | 491 | /* identifiers --- and styles for more specific identifier types */ 492 | #fsdocs-content span.id { 493 | color: var(--fsdocs-identifiers-color);; 494 | } 495 | /* module */ 496 | #fsdocs-content span.m { 497 | color:var(--fsdocs-code-module-color); 498 | } 499 | /* reference type */ 500 | #fsdocs-content span.rt { 501 | color: var(--fsdocs-code-reference-color); 502 | } 503 | /* value type */ 504 | #fsdocs-content span.vt { 505 | color: var(--fsdocs-code-value-color); 506 | } 507 | /* interface */ 508 | #fsdocs-content span.if { 509 | color: var(--fsdocs-code-interface-color); 510 | } 511 | /* type argument */ 512 | #fsdocs-content span.ta { 513 | color: var(--fsdocs-code-typearg-color); 514 | } 515 | /* disposable */ 516 | #fsdocs-content span.d { 517 | color: var(--fsdocs-code-disposable-color); 518 | } 519 | /* property */ 520 | #fsdocs-content span.prop { 521 | color: var(--fsdocs-code-property-color); 522 | } 523 | /* punctuation */ 524 | #fsdocs-content span.p { 525 | color: var(--fsdocs-code-punctuation-color); 526 | } 527 | #fsdocs-content span.pn { 528 | color: var(--fsdocs-code-punctuation2-color); 529 | } 530 | /* function */ 531 | #fsdocs-content span.f { 532 | color: var(--fsdocs-code-function-color); 533 | } 534 | #fsdocs-content span.fn { 535 | color: var(--fsdocs-code-function2-color); 536 | } 537 | /* active pattern */ 538 | #fsdocs-content span.pat { 539 | color: var(--fsdocs-code-activepattern-color); 540 | } 541 | /* union case */ 542 | #fsdocs-content span.u { 543 | color: var(--fsdocs-code-unioncase-color); 544 | } 545 | /* enumeration */ 546 | #fsdocs-content span.e { 547 | color: var(--fsdocs-code-enumeration-color); 548 | } 549 | /* keywords */ 550 | #fsdocs-content span.k { 551 | color: var(--fsdocs-code-keywords-color); 552 | /* font-weight: bold; */ 553 | } 554 | /* comment */ 555 | #fsdocs-content span.c { 556 | color: var(--fsdocs-code-comment-color); 557 | font-weight: 400; 558 | font-style: italic; 559 | } 560 | /* operators */ 561 | #fsdocs-content span.o { 562 | color: var(--fsdocs-code-operators-color); 563 | } 564 | /* numbers */ 565 | #fsdocs-content span.n { 566 | color: var(--fsdocs-code-numbers-color); 567 | } 568 | /* line number */ 569 | #fsdocs-content span.l { 570 | color: var(--fsdocs-code-linenumbers-color); 571 | } 572 | /* mutable var or ref cell */ 573 | #fsdocs-content span.v { 574 | color: var(--fsdocs-code-mutable-color); 575 | font-weight: bold; 576 | } 577 | /* inactive code */ 578 | #fsdocs-content span.inactive { 579 | color: var(--fsdocs-code-inactive-color); 580 | } 581 | /* preprocessor */ 582 | #fsdocs-content span.prep { 583 | color: var(--fsdocs-code-preprocessor-color); 584 | } 585 | /* fsi output */ 586 | #fsdocs-content span.fsi { 587 | color: var(--fsdocs-code-fsioutput-color); 588 | } 589 | 590 | /* tool tip */ 591 | div.fsdocs-tip { 592 | background: #475b5f; 593 | border-radius: 4px; 594 | font: 0.85rem 'Roboto Mono', monospace; 595 | padding: 6px 8px 6px 8px; 596 | display: none; 597 | color: var(--fsdocs-code-tooltip-color); 598 | pointer-events: none; 599 | } 600 | 601 | div.fsdocs-tip code { 602 | color: var(--fsdocs-code-tooltip-color); 603 | font: 0.85rem 'Roboto Mono', monospace; 604 | } 605 | -------------------------------------------------------------------------------- /docsSrc/content/logo.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/FSharp.Collections.Immutable/e1178372738e73f16c9a4cd90760698c784406e0/docsSrc/content/logo.pdn -------------------------------------------------------------------------------- /docsSrc/content/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/FSharp.Collections.Immutable/e1178372738e73f16c9a4cd90760698c784406e0/docsSrc/content/logo.png -------------------------------------------------------------------------------- /docsSrc/content/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 48 | 49 | 50 | 51 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /docsSrc/content/navbar-fixed-left.css: -------------------------------------------------------------------------------- 1 | /* CSS for Bootstrap 5 Fixed Left Sidebar Navigation */ 2 | 3 | 4 | 5 | @media (min-width: 992px){ 6 | 7 | body { 8 | padding-left: 300px; 9 | padding-right: 60px; 10 | } 11 | 12 | #fsdocs-logo { 13 | width:140px; 14 | height:140px; 15 | margin:10px 0px 0px 0px; 16 | border-style:none; 17 | } 18 | 19 | 20 | nav.navbar { 21 | position: fixed; 22 | left: 0; 23 | width: 300px; 24 | bottom: 0; 25 | top: 0; 26 | overflow-y: auto; 27 | overflow-x: hidden; 28 | display: block; 29 | border-right: 1px solid #cecece; 30 | } 31 | 32 | nav.navbar>.container { 33 | flex-direction: column; 34 | padding: 0; 35 | } 36 | 37 | nav.navbar .navbar-nav { 38 | flex-direction: column; 39 | } 40 | nav.navbar .navbar-collapse { 41 | width: 100%; 42 | } 43 | 44 | nav.navbar .navbar-nav { 45 | width: 100%; 46 | } 47 | 48 | nav.navbar .navbar-nav .dropdown-menu { 49 | position: static; 50 | display: block; 51 | } 52 | 53 | nav.navbar .dropdown { 54 | margin-bottom: 5px; 55 | font-size: 14px; 56 | } 57 | 58 | nav.navbar .dropdown-item { 59 | white-space: normal; 60 | font-size: 14px; 61 | vertical-align: middle; 62 | } 63 | 64 | nav.navbar .dropdown-item img { 65 | margin-right: 5px; 66 | } 67 | 68 | nav.navbar .dropdown-toggle { 69 | cursor: default; 70 | } 71 | 72 | nav.navbar .dropdown-menu { 73 | border-radius: 0; 74 | border-left: 0; 75 | border-right: 0; 76 | } 77 | 78 | nav.navbar .dropdown-toggle:not(#bd-theme)::after { 79 | display: none; 80 | } 81 | 82 | .dropdown-menu[data-bs-popper] { 83 | top: auto; 84 | left: auto; 85 | margin-top: auto; 86 | } 87 | 88 | .nav-link:focus, .nav-link:hover { 89 | color: auto; 90 | } 91 | } -------------------------------------------------------------------------------- /docsSrc/content/theme-toggle.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/) 3 | * Copyright 2011-2022 The Bootstrap Authors 4 | * Licensed under the Creative Commons Attribution 3.0 Unported License. 5 | */ 6 | 7 | (() => { 8 | 'use strict' 9 | 10 | const storedTheme = localStorage.getItem('theme') 11 | 12 | const getPreferredTheme = () => { 13 | if (storedTheme) { 14 | return storedTheme 15 | } 16 | 17 | return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' 18 | } 19 | 20 | const setTheme = function (theme) { 21 | const fsdocsTheme = document.getElementById("fsdocs-theme") 22 | const re = /fsdocs-.*.css/ 23 | if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) { 24 | document.documentElement.setAttribute('data-bs-theme', 'dark') 25 | fsdocsTheme.setAttribute("href", fsdocsTheme.getAttribute("href").replace(re,"fsdocs-dark.css")) 26 | 27 | } else { 28 | document.documentElement.setAttribute('data-bs-theme', theme) 29 | 30 | fsdocsTheme.setAttribute("href", fsdocsTheme.getAttribute("href").replace(re,`fsdocs-${theme}.css`)) 31 | } 32 | } 33 | 34 | setTheme(getPreferredTheme()) 35 | 36 | const showActiveTheme = theme => { 37 | const activeThemeIcon = document.getElementById('theme-icon-active') 38 | const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`) 39 | const svgOfActiveBtn = btnToActive.querySelector('i').getAttribute('class') 40 | 41 | document.querySelectorAll('[data-bs-theme-value]').forEach(element => { 42 | element.classList.remove('active') 43 | }) 44 | 45 | btnToActive.classList.add('active') 46 | activeThemeIcon.setAttribute('class', svgOfActiveBtn) 47 | } 48 | 49 | window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { 50 | if (storedTheme !== 'light' || storedTheme !== 'dark') { 51 | setTheme(getPreferredTheme()) 52 | } 53 | }) 54 | 55 | window.addEventListener('DOMContentLoaded', () => { 56 | showActiveTheme(getPreferredTheme()) 57 | 58 | document.querySelectorAll('[data-bs-theme-value]') 59 | .forEach(toggle => { 60 | toggle.addEventListener('click', () => { 61 | const theme = toggle.getAttribute('data-bs-theme-value') 62 | localStorage.setItem('theme', theme) 63 | setTheme(theme) 64 | showActiveTheme(theme) 65 | }) 66 | }) 67 | }) 68 | })() -------------------------------------------------------------------------------- /docsSrc/index.md: -------------------------------------------------------------------------------- 1 | # FSharp.Collections.Immutable 2 | 3 | F# bindings for [System.Collections.Immutable](https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable). 4 | 5 | **FSharp.Collections.Immutable** provides idiomatic F# wrappers for the .NET immutable collections, making it easy to use persistent data structures in F# code. 6 | 7 | ## Key Features 8 | 9 | - `FlatList` (`ImmutableArray`) 10 | - `ImmutableList` 11 | - `Stack` (`ImmutableStack`) 12 | - `Queue` (`ImmutableQueue`) 13 | - `HashMap` (`ImmutableDictionary`) 14 | - `SortedMap` (`ImmutableSortedDictionary`) 15 | - `HashSet` (`ImmutableHashSet`) 16 | - `SortedSet` (`ImmutableSortedSet`) 17 | - `IIndexedSeq` (`IReadOnlyList`) 18 | 19 | --- 20 | 21 |
    22 |
    23 |
    24 |
    25 | The FSharp.Collections.Immutable library can be installed from NuGet: 26 |
    PM> Install-Package FSharp.Collections.Immutable
    27 |
    28 |
    29 |
    30 |
    31 | 32 | --- 33 | 34 |
    35 |
    36 |
    37 |
    38 |
    Tutorials
    39 |

    Step-by-step guide to get started with FSharp.Collections.Immutable.

    40 |
    41 | 44 |
    45 |
    46 |
    47 |
    48 |
    49 |
    How-To Guides
    50 |

    Guides you through the steps involved in addressing key problems and use-cases.

    51 |
    52 | 55 |
    56 |
    57 |
    58 |
    59 |
    60 |
    Explanations
    61 |

    Discusses key topics and concepts at a fairly high level and provide useful background information and explanation.

    62 |
    63 | 66 |
    67 |
    68 |
    69 |
    70 |
    71 |
    Api Reference
    72 |

    Contain technical reference for APIs.

    73 |
    74 | 77 |
    78 |
    79 |
    80 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.300", 4 | "rollForward": "latestMinor" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | $(AssemblyBaseName) 6 | true 7 | true 8 | True 9 | 10 | 11 | 12 | FSharp.Collections.Immutable 13 | FSharp.Collections.Immutable 14 | F# API for using Microsoft Azure Cosmos DB service via NoSQL API 15 | Provides extension methods for the FeedIterator and computation expressions to build operations 16 | 17 | 18 | 19 | true 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | all 39 | runtime; build; native; contentfiles; analyzers; buildtransitive 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/FSharp.Collections.Immutable/FlatList.fs: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | namespace global 3 | #else 4 | namespace FSharp.Collections.Immutable 5 | #endif 6 | 7 | // The FlatList name comes from a similar construct seen in the official F# source code 8 | type FlatList<'T> = System.Collections.Immutable.ImmutableArray<'T> 9 | 10 | // based on the F# Array module source 11 | [] 14 | module FlatList = 15 | 16 | type internal FlatListFactory = System.Collections.Immutable.ImmutableArray 17 | 18 | let inline internal checkNotDefault argName (list : FlatList<'T>) = 19 | if list.IsDefault then 20 | invalidArg argName "Uninstantiated ImmutableArray/FlatList" 21 | 22 | let inline internal check (list : FlatList<'T>) = checkNotDefault (nameof list) list 23 | 24 | ////////// Creating ////////// 25 | 26 | let inline empty<'T> : FlatList<_> = FlatListFactory.Create<'T> () 27 | let inline singleton<'T> (item : 'T) : FlatList<'T> = FlatListFactory.Create<'T> (item) 28 | 29 | let inline ofSeq source = FlatListFactory.CreateRange source 30 | let inline ofArray (source : _ array) = FlatListFactory.CreateRange source 31 | 32 | let inline toSeq (flatList : FlatList<_>) = flatList :> seq<_> 33 | 34 | let inline toArray (list : FlatList<_>) = 35 | check list 36 | Seq.toArray list 37 | 38 | ////////// Building ////////// 39 | 40 | let moveFromBuilder (builder : FlatList<_>.Builder) : FlatList<_> = 41 | checkNotNull (nameof builder) builder 42 | builder.MoveToImmutable () 43 | 44 | let ofBuilder (builder : FlatList<_>.Builder) : FlatList<_> = 45 | checkNotNull (nameof builder) builder 46 | builder.ToImmutable () 47 | 48 | let inline builder () : FlatList<'T>.Builder = FlatListFactory.CreateBuilder () 49 | let inline builderWith capacity : FlatList<'T>.Builder = FlatListFactory.CreateBuilder (capacity) 50 | 51 | let toBuilder list : FlatList<_>.Builder = 52 | check list 53 | list.ToBuilder () 54 | 55 | module Builder = 56 | let inline private check (builder : FlatList<'T>.Builder) = checkNotNull (nameof builder) builder 57 | 58 | let add item builder = 59 | check builder 60 | builder.Add (item) 61 | 62 | let inline internal indexNotFound () = raise <| System.Collections.Generic.KeyNotFoundException () 63 | 64 | let isEmpty (list : FlatList<_>) = list.IsEmpty 65 | let isDefault (list : FlatList<_>) = list.IsDefault 66 | let isDefaultOrEmpty (list : FlatList<_>) = list.IsDefaultOrEmpty 67 | 68 | ////////// IReadOnly* ////////// 69 | 70 | let length list = 71 | check list 72 | list.Length 73 | 74 | let item index list = 75 | check list 76 | list.[index] 77 | 78 | let append list1 list2 : FlatList<'T> = 79 | checkNotDefault (nameof list1) list1 80 | checkNotDefault (nameof list2) list2 81 | list1.AddRange (list2 : FlatList<_>) 82 | 83 | /// Searches for the specified object and returns the zero-based index of the first occurrence within the range 84 | /// of elements in the list that starts at the specified index and 85 | /// contains the specified number of elements. 86 | let indexRangeWith comparer index count item list = 87 | check list 88 | list.IndexOf (item, index, count, comparer) 89 | 90 | let indexRange index count item list = indexRangeWith HashIdentity.Structural index count item list 91 | let indexFromWith comparer index item list = indexRangeWith comparer index (length list - index) item 92 | let indexFrom index item list = indexFromWith HashIdentity.Structural index item list 93 | let indexWith comparer item list = indexFromWith comparer 0 item list 94 | let index item list = indexWith HashIdentity.Structural item list 95 | 96 | /// Searches for the specified object and returns the zero-based index of the last occurrence within the 97 | /// range of elements in the list that contains the specified number 98 | /// of elements and ends at the specified index. 99 | let lastIndexRangeWith comparer index count item list = 100 | check list 101 | list.LastIndexOf (item, index, count, comparer) 102 | 103 | let lastIndexRange index count item list = lastIndexRangeWith HashIdentity.Structural index count item list 104 | let lastIndexFromWith comparer index item list = lastIndexRangeWith comparer index (index + 1) item list 105 | let lastIndexFrom index item list = lastIndexFromWith HashIdentity.Structural index item list 106 | let lastIndexWith comparer item list = lastIndexFromWith comparer (length list - 1) item list 107 | let lastIndex item list = lastIndexWith HashIdentity.Structural item list 108 | 109 | /// Removes the specified objects from the list with the given comparer. 110 | let removeAllWith (comparer : System.Collections.Generic.IEqualityComparer<'T>) (items : 'T seq) list : FlatList<_> = 111 | check list 112 | list.RemoveRange (items, comparer) 113 | 114 | /// Removes the specified objects from the list. 115 | let removeAll items list = removeAllWith HashIdentity.Structural items list 116 | 117 | /// Removes all the elements that do not match the conditions defined by the specified predicate. 118 | let filter predicate list : FlatList<_> = 119 | check list 120 | System.Predicate (not << predicate) |> list.RemoveAll 121 | 122 | /// Removes all the elements that do not match the conditions defined by the specified predicate. 123 | let where predicate list = filter predicate list 124 | 125 | /// Removes a range of elements from the list. 126 | let removeRange index (count : int) list : FlatList<_> = 127 | check list 128 | list.RemoveRange (index, count) 129 | 130 | let blit source sourceIndex (destination : 'T[]) destinationIndex count = 131 | checkNotDefault (nameof source) source 132 | 133 | try 134 | source.CopyTo (sourceIndex, destination, destinationIndex, count) 135 | with exn -> 136 | raise exn // throw same exception with the correct stack trace. Update exception code 137 | 138 | let sortRangeWithComparer comparer index count list = 139 | check list 140 | list.Sort (index, count, comparer) 141 | 142 | let sortRangeWith comparer index count list = 143 | sortRangeWithComparer (ComparisonIdentity.FromFunction comparer) index count list 144 | 145 | let sortRange index count list = sortRangeWithComparer ComparisonIdentity.Structural index count list 146 | 147 | let sortWithComparer (comparer : System.Collections.Generic.IComparer<_>) list = 148 | check list 149 | list.Sort (comparer) 150 | 151 | let sortWith comparer list = sortWithComparer (ComparisonIdentity.FromFunction comparer) list 152 | 153 | let sort list = 154 | check list 155 | list.Sort () 156 | 157 | ////////// Loop-based ////////// 158 | 159 | let inline private builderWithLengthOf list = builderWith <| length list 160 | 161 | let init count initializer = 162 | if count < 0 then 163 | invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative 164 | 165 | let builder = builderWith count 166 | 167 | for i = 0 to count - 1 do 168 | builder.Add <| initializer i 169 | 170 | moveFromBuilder builder 171 | 172 | let rec private concatAddLengths (arrs : FlatList>) i acc = 173 | if i >= length arrs then 174 | acc 175 | else 176 | concatAddLengths arrs (i + 1) (acc + arrs.[i].Length) 177 | 178 | let concat (arrs : FlatList>) = // consider generalizing 179 | let result : FlatList<'T>.Builder = builderWith <| concatAddLengths arrs 0 0 180 | 181 | for i = 0 to length arrs - 1 do 182 | result.AddRange (arrs.[i] : FlatList<'T>) 183 | 184 | moveFromBuilder result 185 | 186 | let inline map mapping list = 187 | check list 188 | let builder = builderWithLengthOf list 189 | 190 | for i = 0 to length list - 1 do 191 | builder.Add (mapping list.[i]) 192 | 193 | moveFromBuilder builder 194 | 195 | let countBy projection list = 196 | check list 197 | // need struct box optimization 198 | let dict = new System.Collections.Generic.Dictionary<'Key, int> (HashIdentity.Structural) 199 | 200 | // Build the groupings 201 | for v in list do 202 | let key = projection v 203 | let mutable prev = Unchecked.defaultof<_> 204 | 205 | if dict.TryGetValue (key, &prev) then 206 | dict.[key] <- prev + 1 207 | else 208 | dict.[key] <- 1 209 | 210 | let res = builderWith dict.Count 211 | let mutable i = 0 212 | 213 | for group in dict do 214 | res.Add (group.Key, group.Value) 215 | i <- i + 1 216 | 217 | moveFromBuilder res 218 | 219 | let indexed list = 220 | check list 221 | let builder = builderWithLengthOf list 222 | 223 | for i = 0 to length list - 1 do 224 | builder.Add (i, list.[i]) 225 | 226 | moveFromBuilder builder 227 | 228 | let inline iter action list = 229 | check list 230 | 231 | for i = 0 to length list - 1 do 232 | action list.[i] 233 | 234 | let iter2 action list1 list2 = 235 | checkNotDefault (nameof list1) list1 236 | checkNotDefault (nameof list2) list2 237 | let f = OptimizedClosures.FSharpFunc<'T, 'U, unit>.Adapt (action) 238 | let len = length list1 239 | 240 | if len <> length list2 then 241 | invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths 242 | 243 | for i = 0 to len - 1 do 244 | f.Invoke (list1.[i], list2.[i]) 245 | 246 | let distinctBy projection (list : FlatList<'T>) = 247 | let builder : FlatList<'T>.Builder = builderWith <| length list 248 | let set = System.Collections.Generic.HashSet<'Key> (HashIdentity.Structural) 249 | let mutable outputIndex = 0 250 | 251 | for i = 0 to length list - 1 do 252 | let item = list.[i] 253 | 254 | if set.Add <| projection item then 255 | outputIndex <- outputIndex + 1 256 | Builder.add item builder 257 | 258 | ofBuilder builder 259 | 260 | let map2 mapping list1 list2 = 261 | checkNotDefault (nameof list1) list1 262 | checkNotDefault (nameof list2) list2 263 | let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (mapping) 264 | let len1 = list1.Length 265 | 266 | if len1 <> list2.Length then 267 | invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths 268 | 269 | let res = builderWith len1 270 | 271 | for i = 0 to len1 - 1 do 272 | res.Add <| f.Invoke (list1.[i], list2.[i]) 273 | 274 | moveFromBuilder res 275 | 276 | let map3 mapping list1 list2 list3 = 277 | checkNotDefault (nameof list1) list1 278 | checkNotDefault (nameof list2) list2 279 | checkNotDefault (nameof list3) list3 280 | let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt (mapping) 281 | let len1 = list1.Length 282 | 283 | if not (len1 = list2.Length) then 284 | invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths 285 | 286 | if not (len1 = list3.Length) then 287 | invalidArg (nameof list3) ErrorStrings.ListsHaveDifferentLengths 288 | 289 | let res = builderWith len1 290 | 291 | for i = 0 to len1 - 1 do 292 | res.Add <| f.Invoke (list1.[i], list2.[i], list3.[i]) 293 | 294 | moveFromBuilder res 295 | 296 | let mapi2 mapping list1 list2 = 297 | checkNotDefault (nameof list1) list1 298 | checkNotDefault (nameof list2) list2 299 | let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt (mapping) 300 | let len1 = list1.Length 301 | 302 | if len1 <> list2.Length then 303 | invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths 304 | 305 | let res = builderWith len1 306 | 307 | for i = 0 to len1 - 1 do 308 | res.Add <| f.Invoke (i, list1.[i], list2.[i]) 309 | 310 | moveFromBuilder res 311 | 312 | let iteri action list = 313 | check list 314 | let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (action) 315 | let len = list.Length 316 | 317 | for i = 0 to len - 1 do 318 | f.Invoke (i, list.[i]) 319 | 320 | let iteri2 action list1 list2 = 321 | checkNotDefault (nameof list1) list1 322 | checkNotDefault (nameof list2) list2 323 | let f = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt (action) 324 | let len1 = list1.Length 325 | 326 | if len1 <> list2.Length then 327 | invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths 328 | 329 | for i = 0 to len1 - 1 do 330 | f.Invoke (i, list1.[i], list2.[i]) 331 | 332 | let mapi mapping list = 333 | check list 334 | let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (mapping) 335 | let len = list.Length 336 | let res = builderWithLengthOf list 337 | 338 | for i = 0 to len - 1 do 339 | res.Add <| f.Invoke (i, list.[i]) 340 | 341 | moveFromBuilder res 342 | 343 | let exists predicate list = 344 | check list 345 | let len = list.Length 346 | let rec loop i = i < len && (predicate list.[i] || loop (i + 1)) 347 | loop 0 348 | 349 | let inline contains e list = 350 | check list 351 | let mutable state = false 352 | let mutable i = 0 353 | 354 | while (not state && i < list.Length) do 355 | state <- e = list.[i] 356 | i <- i + 1 357 | 358 | state 359 | 360 | let exists2 predicate list1 list2 = 361 | checkNotDefault (nameof list1) list1 362 | checkNotDefault (nameof list2) list2 363 | let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (predicate) 364 | let len1 = list1.Length 365 | 366 | if len1 <> list2.Length then 367 | invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths 368 | 369 | let rec loop i = 370 | i < len1 371 | && (f.Invoke (list1.[i], list2.[i]) || loop (i + 1)) 372 | 373 | loop 0 374 | 375 | let forall predicate list = 376 | check list 377 | let len = list.Length 378 | let rec loop i = i >= len || (predicate list.[i] && loop (i + 1)) 379 | loop 0 380 | 381 | let forall2 predicate list1 list2 = 382 | checkNotDefault (nameof list1) list1 383 | checkNotDefault (nameof list2) list2 384 | let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt (predicate) 385 | let len1 = list1.Length 386 | 387 | if len1 <> list2.Length then 388 | invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths 389 | 390 | let rec loop i = 391 | i >= len1 392 | || (f.Invoke (list1.[i], list2.[i]) && loop (i + 1)) 393 | 394 | loop 0 395 | 396 | let groupBy projection list = 397 | check list 398 | let dict = new System.Collections.Generic.Dictionary<'Key, ResizeArray<'T>> (HashIdentity.Structural) 399 | 400 | // Build the groupings 401 | for i = 0 to (list.Length - 1) do 402 | let v = list.[i] 403 | let key = projection v 404 | let ok, prev = dict.TryGetValue (key) 405 | 406 | if ok then 407 | prev.Add (v) 408 | else 409 | let prev = new ResizeArray<'T> (1) 410 | dict.[key] <- prev 411 | prev.Add (v) 412 | 413 | // Return the list-of-lists. 414 | let result = builderWith dict.Count 415 | let mutable i = 0 416 | 417 | for group in dict do 418 | result.Add (group.Key, ofSeq group.Value) 419 | i <- i + 1 420 | 421 | moveFromBuilder result 422 | 423 | let pick chooser list = 424 | check list 425 | 426 | let rec loop i = 427 | if i >= list.Length then 428 | indexNotFound () 429 | else 430 | match chooser list.[i] with 431 | | None -> loop (i + 1) 432 | | Some res -> res 433 | 434 | loop 0 435 | 436 | let tryPick chooser list = 437 | check list 438 | 439 | let rec loop i = 440 | if i >= list.Length then 441 | None 442 | else 443 | match chooser list.[i] with 444 | | None -> loop (i + 1) 445 | | res -> res 446 | 447 | loop 0 448 | 449 | let choose chooser list = 450 | check list 451 | let res = builderWith list.Length 452 | 453 | for i = 0 to list.Length - 1 do 454 | match chooser list.[i] with 455 | | None -> () 456 | | Some b -> res.Add (b) 457 | 458 | ofBuilder res 459 | 460 | let partition predicate list = 461 | check list 462 | let res1 = builderWith list.Length 463 | let res2 = builderWith list.Length 464 | 465 | for i = 0 to list.Length - 1 do 466 | let x = list.[i] 467 | if predicate x then res1.Add (x) else res2.Add (x) 468 | 469 | ofBuilder res1, ofBuilder res2 470 | 471 | let find predicate list = 472 | check list 473 | 474 | let rec loop i = 475 | if i >= list.Length then indexNotFound () 476 | else if predicate list.[i] then list.[i] 477 | else loop (i + 1) 478 | 479 | loop 0 480 | 481 | let tryFind predicate list = 482 | check list 483 | 484 | let rec loop i = 485 | if i >= list.Length then None 486 | else if predicate list.[i] then Some list.[i] 487 | else loop (i + 1) 488 | 489 | loop 0 490 | 491 | let findBack predicate list = 492 | check list 493 | 494 | let rec loop i = 495 | if i < 0 then indexNotFound () 496 | else if predicate list.[i] then list.[i] 497 | else loop (i - 1) 498 | 499 | loop <| length list - 1 500 | 501 | let tryFindBack predicate list = 502 | check list 503 | 504 | let rec loop i = 505 | if i < 0 then None 506 | else if predicate list.[i] then Some list.[i] 507 | else loop (i + 1) 508 | 509 | loop <| length list - 1 510 | 511 | let findIndexBack predicate list = 512 | check list 513 | 514 | let rec loop i = 515 | if i < 0 then indexNotFound () 516 | else if predicate list.[i] then i 517 | else loop (i - 1) 518 | 519 | loop <| length list - 1 520 | 521 | let tryFindIndexBack predicate list = 522 | check list 523 | 524 | let rec loop i = 525 | if i < 0 then None 526 | else if predicate list.[i] then Some i 527 | else loop (i - 1) 528 | 529 | loop <| length list - 1 530 | // TODO: windowed 531 | 532 | ////////// Based on other operations ////////// 533 | 534 | let take count list = removeRange count (length list - count) list 535 | 536 | let inline private lengthWhile predicate list = 537 | check list 538 | let mutable count = 0 539 | 540 | while count < list.Length && predicate list.[count] do 541 | count <- count + 1 542 | 543 | count 544 | 545 | let takeWhile predicate list = take (lengthWhile predicate list) list 546 | 547 | let skip index list = removeRange 0 index list 548 | 549 | let skipWhile predicate list = skip (lengthWhile predicate list) list 550 | 551 | let sub start stop list = skip start list |> take (stop - start - 1) 552 | 553 | let truncate count list = if count < length list then take count list else list 554 | 555 | let splitAt index list = take index list, skip index list 556 | 557 | let head list = item 0 list 558 | 559 | let tryItem index list = 560 | if index >= length list || index < 0 then 561 | None 562 | else 563 | Some (list.[index]) 564 | 565 | let tryHead list = tryItem 0 list 566 | 567 | let last (list : FlatList<_>) = list.[length list - 1] 568 | 569 | let tryLast list = tryItem (length list - 1) list 570 | 571 | let tail list = removeRange 1 (length list - 1) list 572 | 573 | let tryTail list = if isEmpty list then None else Some <| tail list 574 | 575 | let create count item = init count <| fun _ -> item // optimize 576 | 577 | let replicate count item = create item count 578 | 579 | let collect mapping list = concat <| map mapping list 580 | 581 | let inline build f = 582 | let builder = builder () 583 | f builder 584 | moveFromBuilder builder 585 | 586 | let inline update f list = 587 | let builder = toBuilder list 588 | f builder 589 | moveFromBuilder builder 590 | 591 | ////////// 592 | 593 | module ImmutableArray = FlatList 594 | -------------------------------------------------------------------------------- /src/FSharp.Collections.Immutable/ImmutableCollectionUtil.fs: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | namespace global 3 | #else 4 | namespace FSharp.Collections.Immutable 5 | #endif 6 | 7 | [] 8 | module internal ImmutableCollectionUtil = 9 | let inline checkNotNull name (arg : _ | null) = 10 | match arg with 11 | | null -> nullArg name 12 | | _ -> () 13 | 14 | module internal ErrorStrings = 15 | [] 16 | let InputMustBeNonNegative = "The input must be non-negative." 17 | 18 | [] 19 | let ListsHaveDifferentLengths = "The lists have different lengths." 20 | -------------------------------------------------------------------------------- /src/FSharp.Collections.Immutable/ImmutableList.fs: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | namespace global 3 | #else 4 | namespace FSharp.Collections.Immutable 5 | 6 | open FSharp.Collections.Immutable.ImmutableCollectionUtil 7 | #endif 8 | open System.Collections.Immutable 9 | 10 | [] 11 | module ImmutableList = 12 | 13 | ////////// Factory ////////// 14 | 15 | let inline internal check (list : IImmutableList<_>) = checkNotNull (nameof list) list 16 | 17 | let inline empty<'T> = ImmutableList.Create<'T> () 18 | let inline singleton<'T> (item : 'T) : ImmutableList<'T> = ImmutableList.Create<'T> (item) 19 | 20 | let inline ofSeq source = 21 | checkNotNull (nameof source) source 22 | ImmutableList.CreateRange source 23 | 24 | let inline ofArray (source : _ array) = 25 | checkNotNull (nameof source) source 26 | ImmutableList.CreateRange source 27 | 28 | let inline ofList (list : _ list) = ofSeq list 29 | 30 | let inline toSeq (list : ImmutableList<_>) = list :> seq<_> 31 | 32 | let inline toArray (list : ImmutableList<_>) = 33 | check list 34 | Seq.toArray list 35 | 36 | ////////// Building ////////// 37 | 38 | let inline ofBuilder (builder : ImmutableList<_>.Builder) = builder.ToImmutable () 39 | 40 | let inline builder () = ImmutableList.CreateBuilder () 41 | 42 | let toBuilder (list : ImmutableList<_>) = 43 | check list 44 | list.ToBuilder () 45 | 46 | let inline build f = 47 | let builder = builder () 48 | f builder 49 | builder.ToImmutable () 50 | 51 | let inline update f list = 52 | let builder = toBuilder list 53 | f builder 54 | builder.ToImmutable () 55 | 56 | 57 | open System.Collections.Generic 58 | open System 59 | 60 | ////////// IReadOnly* ////////// 61 | 62 | let length list = 63 | check list 64 | list.Count 65 | 66 | let item index list = 67 | check list 68 | list.[index] 69 | 70 | ////////// ImmutableList ////////// 71 | 72 | let contains item (list : ImmutableList<_>) = list.Contains (item) 73 | 74 | let reverse (list : ImmutableList<_>) = list.Reverse () 75 | 76 | let reverseRange (index, count) (list : ImmutableList<_>) = list.Reverse (index, count) 77 | 78 | ////////// IImmutableList ////////// 79 | 80 | /// Replaces an element in the list at a given position with the specified element. 81 | let withItem index value list = 82 | check list 83 | list.SetItem (index, value) 84 | 85 | /// Returns a new list with the first matching element in the list replaced with the specified element with 86 | /// the given comparer. 87 | let replaceWith comparer oldValue value list = 88 | check list 89 | list.Replace (oldValue, value, comparer) 90 | 91 | /// Returns a new list with the first matching element in the list replaced with the specified element. 92 | let replace oldValue value list = replaceWith HashIdentity.Structural oldValue value list 93 | 94 | 95 | /// Creates a list with all the items removed, but with the same sorting and ordering semantics as 96 | /// this list. 97 | let clear list = 98 | check list 99 | list.Clear () 100 | 101 | /// Makes a copy of the list, and adds the specified object to the end of the copied list. 102 | let add item list = 103 | check list 104 | list.Add item 105 | 106 | 107 | /// Makes a copy of the list and adds the specified objects to the end of the copied list. 108 | let append list items = 109 | check list 110 | list.AddRange items 111 | 112 | /// Inserts the specified element at the specified index in a immutable list. 113 | let insert index item list = 114 | check list 115 | list.Insert (index, item) 116 | 117 | /// Inserts the specified elements at the specified index in the immutable list. 118 | let insertRange index items list = 119 | check list 120 | list.InsertRange (index, items) // TODO: rename 121 | 122 | 123 | /// Removes the first occurrence of a specified object from this immutable list using the given comparer. 124 | let removeWith comparer item list = 125 | check list 126 | list.Remove (item, comparer) 127 | 128 | /// Removes the first occurrence of a specified object from this immutable list. 129 | let remove item list = removeWith HashIdentity.Structural item list 130 | 131 | 132 | /// Removes the specified objects from the list with the given comparer. 133 | let exceptWith (comparer : IEqualityComparer<_>) items list = 134 | check list 135 | list.RemoveRange (items, comparer) 136 | 137 | /// Removes the specified objects from the list. 138 | let except items list = exceptWith HashIdentity.Structural items list 139 | 140 | 141 | /// Removes all the elements that do not match the conditions defined by the specified predicate. 142 | let filter predicate list = 143 | check list 144 | Predicate (not << predicate) |> list.RemoveAll 145 | 146 | /// Removes a range of elements from the System.Collections.Immutable.IImmutableList`1. 147 | let removeRange index (count : int) list = 148 | check list 149 | list.RemoveRange (index, count) 150 | 151 | /// Removes the element at the specified index of the immutable list. 152 | let removeAt index list = 153 | check list 154 | list.RemoveAt index 155 | 156 | /// Searches for the specified object and returns the zero-based index of the first occurrence within the range 157 | /// of elements in the list that starts at the specified index and 158 | /// contains the specified number of elements. 159 | let indexRangeWith comparer index count item list = 160 | check list 161 | list.IndexOf (item, index, count, comparer) 162 | 163 | let indexRange index count item list = indexRangeWith HashIdentity.Structural index count item list 164 | let indexFromWith comparer index item list = indexRangeWith comparer index (length list - index) item 165 | let indexFrom index item list = indexFromWith HashIdentity.Structural index item list 166 | let indexWith comparer item list = indexFromWith comparer 0 item list 167 | let index item list = indexWith HashIdentity.Structural item list 168 | 169 | 170 | /// Searches for the specified object and returns the zero-based index of the last occurrence within the 171 | /// range of elements in the list that contains the specified number 172 | /// of elements and ends at the specified index. 173 | let lastIndexRangeWith comparer index count item list = 174 | check list 175 | list.LastIndexOf (item, index, count, comparer) 176 | 177 | let lastIndexRange index count item list = lastIndexRangeWith HashIdentity.Structural index count item list 178 | let lastIndexFromWith comparer index item list = lastIndexRangeWith comparer index (index + 1) item list 179 | let lastIndexFrom index item list = lastIndexFromWith HashIdentity.Structural index item list 180 | let lastIndexWith comparer item list = lastIndexFromWith comparer (length list - 1) item list 181 | let lastIndex item list = lastIndexWith HashIdentity.Structural item list 182 | 183 | ////////// Filter-based ////////// 184 | 185 | let filterFold (predicate : 'State -> 'T -> bool * 'State) initial list = 186 | let state = ref initial 187 | 188 | filter 189 | (fun item -> 190 | let condition, state' = predicate !state item 191 | state := state' 192 | condition 193 | ) 194 | list, 195 | !state 196 | 197 | let skipWhile predicate list = 198 | let condition = ref true 199 | 200 | filter 201 | (fun item -> 202 | if !condition then 203 | condition := !condition && predicate item 204 | !condition 205 | else 206 | false 207 | ) 208 | list 209 | 210 | let skipUntil predicate list = skipWhile (not << predicate) list 211 | 212 | let takeWhile predicate list = 213 | let condition = ref true 214 | 215 | filter 216 | (fun item -> 217 | if !condition then 218 | condition := !condition && predicate item 219 | not !condition 220 | else 221 | true 222 | ) 223 | list 224 | 225 | let takeUntil predicate list = takeWhile (not << predicate) list 226 | 227 | ////////// Loop-based ////////// 228 | 229 | let concat lists = 230 | checkNotNull (nameof lists) lists 231 | 232 | build 233 | <| fun result -> 234 | for list in lists do 235 | result.AddRange list 236 | 237 | let map mapping list = 238 | check list 239 | 240 | build 241 | <| fun builder -> 242 | for item in list do 243 | builder.Add (mapping item) 244 | 245 | let choose chooser list = 246 | check list 247 | 248 | build 249 | <| fun builder -> 250 | for item in list do 251 | match chooser item with 252 | | Some item -> builder.Add item 253 | | None -> () 254 | 255 | 256 | ////////// Based on other operations ////////// 257 | 258 | let isEmpty list = length list = 0 259 | 260 | let take count list = removeRange count (length list - count) list 261 | 262 | let skip index list = removeRange 0 index list 263 | 264 | let truncate count list = if count < length list then take count list else list 265 | 266 | let splitAt index list = take index list, skip index list 267 | 268 | let head list = item 0 list 269 | 270 | let last (list : IImmutableList<_>) = list.[length list - 1] 271 | 272 | let tail list = removeAt 0 list 273 | 274 | let tryItem index list = 275 | if index >= length list || index < 0 then 276 | None 277 | else 278 | Some (list.[index]) 279 | 280 | let tryHead list = tryItem 0 list 281 | 282 | let tryLast list = tryItem (length list - 1) list 283 | 284 | let tryTail list = if isEmpty list then None else Some <| tail list 285 | 286 | let collect mapping list = concat <| map mapping list 287 | 288 | let cons head list = insert 0 head list 289 | 290 | //let ofArray (array: 'T array) = ImmutableList.Create<'T>(items = [||]) 291 | 292 | let init count initializer = 293 | if count < 0 then 294 | // throw the same exception 295 | try 296 | Seq.init count initializer |> ignore 297 | with exn -> 298 | raise exn // get the right stack trace 299 | 300 | build 301 | <| fun builder -> 302 | for i = 0 to count - 1 do 303 | builder.Add <| initializer i 304 | 305 | let unfold generator state = 306 | let rec unfoldLoop state (builder : ImmutableList<_>.Builder) = 307 | match generator state with 308 | | Some (state, item) -> 309 | builder.Add (item) 310 | unfoldLoop state builder 311 | | None -> () 312 | 313 | build <| unfoldLoop state 314 | 315 | 316 | ////////// Seq-based ////////// 317 | 318 | let find predicate list = 319 | check list 320 | Seq.find predicate list 321 | 322 | let tryFind predicate list = 323 | check list 324 | Seq.tryFind predicate list 325 | 326 | let findIndex predicate list = 327 | check list 328 | Seq.findIndex predicate list 329 | 330 | let tryFindIndex predicate list = 331 | check list 332 | Seq.tryFindIndex predicate list 333 | 334 | let pick chooser list = 335 | check list 336 | Seq.pick chooser list 337 | 338 | let fold folder state list = 339 | check list 340 | Seq.fold folder state list 341 | 342 | let forall predicate list = 343 | check list 344 | Seq.forall predicate list 345 | 346 | let forall2 predicate (list1 : IImmutableList<_>) (list2 : IImmutableList<_>) = 347 | checkNotNull (nameof list1) list1 348 | checkNotNull (nameof list2) list2 349 | Seq.forall2 predicate list1 list2 350 | 351 | let iter action list = 352 | check list 353 | Seq.iter action list 354 | -------------------------------------------------------------------------------- /src/FSharp.Collections.Immutable/IndexedSeq.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable 2 | 3 | type IIndexedSeq<'T> = System.Collections.Generic.IReadOnlyList<'T> 4 | 5 | module IndexedSeq = 6 | 7 | let check (seq : IIndexedSeq<_>) = checkNotNull (nameof seq) seq 8 | 9 | let item index seq = 10 | check seq 11 | seq.[index] 12 | 13 | let length seq = 14 | check seq 15 | seq.Count 16 | 17 | module ReadOnlyList = IndexedSeq 18 | -------------------------------------------------------------------------------- /src/FSharp.Collections.Immutable/Maps.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable 2 | 3 | open System.Collections.Generic 4 | 5 | type IMap<'Key, 'Value> = System.Collections.Immutable.IImmutableDictionary<'Key, 'Value> 6 | 7 | type HashMap<'Key, 'Value when 'Key : not null> = System.Collections.Immutable.ImmutableDictionary<'Key, 'Value> 8 | 9 | type HashMapBuilder<'Key, 'Value when 'Key : not null> = HashMap<'Key, 'Value>.Builder 10 | 11 | [] 14 | module HashMap = 15 | 16 | type internal HashMapFactory = System.Collections.Immutable.ImmutableDictionary 17 | 18 | let inline check (map : HashMap<_, _>) = checkNotNull (nameof map) map 19 | 20 | ////////// Creating ////////// 21 | 22 | let inline empty<'Key, 'Value when 'Key : not null> = HashMapFactory.Create<'Key, 'Value> () 23 | let inline singleton item = empty.Add (item) 24 | 25 | let inline ofSeq source = HashMapFactory.CreateRange (source) 26 | 27 | let inline ofSeqWith getKey source = 28 | source 29 | |> Seq.map (fun i -> KeyValuePair (getKey i, i)) 30 | |> HashMapFactory.CreateRange 31 | 32 | let inline ofSeqGroupBy getKey source = 33 | source 34 | |> Seq.groupBy getKey 35 | |> Seq.map (fun (key, value) -> KeyValuePair (key, value)) 36 | |> HashMapFactory.CreateRange 37 | 38 | let inline ofArray (source : _ array) = HashMapFactory.CreateRange (source) 39 | 40 | let inline toSeq (map : HashMap<_, _>) = map :> seq<_> 41 | 42 | let inline toArray (map : HashMap<_, _>) = 43 | check map 44 | Seq.toArray map 45 | 46 | ////////// Building ////////// 47 | 48 | let inline builder () = HashMapFactory.CreateBuilder () 49 | let inline builderWithKeyComparer comparer = HashMapFactory.CreateBuilder (comparer) 50 | let inline builderWithComparers keyComparer valueComparer = HashMapFactory.CreateBuilder (keyComparer, valueComparer) 51 | 52 | let inline ofBuilder (mapBuilder : HashMapBuilder<_, _>) = 53 | checkNotNull (nameof mapBuilder) mapBuilder 54 | mapBuilder.ToImmutable () 55 | 56 | let inline toBuilder map : HashMapBuilder<_, _> = 57 | check map 58 | map.ToBuilder () 59 | 60 | let inline ofKeyComparer<'Key, 'Value when 'Key : not null> comparer = HashMapFactory.Create<'Key, 'Value> (comparer) 61 | let inline ofComparers<'Key, 'Value when 'Key : not null> keyComparer valueComparer = 62 | HashMapFactory.Create<'Key, 'Value> (keyComparer, valueComparer) 63 | 64 | 65 | let inline isEmpty map = 66 | check map 67 | map.IsEmpty 68 | 69 | let inline length map = 70 | check map 71 | map.Count 72 | 73 | let inline keyComparer map = 74 | check map 75 | map.KeyComparer 76 | 77 | let inline valueComparer map = 78 | check map 79 | map.ValueComparer 80 | 81 | let inline containsKey key map = 82 | check map 83 | map.ContainsKey key 84 | 85 | let inline find key map = 86 | check map 87 | map.[key] 88 | 89 | let inline tryFind key map = 90 | check map 91 | 92 | match map.TryGetValue (key) with 93 | | true, value -> Some value 94 | | false, _ -> None 95 | 96 | let inline vTryFind key map = 97 | check map 98 | 99 | match map.TryGetValue (key) with 100 | | true, value -> ValueSome value 101 | | false, _ -> ValueNone 102 | 103 | let inline pick chooser map = 104 | check map 105 | map |> Seq.pick (fun kvp -> chooser kvp.Key kvp.Value) 106 | 107 | let inline tryPick chooser map = 108 | check map 109 | map |> Seq.tryPick (fun kvp -> chooser kvp.Key kvp.Value) 110 | 111 | let inline vTryPick chooser map = 112 | check map 113 | 114 | match map |> Seq.tryPick (fun kvp -> chooser kvp.Key kvp.Value) with 115 | | Some value -> ValueSome value 116 | | None -> ValueNone 117 | 118 | let inline iter action (map : HashMap<_, _>) = 119 | check map 120 | map |> Seq.iter (fun kvp -> action kvp.Key kvp.Value) 121 | 122 | let inline exists predicate map = 123 | check map 124 | map |> Seq.exists (fun kvp -> predicate kvp.Key kvp.Value) 125 | 126 | let inline add key value map : HashMap<_, _> = 127 | check map 128 | map.Add (key, value) 129 | 130 | let inline append map pairs : HashMap<_, _> = 131 | check map 132 | checkNotNull (nameof pairs) pairs 133 | map.AddRange pairs 134 | 135 | let inline remove key map : HashMap<_, _> = 136 | check map 137 | map.Remove key 138 | 139 | let inline except keys map : HashMap<_, _> = 140 | check map 141 | map.RemoveRange keys 142 | 143 | let inline clear map : HashMap<_, _> = 144 | check map 145 | map.Clear () 146 | 147 | let inline filter predicate map = 148 | map 149 | |> Seq.filter (fun (kvp : KeyValuePair<_, _>) -> predicate kvp.Key kvp.Value) 150 | 151 | let inline forall predicate map = 152 | map 153 | |> Seq.forall (fun (kvp : KeyValuePair<_, _>) -> predicate kvp.Key kvp.Value) 154 | 155 | let inline map mapping map' = 156 | map' 157 | |> Seq.map (fun (kvp : KeyValuePair<_, _>) -> mapping kvp.Key kvp.Value) 158 | |> ofSeq 159 | 160 | let inline where predicate map = 161 | map 162 | |> Seq.where (fun (kvp : KeyValuePair<_, _>) -> predicate kvp.Key kvp.Value) 163 | |> empty.AddRange 164 | 165 | 166 | type SortedMap<'Key, 'Value when 'Key : not null> = System.Collections.Immutable.ImmutableSortedDictionary<'Key, 'Value> 167 | 168 | type SortedMapBuilder<'Key, 'Value when 'Key : not null> = SortedMap<'Key, 'Value>.Builder 169 | 170 | [] 173 | module SortedMap = 174 | 175 | type internal SortedMapFactory = System.Collections.Immutable.ImmutableSortedDictionary 176 | 177 | let inline check (sortedMap : SortedMap<_, _>) = checkNotNull (nameof sortedMap) sortedMap 178 | 179 | ////////// Creating ////////// 180 | 181 | let inline empty<'Key, 'Value when 'Key : not null> = SortedMapFactory.Create<'Key, 'Value> () 182 | let inline singleton item = SortedMapFactory.Create (item) 183 | 184 | let inline ofSeq source = SortedMapFactory.CreateRange (source) 185 | 186 | let inline ofSeqWith getKey source = 187 | source 188 | |> Seq.map (fun i -> KeyValuePair (getKey i, i)) 189 | |> SortedMapFactory.CreateRange 190 | 191 | let inline ofSeqGroupBy getKey source = 192 | source 193 | |> Seq.groupBy getKey 194 | |> Seq.map (fun (key, value) -> KeyValuePair (key, value)) 195 | 196 | let inline ofArray (source : _ array) = SortedMapFactory.CreateRange (source) 197 | 198 | let inline toSeq (map : SortedMap<_, _>) = map :> seq<_> 199 | 200 | let inline toArray (map : SortedMap<_, _>) = 201 | check map 202 | Seq.toArray map 203 | 204 | ////////// Building ////////// 205 | 206 | let inline builder () = SortedMapFactory.CreateBuilder () 207 | let inline builderWithKeyComparer comparer = SortedMapFactory.CreateBuilder (comparer) 208 | let inline builderWithComparers keyComparer valueComparer = SortedMapFactory.CreateBuilder (keyComparer, valueComparer) 209 | 210 | let inline ofBuilder (sortedMapBuilder : SortedMapBuilder<_, _>) = 211 | checkNotNull (nameof sortedMapBuilder) sortedMapBuilder 212 | sortedMapBuilder.ToImmutable () 213 | 214 | let inline toBuilder map : SortedMapBuilder<_, _> = 215 | check map 216 | map.ToBuilder () 217 | 218 | let inline ofKeyComparer<'Key, 'Value when 'Key : not null> comparer = SortedMapFactory.Create<'Key, 'Value> (comparer) 219 | let inline ofComparers<'Key, 'Value when 'Key : not null> keyComparer valueComparer = 220 | SortedMapFactory.Create<'Key, 'Value> (keyComparer, valueComparer) 221 | 222 | 223 | let inline isEmpty map = 224 | check map 225 | map.IsEmpty 226 | 227 | let inline length map = 228 | check map 229 | map.Count 230 | 231 | let inline keyComparer map = 232 | check map 233 | map.KeyComparer 234 | 235 | let inline valueComparer map = 236 | check map 237 | map.ValueComparer 238 | 239 | let inline containsKey key map = 240 | check map 241 | map.ContainsKey key 242 | 243 | let inline find key map = 244 | check map 245 | map.[key] 246 | 247 | let inline tryFind key map = 248 | check map 249 | 250 | match map.TryGetValue (key) with 251 | | true, value -> Some value 252 | | false, _ -> None 253 | 254 | let inline vTryFind key map = 255 | check map 256 | 257 | match map.TryGetValue (key) with 258 | | true, value -> ValueSome value 259 | | false, _ -> ValueNone 260 | 261 | let inline pick chooser map = 262 | check map 263 | map |> Seq.pick (fun kvp -> chooser kvp.Key kvp.Value) 264 | 265 | let inline tryPick chooser map = 266 | check map 267 | map |> Seq.tryPick (fun kvp -> chooser kvp.Key kvp.Value) 268 | 269 | let inline vTryPick chooser map = 270 | check map 271 | 272 | match map |> Seq.tryPick (fun kvp -> chooser kvp.Key kvp.Value) with 273 | | Some value -> ValueSome value 274 | | None -> ValueNone 275 | 276 | let inline iter action (map : SortedMap<_, _>) = 277 | check map 278 | map |> Seq.iter (fun kvp -> action kvp.Key kvp.Value) 279 | 280 | let inline exists predicate map = 281 | check map 282 | map |> Seq.exists (fun kvp -> predicate kvp.Key kvp.Value) 283 | 284 | let inline add key value map : SortedMap<_, _> = 285 | check map 286 | map.Add (key, value) 287 | 288 | let inline append map pairs : SortedMap<_, _> = 289 | check map 290 | checkNotNull (nameof pairs) pairs 291 | map.AddRange pairs 292 | 293 | let inline remove key map : SortedMap<_, _> = 294 | check map 295 | map.Remove key 296 | 297 | let inline except keys map : SortedMap<_, _> = 298 | check map 299 | map.RemoveRange keys 300 | 301 | let inline clear map : SortedMap<_, _> = 302 | check map 303 | map.Clear () 304 | 305 | let inline findKey predicate map = 306 | check map 307 | 308 | match (map |> Seq.tryFind (fun kvp -> predicate kvp.Key kvp.Value)) with 309 | | Some value -> value.Key 310 | | None -> raise (new KeyNotFoundException ()) 311 | 312 | let inline tryFindKey predicate map = 313 | check map 314 | map |> Seq.tryFind (fun kvp -> predicate kvp.Key kvp.Value) 315 | 316 | let inline vTryFindKey predicate map = 317 | check map 318 | 319 | match (map |> Seq.tryFind (fun kvp -> predicate kvp.Key kvp.Value)) with 320 | | Some value -> ValueSome value.Key 321 | | None -> ValueNone 322 | 323 | let inline filter predicate map = 324 | map 325 | |> Seq.filter (fun (kvp : KeyValuePair<_, _>) -> predicate kvp.Key kvp.Value) 326 | 327 | let inline forall predicate map = 328 | map 329 | |> Seq.forall (fun (kvp : KeyValuePair<_, _>) -> predicate kvp.Key kvp.Value) 330 | 331 | let inline map mapping (map' : SortedMap<_, _>) = 332 | map' 333 | |> Seq.map (fun (kvp : KeyValuePair<_, _>) -> mapping kvp.Key kvp.Value) 334 | |> ofSeq 335 | 336 | let inline where predicate map = 337 | map 338 | |> Seq.where (fun (kvp : KeyValuePair<_, _>) -> predicate kvp.Key kvp.Value) 339 | |> empty.AddRange 340 | -------------------------------------------------------------------------------- /src/FSharp.Collections.Immutable/Queue.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable 2 | 3 | type IQueue<'T> = System.Collections.Immutable.IImmutableQueue<'T> 4 | 5 | type Queue<'T> = System.Collections.Immutable.ImmutableQueue<'T> 6 | 7 | [] 10 | module Queue = 11 | 12 | type internal QueueFactory = System.Collections.Immutable.ImmutableQueue 13 | 14 | let inline private check (queue : IQueue<_>) = checkNotNull (nameof queue) queue 15 | 16 | let inline empty<'T> : Queue<'T> = QueueFactory.Create<'T> () 17 | 18 | let inline singleton<'T> (item : 'T) : Queue<'T> = QueueFactory.Create<'T> (item) 19 | 20 | let inline ofSeq (source : 'T seq) : Queue<'T> = QueueFactory.CreateRange source 21 | 22 | let inline toSeq (queue : Queue<_>) = queue :> seq<_> 23 | 24 | let isEmpty queue = 25 | check queue 26 | queue.IsEmpty 27 | 28 | let clear queue : IQueue<_> = 29 | check queue 30 | queue.Clear () 31 | 32 | let enqueue item queue : IQueue<_> = 33 | check queue 34 | queue.Enqueue item 35 | 36 | let head queue = 37 | check queue 38 | queue.Peek () 39 | 40 | let tail queue : IQueue<_> = 41 | check queue 42 | queue.Dequeue () 43 | 44 | ////////// 45 | 46 | let (|Cons|Nil|) queue = // consider renaming 47 | if isEmpty queue then Nil else Cons (head queue, tail queue) 48 | 49 | ////////// Predicate based ////////// 50 | 51 | let filter predicate queue = 52 | let rec loop queue result = 53 | match queue with 54 | | Cons (head, tail) -> 55 | loop tail 56 | <| if predicate head then enqueue head result else result 57 | | Nil -> result 58 | 59 | loop queue <| clear queue 60 | 61 | ////////// Seq-based ////////// 62 | 63 | let find predicate queue = 64 | check queue 65 | Seq.find predicate queue 66 | 67 | let tryFind predicate queue = 68 | check queue 69 | Seq.tryFind predicate queue 70 | 71 | let findIndex predicate queue = 72 | check queue 73 | Seq.findIndex predicate queue 74 | 75 | let tryFindIndex predicate queue = 76 | check queue 77 | Seq.tryFindIndex predicate queue 78 | 79 | let pick chooser queue = 80 | check queue 81 | Seq.pick chooser queue 82 | 83 | let tryPick chooser queue = 84 | check queue 85 | Seq.tryPick chooser queue 86 | 87 | let iter action queue = 88 | check queue 89 | Seq.iter action queue 90 | 91 | let iteri action queue = 92 | check queue 93 | Seq.iteri action queue 94 | 95 | let iter2 action (queue1 : IQueue<_>) (queue2 : IQueue<_>) = 96 | checkNotNull (nameof queue1) queue1 97 | checkNotNull (nameof queue2) queue2 98 | Seq.iter2 action queue1 queue2 99 | 100 | let fold folder state queue = 101 | check queue 102 | Seq.fold folder state queue 103 | 104 | let forall predicate queue = 105 | check queue 106 | Seq.forall predicate 107 | 108 | let exists predicate queue = 109 | check queue 110 | Seq.exists predicate queue 111 | 112 | let reduce reduction queue = 113 | check queue 114 | Seq.reduce reduction queue 115 | 116 | let inline sum queue = 117 | check queue 118 | Seq.sum queue 119 | 120 | let inline sumBy projection queue = 121 | check queue 122 | Seq.sumBy projection queue 123 | 124 | let inline average queue = 125 | check queue 126 | Seq.average queue 127 | 128 | let inline averageBy projection queue = 129 | check queue 130 | Seq.averageBy projection 131 | 132 | module ImmutableQueue = Queue 133 | -------------------------------------------------------------------------------- /src/FSharp.Collections.Immutable/Seq.fs: -------------------------------------------------------------------------------- 1 | #if INTERACTIVE 2 | namespace global 3 | #else 4 | namespace FSharp.Collections.Immutable 5 | #endif 6 | 7 | [] 8 | module Seq = 9 | 10 | let inline ofFlatList flatList = FlatList.toSeq flatList 11 | let inline toFlatList seq = FlatList.ofSeq seq 12 | 13 | let inline ofStack stack = Stack.toSeq stack 14 | let inline toStack seq = Stack.ofSeq seq 15 | 16 | let inline ofImmutableList immutableList = ImmutableList.toSeq immutableList 17 | let inline toImmutableList seq = ImmutableList.ofSeq seq 18 | 19 | let inline ofQueue queue = Queue.toSeq queue 20 | let inline toQueue queue = Queue.ofSeq queue 21 | 22 | let inline ofHashMap hashMap = HashMap.toSeq hashMap 23 | let inline toHashMap hashMap = HashMap.ofSeq hashMap 24 | 25 | let inline ofSortedMap sortedHashMap = SortedMap.toSeq sortedHashMap 26 | let inline toSortedMap sortedHashMap = SortedMap.ofSeq sortedHashMap 27 | 28 | let inline ofHashSet hashSet = HashSet.toSeq hashSet 29 | let inline toHashSet hashSet = HashSet.ofSeq hashSet 30 | 31 | let inline ofSortedSet sortedSet = SortedSet.toSeq sortedSet 32 | let inline toSortedSet sortedSet = SortedSet.ofSeq sortedSet 33 | -------------------------------------------------------------------------------- /src/FSharp.Collections.Immutable/Sets.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable 2 | 3 | type ISet<'T> = System.Collections.Immutable.IImmutableSet<'T> 4 | 5 | type HashSet<'T> = System.Collections.Immutable.ImmutableHashSet<'T> 6 | type HashSetBuilder<'T> = HashSet<'T>.Builder 7 | 8 | [] 11 | module HashSet = 12 | 13 | type internal HashSetFactory = System.Collections.Immutable.ImmutableHashSet 14 | 15 | let inline check (set : HashSet<_>) = checkNotNull (nameof set) set 16 | 17 | ////////// Creating ////////// 18 | 19 | let inline empty<'T> = HashSetFactory.Create<'T> () 20 | let inline singleton<'T> (item : 'T) = HashSetFactory.Create<'T> (item) 21 | let inline ofSeq source = HashSetFactory.CreateRange (source) 22 | let inline ofSeqWithComparer comparer source = HashSetFactory.Create (comparer, items = (source |> Array.ofSeq)) 23 | let inline ofArray (source : _ array) = HashSetFactory.CreateRange (source) 24 | 25 | let inline ofBuilder (hashSetBuilder : HashSetBuilder<_>) = 26 | checkNotNull (nameof hashSetBuilder) hashSetBuilder 27 | hashSetBuilder.ToImmutable () 28 | 29 | let inline ofComparer<'T> comparer = HashSetFactory.Create<'T> (equalityComparer = comparer) 30 | 31 | let inline toSeq (set : HashSet<_>) = set :> seq<_> 32 | 33 | let inline toArray (set : HashSet<_>) = 34 | check set 35 | Seq.toArray set 36 | 37 | ////////// Building ////////// 38 | 39 | let inline builder () = HashSetFactory.CreateBuilder () 40 | let inline builderWith capacity : HashSet<'T>.Builder = HashSetFactory.CreateBuilder (capacity) 41 | let inline builderWithComparer comparer = HashSetFactory.CreateBuilder (comparer) 42 | 43 | let inline toBuilder set : HashSetBuilder<_> = 44 | check set 45 | set.ToBuilder () 46 | 47 | let inline keyComparer set = 48 | check set 49 | set.KeyComparer 50 | 51 | let inline length set = 52 | check set 53 | set.Count 54 | 55 | let inline isEmpty set = 56 | check set 57 | set.IsEmpty 58 | 59 | let inline contains value set = 60 | check set 61 | set.Contains value 62 | 63 | let inline exists predicate map = 64 | check map 65 | map |> Seq.exists predicate 66 | 67 | let inline isSubset (set1 : HashSet<_>) set2 = 68 | check set1 69 | set1.IsSubsetOf set2 70 | 71 | let inline isProperSubset (set1 : HashSet<_>) set2 = 72 | check set1 73 | set1.IsProperSubsetOf set2 74 | 75 | let inline isSuperset (set1 : HashSet<_>) set2 = 76 | check set1 77 | set1.IsSupersetOf set2 78 | 79 | let inline isProperSuperset (set1 : HashSet<_>) set2 = 80 | check set1 81 | set1.IsProperSupersetOf set2 82 | 83 | let inline add value set : HashSet<_> = 84 | check set 85 | set.Add (value) 86 | 87 | let inline union set values : HashSet<_> = 88 | check set 89 | values |> set.Union 90 | 91 | let inline unionMany (sets : HashSet<_> seq) = Seq.reduce union sets 92 | 93 | let inline intersect (set1 : HashSet<_>) set2 = 94 | check set1 95 | set1.Intersect set2 96 | 97 | let inline intersectMany (sets : HashSet<_> seq) = Seq.reduce intersect sets 98 | 99 | let inline remove value set : HashSet<_> = 100 | check set 101 | set.Remove value 102 | 103 | let inline difference values set : HashSet<_> = 104 | check set 105 | values |> set.Except 106 | 107 | let inline clear set : HashSet<_> = 108 | check set 109 | set.Clear () 110 | 111 | let inline filter predicate set = set |> Seq.filter predicate |> empty.Union 112 | 113 | let inline where predicate set = set |> Seq.where predicate |> empty.Union 114 | 115 | let inline pick chooser set = 116 | check set 117 | set |> Seq.pick chooser 118 | 119 | let inline tryPick chooser set = 120 | check set 121 | set |> Seq.tryPick chooser 122 | 123 | let inline vTryPick chooser set = 124 | check set 125 | 126 | match set |> Seq.tryPick chooser with 127 | | Some value -> ValueSome value 128 | | None -> ValueNone 129 | 130 | let inline map mapping (set : HashSet<_>) = set |> Seq.map mapping |> ofSeq 131 | 132 | let inline forall predicate set = set |> Seq.forall predicate 133 | 134 | let inline iter action (set : HashSet<_>) = 135 | check set 136 | set |> Seq.iter action 137 | 138 | type SortedSet<'T> = System.Collections.Immutable.ImmutableSortedSet<'T> 139 | type SortedSetBuilder<'T> = SortedSet<'T>.Builder 140 | 141 | [] 144 | module SortedSet = 145 | 146 | type internal SortedSetFactory = System.Collections.Immutable.ImmutableSortedSet 147 | 148 | let inline check (sortedSet : SortedSet<_>) = checkNotNull (nameof sortedSet) sortedSet 149 | 150 | ////////// Creating ////////// 151 | 152 | let inline empty<'T> = SortedSetFactory.Create<'T> () 153 | let inline singleton<'T> (item : 'T) = SortedSetFactory.Create<'T> (item) 154 | let inline ofSeq source = SortedSetFactory.CreateRange (source) 155 | let inline ofSeqWithComparer comparer source = SortedSetFactory.Create (comparer, items = (source |> Array.ofSeq)) 156 | let inline ofArray (source : _ array) = SortedSetFactory.CreateRange (source) 157 | 158 | let inline ofBuilder (sortedSetBuilder : SortedSetBuilder<_>) = 159 | checkNotNull (nameof sortedSetBuilder) sortedSetBuilder 160 | sortedSetBuilder.ToImmutable () 161 | 162 | let inline ofComparer<'T> comparer = SortedSetFactory.Create<'T> (comparer = comparer) 163 | 164 | let inline toSeq (set : SortedSet<_>) = set :> seq<_> 165 | 166 | let inline toArray (set : SortedSet<_>) = 167 | check set 168 | Seq.toArray set 169 | 170 | ////////// Building ////////// 171 | 172 | let inline builder () = SortedSetFactory.CreateBuilder () 173 | let inline builderWith capacity : SortedSet<'T>.Builder = SortedSetFactory.CreateBuilder (capacity) 174 | let inline builderWithComparer comparer = SortedSetFactory.CreateBuilder (comparer) 175 | 176 | let inline toBuilder set : SortedSetBuilder<_> = 177 | check set 178 | set.ToBuilder () 179 | 180 | let inline keyComparer set = 181 | check set 182 | set.KeyComparer 183 | 184 | 185 | let inline length set = 186 | check set 187 | set.Count 188 | 189 | let inline contains value set = 190 | check set 191 | set.Contains value 192 | 193 | let inline isEmpty set = 194 | check set 195 | set.IsEmpty 196 | 197 | let inline exists predicate map = 198 | check map 199 | map |> Seq.exists predicate 200 | 201 | let inline isSubset (set1 : SortedSet<_>) set2 = 202 | check set1 203 | set1.IsSubsetOf set2 204 | 205 | let inline isProperSubset (set1 : SortedSet<_>) set2 = 206 | check set1 207 | set1.IsProperSubsetOf set2 208 | 209 | let inline isSuperset (set1 : SortedSet<_>) set2 = 210 | check set1 211 | set1.IsSupersetOf set2 212 | 213 | let inline isProperSuperset (set1 : SortedSet<_>) set2 = 214 | check set1 215 | set1.IsProperSupersetOf set2 216 | 217 | let inline add value set : SortedSet<_> = 218 | check set 219 | set.Add (value) 220 | 221 | let inline union set values : SortedSet<_> = 222 | check set 223 | values |> set.Union 224 | 225 | let inline unionMany (sets : SortedSet<_> seq) = Seq.reduce union sets 226 | let inline intersect (set1 : SortedSet<_>) set2 = set1.Intersect set2 227 | let inline intersectMany (sets : SortedSet<_> seq) = Seq.reduce intersect sets 228 | 229 | let inline remove value set : SortedSet<_> = 230 | check set 231 | set.Remove value 232 | 233 | let inline difference values set : SortedSet<_> = 234 | check set 235 | values |> set.Except 236 | 237 | let inline clear set : SortedSet<_> = 238 | check set 239 | set.Clear () 240 | 241 | let inline filter predicate set = set |> Seq.filter predicate |> empty.Union 242 | 243 | let inline where predicate set = set |> Seq.where predicate |> empty.Union 244 | 245 | let inline pick chooser set = 246 | check set 247 | set |> Seq.pick chooser 248 | 249 | let inline tryPick chooser set = 250 | check set 251 | set |> Seq.tryPick chooser 252 | 253 | let inline vTryPick chooser set = 254 | check set 255 | 256 | match set |> Seq.tryPick chooser with 257 | | Some value -> ValueSome value 258 | | None -> ValueNone 259 | 260 | let inline map mapping (set : SortedSet<_>) = set |> Seq.map mapping |> ofSeq 261 | 262 | let inline forall predicate set = set |> Seq.forall predicate 263 | 264 | let inline iter action (set : SortedSet<_>) = 265 | check set 266 | set |> Seq.iter action 267 | -------------------------------------------------------------------------------- /src/FSharp.Collections.Immutable/Stack.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable 2 | 3 | type IStack<'T> = System.Collections.Immutable.IImmutableStack<'T> 4 | 5 | type Stack<'T> = System.Collections.Immutable.ImmutableStack<'T> 6 | 7 | [] 10 | module Stack = 11 | type internal StackFactory = System.Collections.Immutable.ImmutableStack 12 | 13 | let inline internal check (stack : IStack<_>) = checkNotNull (nameof stack) stack 14 | 15 | let inline empty<'T> = StackFactory.Create<'T> () 16 | 17 | let inline ofSeq source = StackFactory.CreateRange source 18 | 19 | let inline ofArray (array : 'T[]) : Stack<'T> = ofSeq array 20 | 21 | let inline toSeq (stack : IStack<_>) = stack :> seq<_> 22 | 23 | let push head stack : IStack<'T> = 24 | check stack 25 | stack.Push head 26 | 27 | let cons head stack = push head stack 28 | 29 | let peek stack = 30 | check stack 31 | stack.Peek () 32 | 33 | let head stack = peek stack 34 | 35 | let tail stack : IStack<_> = 36 | check stack 37 | stack.Pop () 38 | 39 | let pop stack = 40 | check stack 41 | stack.Peek (), tail stack 42 | 43 | let (|Cons|Nil|) stack = 44 | check stack 45 | 46 | if stack.IsEmpty then 47 | Nil 48 | else 49 | Cons (stack.Peek (), stack.Pop ()) 50 | 51 | ///////////// 52 | module ImmutableStack = Stack 53 | -------------------------------------------------------------------------------- /tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | true 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/FSharp.Collections.Immutable.Tests/Attributes.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable 2 | 3 | open Microsoft.VisualStudio.TestTools.UnitTesting 4 | 5 | [] 6 | 7 | do () 8 | -------------------------------------------------------------------------------- /tests/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | $(AssemblyBaseName).Tests 6 | 7 | Exe 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /tests/FSharp.Collections.Immutable.Tests/FlatList.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable.Tests 2 | 3 | open System 4 | open Microsoft.VisualStudio.TestTools.UnitTesting 5 | 6 | [] 7 | type FlatListTests () = 8 | 9 | [] 10 | member this.TestMethodPassing () = Assert.IsTrue (true) 11 | -------------------------------------------------------------------------------- /tests/FSharp.Collections.Immutable.Tests/ImmutableList.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable.Tests 2 | 3 | open System 4 | open Microsoft.VisualStudio.TestTools.UnitTesting 5 | 6 | [] 7 | type ImmutableListTests () = 8 | 9 | [] 10 | member this.TestMethodPassing () = Assert.IsTrue (true) 11 | -------------------------------------------------------------------------------- /tests/FSharp.Collections.Immutable.Tests/IndexedSeq.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable.Tests 2 | 3 | open System 4 | open Microsoft.VisualStudio.TestTools.UnitTesting 5 | 6 | [] 7 | type IndexedSeqTests () = 8 | 9 | [] 10 | member this.TestMethodPassing () = Assert.IsTrue (true) 11 | -------------------------------------------------------------------------------- /tests/FSharp.Collections.Immutable.Tests/Maps.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable.Tests 2 | 3 | open System 4 | open Microsoft.VisualStudio.TestTools.UnitTesting 5 | 6 | [] 7 | type HashMapTests () = 8 | 9 | [] 10 | member this.TestMethodPassing () = Assert.IsTrue (true) 11 | 12 | [] 13 | type SortedMapTests () = 14 | 15 | [] 16 | member this.TestMethodPassing () = Assert.IsTrue (true) 17 | -------------------------------------------------------------------------------- /tests/FSharp.Collections.Immutable.Tests/Queue.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable.Tests 2 | 3 | open System 4 | open Microsoft.VisualStudio.TestTools.UnitTesting 5 | 6 | [] 7 | type QueueTests () = 8 | 9 | [] 10 | member this.TestMethodPassing () = Assert.IsTrue (true) 11 | -------------------------------------------------------------------------------- /tests/FSharp.Collections.Immutable.Tests/Sets.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable.Tests 2 | 3 | open System 4 | open Microsoft.VisualStudio.TestTools.UnitTesting 5 | 6 | [] 7 | type HashSetTests () = 8 | 9 | [] 10 | member this.TestMethodPassing () = Assert.IsTrue (true) 11 | 12 | [] 13 | type SortedSetTests () = 14 | 15 | [] 16 | member this.TestMethodPassing () = Assert.IsTrue (true) 17 | -------------------------------------------------------------------------------- /tests/FSharp.Collections.Immutable.Tests/Stack.fs: -------------------------------------------------------------------------------- 1 | namespace FSharp.Collections.Immutable.Tests 2 | 3 | open System 4 | open Microsoft.VisualStudio.TestTools.UnitTesting 5 | 6 | [] 7 | type StackTests () = 8 | 9 | [] 10 | member this.TestMethodPassing () = Assert.IsTrue (true) 11 | --------------------------------------------------------------------------------