Repository navigation
Variables And Secrets
Chris Michael edited this page Oct 7, 2026
·
1 revision
Loader.load answers Variables.Variables: the process environment, read once, with the loaded values settled over it. Loading never writes the process environment; the values live in this value for as long as the program keeps it, and two loads with different options can coexist. Variables.process() is the same view with no file.
A key is present when its value is set and not empty.
| Read | Answers |
|---|---|
find, has
|
the value, or None / false
|
text |
the value, or Missing([key])
|
int, float, decimal, bool
|
the converted value, Missing, or Malformed(key, kind)
|
findInt, findFloat, findDecimal, findBool
|
the converted value, or None
|
secret |
the value as a Std.App.Secret, or Missing
|
require(&keys) |
Ok(()), or Missing with every absent key in the order asked |
Whole numbers admit a sign, numbers ignore surrounding space, and a truth value is true or false in any letter case — never yes, 1, or on.
module Ex06Variables
import Std.App.Secret as Secret
import Std.Io as Io
import Std.Map as Map
import PuduLangEnvironment as Environment
import PuduLangEnvironment.Loader as Loader
import PuduLangEnvironment.Options as Options
import PuduLangEnvironment.Source as Source
import PuduLangEnvironment.Variables as Variables
fn line(text: Str) -> () { let _shown = Io.writeLine(text) }
export fn main() -> Int {
let text = "PORT=8080\nRATIO=0.75\nPRICE=19.99\nDEBUG=False\nTOKEN=tok-123\nHOST=from-file\nBLANK=\n"
let options = Options.defaults().withSources(&[Source.Text("example", text)]).withoutOverwrite()
match Loader.loadOver(&options, &Map.fromPairs(&[("HOST", "from-shell")])) {
case Ok(variables) => {
line("int PORT " + show(Variables.int(&variables, "PORT")))
line("float RATIO " + show(Variables.float(&variables, "RATIO")))
line("decimal PRICE " + show(Variables.decimal(&variables, "PRICE")))
line("bool DEBUG " + show(Variables.bool(&variables, "DEBUG")))
line("int HOST " + show(Variables.int(&variables, "HOST")))
line("findInt HOST " + show(Variables.findInt(&variables, "HOST")))
line("text HOST " + show(Variables.text(&variables, "HOST")))
line("has BLANK " + show(Variables.has(&variables, "BLANK")))
line("require " + show(Variables.require(&variables, &["PORT", "BLANK", "MISSING"])))
match Variables.secret(&variables, "TOKEN") {
case Ok(token) => line("secret TOKEN " + Secret.redact(&token))
case Err(problem) => line(Environment.describe(&problem))
}
line("applied " + show(variables.applied))
line("rendered " + show(variables))
line(Variables.describe(&variables))
0
}
case Err(_) => 1
}
}
Output:
int PORT Ok(8080)
float RATIO Ok(0.75)
decimal PRICE Ok(19.99)
bool DEBUG Ok(false)
int HOST Err(Malformed("HOST", "a whole number"))
findInt HOST None
text HOST Ok("from-shell")
has BLANK false
require Err(Missing(["BLANK", "MISSING"]))
secret TOKEN [REDACTED]
applied ["BLANK", "DEBUG", "PORT", "PRICE", "RATIO", "TOKEN"]
rendered Variables{lookup: <fn <fn>>, applied: ["BLANK", "DEBUG", "PORT", "PRICE", "RATIO", "TOKEN"], sources: ["example"], skipped: []}
6 variables applied from example; values are hidden.
HOST kept the shell's value because the load ran withoutOverwrite(); applied lists only the keys the load set. Rendering the view with show prints <fn> where the values are, because they are held behind a function.