The `if-let` construct - together with the fact that only `nil` and `false` are falsey - is very handy to express monadic chains over maybe/optional values:
```janet
(if-let [resp (make-http-request) # is nil on failure
{:status stat :body body} resp # always truthy, binds `stat` and `body`
check (= 200 stat) # continue only on 200 OK
json (json/decode body true) # is nil on failure
{:foo foo} json]
foo)
```
However, none of the bindings are available in the false branch, which
could be helpful to have for error reporting or fallback logic:
```janet
(if-let [resp (make-http-request)
{:status stat :body body} resp
check (= 200 stat)
json (json/decode body true)
{:foo foo} json]
foo
(if (= 401 stat) # unauthorized, refresh the token
(try-again)
nil))
```
This PR makes all symbols introduced by the bindings of an `if-let` available in all branches.
Also match shell convention, with strict searching from left to right
instead of right to left by default. The _first_ element will be bound
to (dyn *syspath*), not the last.
Potentially breaking change for complicated, custom setups
Rather than needing to create wrapper scripts for utilities, `janet -t
mylib <args...>` can be a simple, if somewhat awkward, alternative for
portable command line tooling. Also use spork/fmt on boot.janet.
This also improves the error behavior of malformed automatic parameters
so that the error conditions are better defined. Previously, large
indices like 88888888888888 would actually simply be converted to
symbols and fail later. This change puts strict limitations on this
conversion process - If a symbol is of the form "$<str>" and
(scan-number str) returns non-nil, the result must be a valid index. In
this way, symbols like `$1.1` will result in an immediate macro expansion error
rather than (usually) and unknown binding error.
Prevent redefining bindings by accident. There are
a few cases where we want to allow this, such as the `default` macro, so
we allow a keyword :shadow to be included in the `def` expression to
turn off this lint.
TODO:
* Clean up test suite to remove binding shadowing
* Make sure that we don't get lints with *redef* turned on
* Add positive and negative tests for lint messages.
* Add location of shadowed binding in message
For unclosable files, no need to dup, but for closable files we can get
a resource leak. Valgrind and similar tools won't catch this but IO will
unexpectedly start going to the wrong descriptor if a file was
transferred to a new thread, closed, and then a new file was created.
E.g.
(import project/file.janet :as file)
(import ./project/file.janet :as file)
(import /home/me/project/file.janet :as file)
Will now do what you expect. When loading with a file extension, this
will first check directly if the relative (or absolute) path exists.
Also expands `module/find` to be able to show all matches for a given
module name without needing to try and catch import errors. Lastly, one
can also override the module/find functionality by passing `:loader`
directly.
E.g.
(import ./macoslib.dylib :loader :native :as my-thing)
Which will allow easily bypassing all module/find logic;
"./macoslib.dylib" will be passed directly to the :native loader
`(get module/loaders :native)`.
The module system was previously left open to customization but with
defaults to encourage a more typical style. However, these qol
improvements are more than worth it and can in some cases let people
stop fighting the module system.