github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/docs/commands/let.md (about) 1 # `let` 2 3 > Evaluate a mathematical function and assign to variable (deprecated) 4 5 ## Description 6 7 `let` evaluates a mathematical function and then assigns it to a locally 8 scoped variable (like `set`) 9 10 **This is a deprecated feature. Please refer to [`expr`](expr.md) instead.** 11 12 ## Usage 13 14 ``` 15 let var_name=evaluation 16 17 let var_name++ 18 19 let var_name-- 20 ``` 21 22 ## Examples 23 24 ``` 25 » let age=18 26 » $age 27 18 28 29 » let age++ 30 » $age 31 19 32 33 » let under18=age<18 34 » $under18 35 false 36 37 » let under21 = age < 21 38 » $under21 39 true 40 ``` 41 42 ## Detail 43 44 ### Other Operators 45 46 `let` also supports the following operators (substitute **VAR** with your 47 variable name, and **NUM** with a number): 48 49 * `VAR--`, subtract 1 from VAR 50 * `VAR++`, add 1 to VAR 51 * `VAR -= NUM`, subtract NUM from VAR 52 * `VAR += NUM`, add NUM to VAR 53 * `VAR /= NUM`, divide VAR by NUM 54 * `VAR *= NUM`, multiply VAR by NUM 55 56 eg 57 58 ``` 59 » let i=0 60 » let i++ 61 » $i 62 1 63 64 » let i+=8 65 » $i 66 9 67 68 » let i/=3 69 3 70 ``` 71 72 Please note these operators are not supported by `=`. 73 74 ### Variables 75 76 There are two ways you can use variables with the math functions. Either by 77 string interpolation like you would normally with any other function, or 78 directly by name. 79 80 String interpolation: 81 82 ``` 83 » set abc=123 84 » = $abc==123 85 true 86 ``` 87 88 Directly by name: 89 90 ``` 91 » set abc=123 92 » = abc==123 93 false 94 ``` 95 96 To understand the difference between the two, you must first understand how 97 string interpolation works; which is where the parser tokenised the parameters 98 like so 99 100 ``` 101 command line: = $abc==123 102 token 1: command (name: "=") 103 token 2: parameter 1, string (content: "") 104 token 3: parameter 1, variable (name: "abc") 105 token 4: parameter 1, string (content: "==123") 106 ``` 107 108 Then when the command line gets executed, the parameters are compiled on demand 109 similarly to this crude pseudo-code 110 111 ``` 112 command: "=" 113 parameters 1: concatenate("", GetValue(abc), "==123") 114 output: "=" "123==123" 115 ``` 116 117 Thus the actual command getting run is literally `123==123` due to the variable 118 being replace **before** the command executes. 119 120 Whereas when you call the variable by name it's up to `=` or `let` to do the 121 variable substitution. 122 123 ``` 124 command line: = abc==123 125 token 1: command (name: "=") 126 token 2: parameter 1, string (content: "abc==123") 127 ``` 128 129 ``` 130 command: "=" 131 parameters 1: concatenate("abc==123") 132 output: "=" "abc==123" 133 ``` 134 135 The main advantage (or disadvantage, depending on your perspective) of using 136 variables this way is that their data-type is preserved. 137 138 ``` 139 » set str abc=123 140 » = abc==123 141 false 142 143 » set int abc=123 144 » = abc==123 145 true 146 ``` 147 148 Unfortunately is one of the biggest areas in Murex where you'd need to be 149 careful. The simple addition or omission of the dollar prefix, `$`, can change 150 the behavior of `=` and `let`. 151 152 ### Strings 153 154 Because the usual Murex tools for encapsulating a string (`"`, `'` and `()`) 155 are interpreted by the shell language parser, it means we need a new token for 156 handling strings inside `=` and `let`. This is where backtick comes to our 157 rescue. 158 159 ``` 160 » set str abc=123 161 » = abc==`123` 162 true 163 ``` 164 165 Please be mindful that if you use string interpolation then you will need to 166 instruct `=` and `let` that your field is a string 167 168 ``` 169 » set str abc=123 170 » = `$abc`==`123` 171 true 172 ``` 173 174 ### Best practice recommendation 175 176 As you can see from the sections above, string interpolation offers us some 177 conveniences when comparing variables of differing data-types, such as a `str` 178 type with a number (eg `num` or `int`). However it makes for less readable code 179 when just comparing strings. Thus the recommendation is to avoid using string 180 interpolation except only where it really makes sense (ie use it sparingly). 181 182 ### Non-boolean logic 183 184 Thus far the examples given have been focused on comparisons however `=` and 185 `let` supports all the usual arithmetic operators: 186 187 ``` 188 » = 10+10 189 20 190 191 » = 10/10 192 1 193 194 » = (4 * (3 + 2)) 195 20 196 197 » = `foo`+`bar` 198 foobar 199 ``` 200 201 ### Read more 202 203 Murex uses the [govaluate package](https://github.com/Knetic/govaluate). More information can be found in it's manual. 204 205 ### Type Annotations 206 207 When `set` or `global` are used as a function, the parameters are passed as a 208 string which means the variables are defined as a `str`. If you wish to define 209 them as an alternate data type then you should add type annotations: 210 211 ``` 212 » set int age = 30 213 ``` 214 215 (`$age` is an integer, `int`) 216 217 ``` 218 » global bool dark_theme = true 219 ``` 220 221 (`$dark_theme` is a boolean, `bool`) 222 223 When using `set` or `global` as a method, by default they will define the 224 variable as the data type of the pipe: 225 226 ``` 227 » open example.json -> set: file 228 ``` 229 230 (`$file` is defined a `json` type because `open` wrote to `set`'s pipe with a 231 `json` type) 232 233 You can also annotate `set` and `global` when used as a method too: 234 235 ``` 236 out 30 -> set: int age 237 ``` 238 239 (`$age` is an integer, `int`, despite `out` writing a string, `str, to the pipe) 240 241 > `export` does not support type annotations because environmental variables 242 > must always be strings. This is a limitation of the current operating systems. 243 244 ### Scoping 245 246 Variable scoping is simplified to three layers: 247 248 1. Local variables (`set`, `!set`, `let`) 249 2. Global variables (`global`, `!global`) 250 3. Environmental variables (`export`, `!export`, `unset`) 251 252 Variables are looked up in that order of too. For example a the following 253 code where `set` overrides both the global and environmental variable: 254 255 ``` 256 » set foobar=1 257 » global foobar=2 258 » export foobar=3 259 » out $foobar 260 1 261 ``` 262 263 #### Local variables 264 265 These are defined via `set` and `let`. They're variables that are persistent 266 across any blocks within a function. Functions will typically be blocks 267 encapsulated like so: 268 269 ``` 270 function example { 271 # variables scoped inside here 272 } 273 ``` 274 275 ...or... 276 277 ``` 278 private example { 279 # variables scoped inside here 280 } 281 282 ``` 283 284 ...however dynamic autocompletes, events, unit tests and any blocks defined in 285 `config` will also be triggered as functions. 286 287 Code running inside any control flow or error handing structures will be 288 treated as part of the same part of the same scope as the parent function: 289 290 ``` 291 » function example { 292 » try { 293 » # set 'foobar' inside a `try` block 294 » set foobar=example 295 » } 296 » # 'foobar' exists outside of `try` because it is scoped to `function` 297 » out $foobar 298 » } 299 example 300 ``` 301 302 Where this behavior might catch you out is with iteration blocks which create 303 variables, eg `for`, `foreach` and `formap`. Any variables created inside them 304 are still shared with any code outside of those structures but still inside the 305 function block. 306 307 Any local variables are only available to that function. If a variable is 308 defined in a parent function that goes on to call child functions, then those 309 local variables are not inherited but the child functions: 310 311 ``` 312 » function parent { 313 » # set a local variable 314 » set foobar=example 315 » child 316 » } 317 » 318 » function child { 319 » # returns the `global` value, "not set", because the local `set` isn't inherited 320 » out $foobar 321 » } 322 » 323 » global $foobar="not set" 324 » parent 325 not set 326 ``` 327 328 It's also worth remembering that any variable defined using `set` in the shells 329 FID (ie in the interactive shell) is localised to structures running in the 330 interactive, REPL, shell and are not inherited by any called functions. 331 332 #### Global variables 333 334 Where `global` differs from `set` is that the variables defined with `global` 335 will be scoped at the global shell level (please note this is not the same as 336 environmental variables!) so will cascade down through all scoped code-blocks 337 including those running in other threads. 338 339 #### Environmental variables 340 341 Exported variables (defined via `export`) are system environmental variables. 342 Inside Murex environmental variables behave much like `global` variables 343 however their real purpose is passing data to external processes. For example 344 `env` is an external process on Linux (eg `/usr/bin/env` on ArchLinux): 345 346 ``` 347 » export foo=bar 348 » env -> grep foo 349 foo=bar 350 ``` 351 352 ### Function Names 353 354 As a security feature function names cannot include variables. This is done to 355 reduce the risk of code executing by mistake due to executables being hidden 356 behind variable names. 357 358 Instead Murex will assume you want the output of the variable printed: 359 360 ``` 361 » out "Hello, world!" -> set hw 362 » $hw 363 Hello, world! 364 ``` 365 366 On the rare occasions you want to force variables to be expanded inside a 367 function name, then call that function via `exec`: 368 369 ``` 370 » set cmd=grep 371 » ls -> exec $cmd main.go 372 main.go 373 ``` 374 375 This only works for external executables. There is currently no way to call 376 aliases, functions nor builtins from a variable and even the above `exec` trick 377 is considered bad form because it reduces the readability of your shell scripts. 378 379 ### Usage Inside Quotation Marks 380 381 Like with Bash, Perl and PHP: Murex will expand the variable when it is used 382 inside a double quotes but will escape the variable name when used inside single 383 quotes: 384 385 ``` 386 » out "$foo" 387 bar 388 389 » out '$foo' 390 $foo 391 392 » out %($foo) 393 bar 394 ``` 395 396 ## See Also 397 398 * [Reserved Variables](../user-guide/reserved-vars.md): 399 Special variables reserved by Murex 400 * [Variable and Config Scoping](../user-guide/scoping.md): 401 How scoping works within Murex 402 * [`%(Brace Quote)`](../parser/brace-quote.md): 403 Initiates or terminates a string (variables expanded) 404 * [`=` (arithmetic evaluation)](../parser/equ.md): 405 Evaluate a mathematical function (deprecated) 406 * [`[ Index ]`](../parser/item-index.md): 407 Outputs an element from an array, map or table 408 * [`[[ Element ]]`](../parser/element.md): 409 Outputs an element from a nested structure 410 * [`export`](../commands/export.md): 411 Define an environmental variable and set it's value 412 * [`expr`](../commands/expr.md): 413 Expressions: mathematical, string comparisons, logical operators 414 * [`global`](../commands/global.md): 415 Define a global variable and set it's value 416 * [`if`](../commands/if.md): 417 Conditional statement to execute different blocks of code depending on the result of the condition 418 * [`set`](../commands/set.md): 419 Define a local variable and set it's value 420 421 <hr/> 422 423 This document was generated from [builtins/core/typemgmt/math_doc.yaml](https://github.com/lmorg/murex/blob/master/builtins/core/typemgmt/math_doc.yaml).