detailed.md (5892B)
1 # Detailed Examples 2 3 ## Type Conversions 4 5 ### Nickel to Julia Type Mapping 6 7 | Nickel Type | Julia Type | 8 |:------------|:-----------| 9 | Null | `nothing` | 10 | Bool | `Bool` | 11 | Number (integer) | `Int64` | 12 | Number (float) | `Float64` | 13 | String | `String` | 14 | Array | `Vector{Any}` | 15 | Record | `Dict{String, Any}` | 16 | Enum (tag only) | `NickelEnum(tag, nothing)` | 17 | Enum (with argument) | `NickelEnum(tag, arg)` | 18 19 ### Typed Evaluation 20 21 Pass a type as the second argument to `nickel_eval` to convert the result: 22 23 ```julia 24 nickel_eval("42", Int) # => 42::Int64 25 nickel_eval("3.14", Float64) # => 3.14::Float64 26 nickel_eval("\"hello\"", String) # => "hello"::String 27 ``` 28 29 #### Typed Dicts 30 31 ```julia 32 nickel_eval("{ a = 1, b = 2 }", Dict{String, Int}) 33 # => Dict{String, Int64}("a" => 1, "b" => 2) 34 35 nickel_eval("{ x = 1.5, y = 2.5 }", Dict{Symbol, Float64}) 36 # => Dict{Symbol, Float64}(:x => 1.5, :y => 2.5) 37 ``` 38 39 #### Typed Vectors 40 41 ```julia 42 nickel_eval("[1, 2, 3]", Vector{Int}) 43 # => [1, 2, 3]::Vector{Int64} 44 45 nickel_eval("[\"a\", \"b\", \"c\"]", Vector{String}) 46 # => ["a", "b", "c"]::Vector{String} 47 ``` 48 49 #### NamedTuples 50 51 Records can be converted to `NamedTuple` for convenient field access: 52 53 ```julia 54 server = nickel_eval( 55 "{ host = \"localhost\", port = 8080 }", 56 @NamedTuple{host::String, port::Int} 57 ) 58 server.host # => "localhost" 59 server.port # => 8080 60 ``` 61 62 ## Enums — `NickelEnum` 63 64 Nickel enums are represented as `NickelEnum`, a struct with two fields: 65 - `tag::Symbol` — the variant name 66 - `arg::Any` — the payload (`nothing` for bare tags) 67 68 ### Simple Enum Tags 69 70 ```julia 71 result = nickel_eval("let x = 'Foo in x") 72 result.tag # => :Foo 73 result.arg # => nothing 74 ``` 75 76 `NickelEnum` supports direct comparison with `Symbol`: 77 78 ```julia 79 result == :Foo # => true 80 result == :Bar # => false 81 ``` 82 83 ### Enums with Arguments 84 85 Enum variants can carry a value of any type: 86 87 ```julia 88 # Integer payload 89 result = nickel_eval("let x = 'Some 42 in x") 90 result.tag # => :Some 91 result.arg # => 42 92 93 # String payload 94 result = nickel_eval("let x = 'Message \"hello\" in x") 95 result.tag # => :Message 96 result.arg # => "hello" 97 98 # Record payload 99 result = nickel_eval("let x = 'Ok { value = 123, status = \"done\" } in x") 100 result.tag # => :Ok 101 result.arg["value"] # => 123 102 result.arg["status"] # => "done" 103 104 # Array payload 105 result = nickel_eval("let x = 'Batch [1, 2, 3] in x") 106 result.arg # => Any[1, 2, 3] 107 ``` 108 109 ### Nested Enums 110 111 Enums can appear inside records, arrays, or other enums: 112 113 ```julia 114 # Enum inside a record 115 result = nickel_eval("{ status = 'Active, result = 'Ok 42 }") 116 result["status"] == :Active # => true 117 result["result"].arg # => 42 118 119 # Enum inside another enum 120 result = nickel_eval("let x = 'Container { inner = 'Value 42 } in x") 121 result.tag # => :Container 122 result.arg["inner"].tag # => :Value 123 result.arg["inner"].arg # => 42 124 125 # Array of enums 126 result = nickel_eval("let x = 'List ['Some 1, 'None, 'Some 3] in x") 127 result.arg[1].tag # => :Some 128 result.arg[1].arg # => 1 129 result.arg[2] == :None # => true 130 ``` 131 132 ### Pattern Matching 133 134 Nickel's `match` resolves before reaching Julia — you get back the matched value: 135 136 ```julia 137 result = nickel_eval(""" 138 let x = 'Some 42 in 139 x |> match { 140 'Some v => v, 141 'None => 0 142 } 143 """) 144 # => 42 145 146 result = nickel_eval(""" 147 let x = 'Some 42 in 148 x |> match { 149 'Some v => 'Doubled (v * 2), 150 'None => 'Zero 0 151 } 152 """) 153 result.tag # => :Doubled 154 result.arg # => 84 155 ``` 156 157 ### Pretty Printing 158 159 `NickelEnum` displays in Nickel's own syntax: 160 161 ```julia 162 repr(nickel_eval("let x = 'None in x")) # => "'None" 163 repr(nickel_eval("let x = 'Some 42 in x")) # => "'Some 42" 164 repr(nickel_eval("let x = 'Msg \"hi\" in x")) # => "'Msg \"hi\"" 165 ``` 166 167 ### Real-World Patterns 168 169 #### Result Type 170 171 ```julia 172 code = """ 173 let divide = fun a b => 174 if b == 0 then 175 'Err "division by zero" 176 else 177 'Ok (a / b) 178 in 179 divide 10 2 180 """ 181 result = nickel_eval(code) 182 result == :Ok # => true 183 result.arg # => 5 184 ``` 185 186 #### Option Type 187 188 ```julia 189 code = """ 190 let find = fun arr pred => 191 let matches = std.array.filter pred arr in 192 if std.array.length matches == 0 then 193 'None 194 else 195 'Some (std.array.first matches) 196 in 197 find [1, 2, 3, 4] (fun x => x > 2) 198 """ 199 result = nickel_eval(code) 200 result == :Some # => true 201 result.arg # => 3 202 ``` 203 204 #### State Machine 205 206 ```julia 207 result = nickel_eval(""" 208 let state = 'Running { progress = 75, task = "downloading" } in state 209 """) 210 result.tag # => :Running 211 result.arg["progress"] # => 75 212 result.arg["task"] # => "downloading" 213 ``` 214 215 ## Export Formats 216 217 Evaluate Nickel code and serialize to JSON, TOML, or YAML: 218 219 ```julia 220 nickel_to_json("{ name = \"myapp\", version = \"1.0\" }") 221 # => "{\n \"name\": \"myapp\",\n \"version\": \"1.0\"\n}" 222 223 nickel_to_yaml("{ name = \"myapp\", version = \"1.0\" }") 224 # => "name: myapp\nversion: '1.0'\n" 225 226 nickel_to_toml("{ name = \"myapp\", version = \"1.0\" }") 227 # => "name = \"myapp\"\nversion = \"1.0\"\n" 228 ``` 229 230 ## File Evaluation with Imports 231 232 Nickel files can import other Nickel files. `nickel_eval_file` resolves imports relative to the file's directory. 233 234 Given these files: 235 236 ```nickel 237 # shared.ncl 238 { 239 project_name = "MyProject", 240 version = "1.0.0" 241 } 242 ``` 243 244 ```nickel 245 # config.ncl 246 let shared = import "shared.ncl" in 247 { 248 name = shared.project_name, 249 version = shared.version, 250 debug = true 251 } 252 ``` 253 254 ```julia 255 config = nickel_eval_file("config.ncl") 256 config["name"] # => "MyProject" 257 config["version"] # => "1.0.0" 258 config["debug"] # => true 259 ``` 260 261 Subdirectory imports also work: 262 263 ```nickel 264 # lib/utils.ncl 265 { 266 helper = fun x => x * 2 267 } 268 ``` 269 270 ```nickel 271 # main.ncl 272 let utils = import "lib/utils.ncl" in 273 { result = utils.helper 21 } 274 ``` 275 276 ```julia 277 nickel_eval_file("main.ncl") 278 # => Dict("result" => 42) 279 ```