
Dnjs: A pure subset of JavaScript that wants to replace configuration languages - tudorizer
https://github.com/leontrolski/dnjs
======
anderskaseorg
Other pure languages with similar goals:
[https://cuelang.org/](https://cuelang.org/), [https://dhall-
lang.org/](https://dhall-lang.org/),
[https://gcl.readthedocs.io/](https://gcl.readthedocs.io/),
[https://jsonnet.org/](https://jsonnet.org/).

~~~
arethuza
Interesting use by \ by dhall to define functions - though you can use λ as
well.

~~~
trumpeta
It's from haskell, which uses \ to define lambdas

~~~
arethuza
Thanks, in case anyone else is interested:

[https://wiki.haskell.org/Anonymous_function](https://wiki.haskell.org/Anonymous_function)

------
4WIW
Creating languages is fun but writing code in a half-assed language created by
brilliant but not too experienced engineer with no real support for anything,
is not! Then, when this engineer leaves your company, you are left with a mess
to clean up...

IMO better strategy would be to reduce config files to absolute minimum and
let the code that reads them handle all the complexity. Whatever is your main
language, it is better supported than any of this.

~~~
mypalmike
Yeah at one job I worked, a smart but then-inexperienced guy had developed a
language, implemented a transpiler, written a bunch of tooling in the
language, and left the company. We'll call him "Steve". By the time I got
there it had been largely replaced, but one still heard rumors about the
dreaded "Stevecode".

~~~
dkersten
I wrote a little mini language[1] for a project last year (I only inflicted it
on myself, not any poor user). It served its purpose well: allowing me to
quickly implement people's requests in a way that was sandboxed/isolated from
other people's accounts, was impossible to take the system down no matter how
bad the bugs, and I could implement these requests without redeploying the
underlying system. In that respect, it was a huge success (and that's what it
was for). But in every other way, I think it was a failure: I came to hate
writing code in it, despite having designed it. It had missing language
features that I never got around to implementing, strange bugs that I didn't
have time to fix (so just avoided doing those things), nobody else but me
could use it (the project was just me developing, but the users couldn't use
it either despite having access to it. It replaced a simple but very limited
command language, but in doing so added too much real programming). So while
it succeeded in what it set out to do, it wasn't a sustainable or future proof
solution. I did learn what users actually wanted though and got to
test/prototype it before adding it to the underlying system. I'm also working
on a declarative replacement now (basically a TOML file where values can be
expressions -- a template system), that supports everything the users actually
want, but is vastly simpler and has much less custom logic.

It was a great learning experience though and I'm glad I did it, both in terms
of learning how to write grammars (I used Clojure's Instaparse) and
interpreters, and in learning about the users needs and wants.

But I shudder to think about if anybody else would have had to use it...

[1] Some interesting (IMHO) technical details: it was a synchronous language
(of the transformational system variety), meaning that while it was executing,
time was essentially frozen: it would gather all of its inputs, then evaluate
the code as a pure transformation of its inputs (made it very easy to
interpret!) and then write its output. The inputs are essentially immutable
while the evaluation is happening. This was run in a database transaction for
consistency. Each individual script was basically stop-the-world non-
concurrent, but the scripts were independent and many could execute at once.
I'm a very big fan of event driven synchronous languages.

~~~
4WIW
Thanks for sharing. There are often good reasons for creating DSLs, internal
or external.

However I argue that I haven't seen many cases where configuration DSL were
well justified.

By configuration here I mean something that describes differences between
different deployed instances of the same code. If you take all deployed
instances of the existing system and see what is minimal amount of
configuration to describe these, you would usually find that very little
information, just a few lines would be enough.

The rest of configuration file complexity comes from the designer's attempts
to predict the future extensions. My point is, these should be resisted for
the sake of future generations of maintainers.

~~~
dkersten
What do you think of the HashiCorp configuration language (HCL)?

Overall, I completely agree with you, but I do find that HCL seems to be a
pretty good fit for what its used for (at least for Terraform, which I have
more experience with than their other tools).

My little story was really just me dumping my thoughts after reading about
Stevecode, as someone who made a little language (not a configuration language
though) and isn't an argument for doing so at all. I'm definitely in favour of
sticking with an existing format for configuration (personally, I like to use
TOML or EDN for my own projects' configuration needs, or JSON if its not
normally meant to be edited by humans) and even for scripting, normally I'd
stick with Lua or Javascript. My language came into existence because neither
of those was sandboxed in the way I wanted (it was also deterministic and
guaranteed to halt as there were no unbounded loops).

~~~
4WIW
Hah...Terraform is called "Infrastructure as code", so it's HCL language is
"code", not configuration, at least by my definition.

The whole point of configuration is that it's not code.

I use a simple litmus test: can your system work at least to some extent with
empty configuration file? If not, you went too far and need to simplify.

------
flying_sheep
I wouldn't use any turing-complete language as configuration :-/

Determinism is essential for a configuration language. I don't want to load
configurations that may or may not crash your computer.

~~~
anderskaseorg
Turing-completeness, determinism, and crash safety are three independent
properties; a given language may have any 0, 1, 2, or 3 of them.

~~~
Gehinnn
usually, a freeze is considered a crash too. In this sense, you cannot be
turing complete and crash safe.

~~~
Dylan16807
You can be turing complete without meaningful freezes. You can be non turing
complete with meaningful freezes.

------
Nihilartikel
I feel like clojurescript but with all mutable functions removed would be a
good option.

You get a cleaner literal syntax than Json, and easily composable functions.

The hiccup list syntax for html in clojure is a joy to work with.

~~~
keymone
Just EDN would be good start. When more dynamism is needed - it’s already a
Clojure subset.

~~~
dkersten
EDN with a few added reader tags is great for configuration. I love Duct[1]
configs: its just EDN, but it adds tags for including other files, reading
environment variables, referencing other keys in the config (that's added by
Integrant[2] not Duct itself): [https://github.com/duct-
framework/duct/wiki/Configuration](https://github.com/duct-
framework/duct/wiki/Configuration)

[1] [https://github.com/duct-framework/duct](https://github.com/duct-
framework/duct)

[2]
[https://github.com/weavejester/integrant](https://github.com/weavejester/integrant)

------
leontrolski
Author here, thanks for the feedback. This is v. much a work-in-progress, I'm
in the process of trying to build something non-trivial with it. Just a couple
of things to address in general from the comments so far:

\- I'm aware of dhall, jsonnet and friends, I think they're cool, but I think
the most important for me is this is _just js_ \- that means nothing new to
learn + you can use it from front end code + free linters etc.

\- I think the to-html bit is important, this is something I tangentially
talked about in more detail here - [https://leontrolski.github.io/dom-
syntax.html](https://leontrolski.github.io/dom-syntax.html). There was some
discussion on hacker news about that one too, again, I think the important
"feature" is that it's _just js_. Again, to me that's really important, so
hiccup, jsx, etc don't cut it.

~~~
leontrolski
To clarify, the non-trivial thing I'm building is more for exploring the
"unified templating language for backend and frontend" bit. I want to be able
to use the same code to:

\- render entire pages from a Python backend

\- use as components in mithril
[https://mithril.js.org/](https://mithril.js.org/)

------
jmull
By now I shouldn't be, but I'm always a little surprised when people come to
the conclusion that the best way to represent HTML is JSON or Javascript (or
anything but HTML).

~~~
throwaway894345
Why is XML a better way to represent HTML than any other hierarchical data
representation language? “JSON doesn’t have Multiline strings” would be a good
argument, but there are lots of good reasons that XML is a bad format (overly
verbose, overly complicated, parsers frequently have security vulnerabilities,
etc etc).

~~~
monadic2
Element attributes are awkward to express in JSON, at the very least. Maybe if
the markup language was originally designed around json it would be a more
natural fit.

~~~
throwaway894345
I think this is fine:

    
    
        {
            “type”: “div”,
            “style”: “...”,
            “children”: [ ... ]
        }
    

If you really want to treat attributes specially you could put them under an
“attributes” key:

    
    
        {
            “type”: “div”,
            “attributes”: {“style”: “...”},
            “children”: [ ... ]
        }

------
femto113
I do think that something that's enough more than JSON to make it more usable
for config (comments, imports, reference env vars, etc) would be great to
have, but I don't see the two goals (config and templates) of dnjs as
particularly compatible. HOCON[1] is the best attempt at a "better JSON" that
I've seen but it's cross support of Java properties syntax makes it a little
weird to use in a JavaScript centric stack.

[1]
[https://github.com/lightbend/config/blob/master/HOCON.md](https://github.com/lightbend/config/blob/master/HOCON.md)

------
lucasmullens
I agree that there's a ton of value in only using a subset of JavaScript, but
wouldn't this be better implemented as a set of lint rules?

Are there other advantages to a custom runtime that I'm missing? Even if it's
only a subset of the language, the Python runtime probably isn't faster than
Node.js.

------
verdverm
[https://cuelang.org](https://cuelang.org) is what I use. Wrap code in data,
not data with code

------
throwaway894345
This seems a lot like Starlark (go.starlark.net). Apart from the JS vs Python
dialectal distinction, are there other differences?

------
bravura
I just wanted to say that hydra is a mature, supported, well designed config
library for python:

[https://hydra.cc/docs/intro](https://hydra.cc/docs/intro)

Great for machine learning and generic config too

~~~
q3k
> defaults: [db: mysql] is a special directive telling Hydra to use
> db/mysql.yaml when composing the configuration object.

Oh no. This sort of implicit magic has no place in my hear and my
configuration files.

------
hans_castorp
I was immediately reminded of [https://xkcd.com/927/](https://xkcd.com/927/)

------
aronpye
Can someone explain why you wouldn’t use python or lua, or even just plain JS?

~~~
q3k
I don't understand dnjs either, but the reasons I use jsonnet over a
mainstream language (like Python, Lua or JS) for configuration:

    
    
       - purity (within bounds of the local filesystem state; no dynamically computed imports; no networking; no file writes)
       - declarative, lazily evaluated
       - override syntax (allowing for a balance between explicitly defined APIs and some level of monkeypatching)
       - file-relative import syntax
       - simple interpreter that's easily embeddable into tools (no modules, no interpreter path, no global configuration files, etc)

------
darawk
This seems neat. I feel like i've re-invented less complete versions of this
dozens of times.

------
anentropic
FYI some of the python imports in the README are like: `from djns`

I think that's a typo?

