JanetDocsSourcePlaygroundI'm feeling luckyCommunityGitHub sign in
# Consider this problem: As a module author, I want to define a dynamic # binding `:debug` to provide additional diagnostic information when an # error occurs in a module function. I'll use `defdyn` to create a public # alias for my dynamic binding: # In the file: my-mod.janet # The `defdyn` macro creates the dynamic binding :debug, and explicitly # exposes it in the module's API as `my-mod/*debug*`. (defdyn *debug*) # contrived function - the important thing is that it enables additional # diagnostic logging when the `*debug*` alias's reference dynamic binding # is true. (defn my-func [& params] (try (do (when (empty? params) (error "missing required argument"))) ([err fib] (when (dyn *debug*) (print "[diagnostics] ...")) (error (string "error occurred: " err))))) # Let's test our module from the REPL. # $ repl:1:> (import /my-mod) # $ repl:2:> (setdyn my-mod/*debug* true) # enable debug mode # $ repl:3:> (my-mod/my-func) # [diagnostics] ... # error: error occured: missing required argument # in my-func [my-mod.janet] on line 11, column 7 # in thunk [repl] (tail call) on line 4, column 1 # entering debug[1] - (quit) to exit # debug[1]:1:> # We see our diagnostic message, but we also entered Janet's built-in # debugger! That's not what we intended. Why did this happen? # It turns out that our module's `my-mod/*debug*` alias is namespaced, but the # referenced `:debug` dynamic binding is not. We've created a collision with # the `:debug` dynamic binding in the core API that activates the debugger # on error. # $ repl> *debug* :debug # $ repl> my-mod/*debug* :debug # The solution is to set the :defdyn-prefix dynamic binding to add a namespace # for all dynamic bindings in our module. We should use a reasonably unique # prefix - let's go with the name of the module's public git repo. It's okay # for the prefix to be long - it will be used as the key for the dynamic # binding in the current environment, but the user won't typically interact # with it directly. The prefix is effectively "hidden" from the user. We'll # prepend this line at the top of our module: (setdyn *defdyn-prefix* "codeberg.org/quexxon/my-mod/") # Now our `my-mod/*debug*` alias refers to an isolated, namespaced binding: # $ repl:1:> (import /my-mod) # $ repl:2:> my-mod/*debug* :codeberg.org/quexxon/my-mod/debug # $ repl:3:> (setdyn my-mod/*debug* true) # enable debug mode # $ repl:4:> (my-mod/my-func) # [diagnostics] ... # error: error occured: missing required argument # in my-func [my-mod.janet] on line 11, column 7 # in thunk [repl] (tail call) on line 4, column 1 # $ repl:5:> # Much better! We see our diagnostic message, but we didn't trigger Janet's # debugger. As a general rule, always set `*defdyn-prefix*` to a unique # value when defining dynamic bindings for your module.