Pkl, a Programming Language for Configuration
pkl-lang.org
pkl-lang.org
(via https://news.ycombinator.com/item?id=39239265, but we merged that thread hither)
https://github.com/oracle/graal/tree/master/truffle
Disclaimer: graalvm dev here.
Edit: typo
Sadly they renamed Kyber to MLKEM.
What now?
Cue chase montage shenanigans with Under The Boardwalk playing in the background
Do you smell toast.
thanks, that will help improve the output.
GraalVM is an alternate JDK that can, among other things, do ahead-of-time compilation for Java.
Truffle is a framework for building languages, that sits on top of Graal.
Futamura Projections are a particularly interesting use case for compile-time partial evaluation.
With partial evaluation you have static (known at compile time) arguments to your function, and dynamic (known at runtime) arguments.
When “your function” is an interpreter, and your static arguments are source code, the output is effectively a self-contained compiled binary.
When your function is an interpreter, and the static arguments are the source code for the interpreter itself, that “self-contained compiled binary” is now itself a compiler.
I really like Pkl's comparison page, which includes its weak points as well! https://pkl-lang.org/main/current/introduction/comparison.ht...
Pkl’s native binaries are larger than those of other config languages.
It should be as fast and easy to use and reliable as something like esbuild, so I'd suggest they may want to rewrite it in Go like esbuild. I'm not a Go fan at all, but it clearly does some things really well.
The Gradle compatibility matrix is pretty complicated: https://docs.gradle.org/current/userguide/compatibility.html...
I’ve also used Facebook’s Buck build system, as an attempt to get away from Gradle, and it’s also fussy about JDK versions.
You know that the code compiled using future version of java won' work in older versions..rt? I would like to know if any other programming language does that kind of thing.
> t should be as fast and easy to use and
How did you conclude that it's not fast? They are creating native binaries just like Go or any other AOT languages with GCs. Graal native images are as fast or faster than Go. Also it contains a REPL, that's why bigger size. So for CLI tooling as a developer using pkl, you won't see any difference if it's written in java + kotlin or golang.
Of course, but what surprises me is the lack of backwards compatibility -- future JVMs refusing the run old code. I get that you have to deprecate old unsafe APIs sometimes, but it feels silly that I need three different Java versions for different Android projects.
They are creating native binaries just like Go or any other AOT languages with GCs. Graal native images are as fast or faster than Go. Also it contains a REPL, that's why bigger size. So for CLI tooling as a developer using pkl, you won't see any difference if it's written in java + kotlin or golang.
That's good! I thought you needed Java to run it.
I figured I should give it a proper try, so I just downloaded it. 105MB!! They're not kidding when they say it's big. I also checked bun (47.7MB) and esbuild (9.8MB) for comparison.
pkl does seem to start up pretty fast, though. 1.6s on the first run (presumably just the time needed to cache that big binary) and ~100ms thereafter.
I'd actually say that our tooling in some ways is more mature. For example, I think our IDE experience (at least in JetBrains editors) is the best out there.
Apple employees can rate it as an old project among themselves, while it is more convenient for everyone else to rate the product from the moment of publication.
It seems like they did not aim to make yet another mvp configuration language, but something that can scale across a wide range of usage scenarios spanning all the way from short-lived processes reading a number from a file to huge libraries of default/override hierarchies. Lack of universality sets an upper bound for the value of a configuration language, particularly when seen through the lens of configuring heterogeneous tech stacks.
wtf is Graal? That sounds like a supporting character from Beowulf.
Polyglot and native compilation enabled runtime for JVM, can run Js, Python, Ruby and more.
That's pretty clever... How is this implemented in actual code though? I can't even begin to imagine how that magic machinery works.
I know it's partly on me for not knowing the domain, but I honestly suspected somebody is trying to make fun of me with some concentrated technobabble.
Especially since I wouldn't expect the topic (configuration languages) to require complex mathematical machinery to work with. Now I have something interesting to dig into.
Let me tell about a revolutionary device called a Turbo encabulator.
One of the races in the books was the Anthropophagi (basically modeled on New Guinea headhunters). They talked like that.
Even now the OG comment says “Fuamura” but the quote in the GP comment has the original “Futurama” written in it.
https://en.wikipedia.org/wiki/Partial_evaluation#Futamura_pr...
Especially the Futamura projections. It's almost magic and very few people have even heard of them.
I once saw a demo where someone took a simple operation in Ruby using inefficient-but-elegant syntax (including creating and sorting an array, where a simple loop would have been the appropriate approach in C). He compiled that using TruffleRuby and the entire array construction and sorting was completely optimized out of the generated bytecode.
Here is one of the many: https://youtu.be/bf5pQVgux3c?si=S8Dm5d_GXYXgJtnY
If you go looking for more you will find many more marbles.
And the (original, I think), Pokémon or Tech Term: https://docs.google.com/forms/d/e/1FAIpQLSfsG7AEFLvlW68aIVIs...
not Futurama :D
I think that too, "Futamura projections" are important but they are very very far from "complex mathematical machinery" as you may hear it. They are indeed very simple (even mathematically trivial) and require no special background to understand.
I ask cause the Project Galahad page on openjdk.org is a bit sparse on details.
Some background on the recent changes there: https://medium.com/graalvm/truffle-unchained-13887b77b62c
In other words antlr and truffle are a great fit. We even use this pairing for our example language simplelanguage.
Of course, I know those two frameworks are one of the engineering marble of the age and would understand even if they decided to go without any concrete needs for it.
Today we have a programming language coming as a 87 MB binary to create config files. And to run that programming language you need to manually crate a ... config file.
So what we are missing now is a 500GB framework that can write the config file for the programming language that is writing a config file for the actual program I wish to use.
I am sorry, but very clearly a huge chunk of today's "developers" really are in the business of creating problems.
For nested data, it doesn't matter to me if it's JSON, YAML, TOML or whatever. Just agree on ONE format.
TOML also is a good example of "creating problems instead of solutions": They deliberately (!) broke compatibility to the INI format due to "I can't stand unquoted strings". Yeah, emotional feelings about CONFIG FILE FORMATS.
And again: If your config files are so complex, how about just creating a TUI/GUI so the user can configure your program in an accessible fashion?
Anyway, yes, there is something that can be improved: Create a standard instead of adding another layer of complexity for something that should be very simple in the first place.
Yes, binary size may feel arbitrary, but it often gives a hint about the hammer's size.
INI is hard to parse because without quotes the parser would not know whether it’s a “True” as a string or True as Boolean value. Formal parser that can be included into a program as a library and would reliably generate internal config representation (e.g., read config file into an object in application memory that will be used to modify the behaviour) is a good thing and TOML helps with that while INI does not.
If you would de-serialize an ini-file into Objects in memory you would typically have meta-data about your object (RTTI/Reflection for compiled languages, given for scripting languages).
But yes, INI isn't without flaws. Parsers typically will need to accept everything from "true", "True", True, true, 1, "1" for boolean fields.
From what I see, 20+ years proven parsers are available for pretty much any language there is.
And yes, the format is a bit flawed. But it gets the job done.
There is no "ini format" though? INI is a weak set of conventions that different programs parse in a completely different way, it's the opposite of standardization.
Sounds like a social problem. How do you get _everyone_ to agree on anything?
Try taking more time to consider the problems you don’t have that others do, instead of writing anything off that doesn’t make sense to you (and simultaneously gatekeeping an entire industry)
My parents most likely would have complained that they'd need 58 floppy disks to run this programming language. :)
Because you have not run into the problems this has addressed in your career, does not mean that you know better than Apple how to solve them. In fact, it means like you are uniquely unqualified to solve them. Acting like you are in a severely condescending dismissal of the engineers who worked on this makes for boring conversation.
It's an interesting question - just how complex are our biggest configuration problems today?
https://gist.github.com/cstrahan/528b00cd5c3a22e3d8f057bb1a7...
Now consider 100s (if not 1000s) of such files.
I haven't given Pkl an in depth look yet, but I can say that the Industry Standard™ of "simple YAML" + string substitution (with delicate, error prone indentation -- since YAML is indentation sensitive) is easily beat by any of:
- https://nixos.org/manual/nix/stable/language/index.html
- (insert many more here, probably including Pkl)
Nobody is stopped from compiling code, converting it to base64 and storing it into a label for later execution.
Arbitrary parameters like this are the opposite of a unifying abstraction.
That ingress-behavior wasn‘t defined but pluggable to suit existing load balancers also broke the abstraction.
Often one will get to a very simple config format in the end. Of course, when one has to deal with very complex formats created by others, already widespread in use, on cannot easily change the format. Maybe that is the reason we get these meta config tools.
But sometimes the complexity is irreducible. Kubernetes is one such case. The model is very well thought out, and just about as simple as it could get without removing functionality. It has sensible defaults, built-in versioning, well-defined schema etc. But if you want to describe a complete installation of a distributed system with many heterogenous processes, spread across many hosts, communicating in specific ways, with specific permissions, persistence, isolation, automatic scaling, resilience, etc, there are a lot of details. I've worked with systems that have thousands of lines of configuration, and honestly that's not extraordinary. Many people on this site will rightly scoff and say, "psshh, that's nothing."
Configuration languages are a really important area of research in the tech industry right now, and every time someone posts one on here, there are a huge number of dismissive comments. Fine. Not everyone has this problem, but it's a real problem, and solving it represents a real advance in the state of the art.
They invented the Objective C and Swift, and made it pretty much impossible to use any standard language to target their platforms in a cross-platform way. I can access the Windows API with any language I want. I can access the Linux Kernel API with any language I want. I can access the BSD API with any language I want. I even can use any language I want on the C64 to call native operating system functions.
So, yes, I am taking the liberty to believe I know better than Apple. TBH I don't regard this conclusion as rocket science, so no ego involved here.
It's also open sourced under the Apache 2.0 license, which grants you the right to distribute and modify it.
Cheers
This however does not change my position that we have more than enough scripting languages focused on string operations, and don't need another language. And especially not a bloated one that itself depends on huge frameworks. If the same task can be solved using a 76KB standard Unix tool, creating a custom language with a huge bloated runtime simply can not be justified. It's bringing a sledgehammer for a task where a swiss pocket knife would be appropriate.
And my position also stands that it would make far more sense to finally agree on a config file standard format, with native parsers existing in every major programming language, so people stop re-inventing the wheel again and again and again, with the wheel becoming heavier and bulkier and less round every time.
It would also make far more sense for humans to agree on most things and work towards a common goal that benefitted us all, but that’s just as much of a pipe dream.
Proposing something that will never happen is not a practical solution.
Apple had nothing to do with the invention of Objective-C. It had been out for 12 years when Apple first started using it.
Also, since Objective-C is basically just C, you could actually target their platform with plain C if you know what you're doing. Some of the underlying frameworks (e.g. Core Foundation) are actually C APIs.
Now people are responsible for thousands of containers, and GUI simply does not scale. And the configuration of those containers is domain-specific and/or varies enough so that you cannot write a single config file and copy it over. This is why configuration management systems first came with template engines and now require a separate tool to render the configuration.
Nobody would develop anything like that if it was not needed.
If fully agree that you might need to automate editing and deploying config files. But you don't need a new programming language for that. Just use one of the thousands of existing languages and tools.
You can for example use sed. A standard since 1973. Available everywhere. 76KB in size. Extremely fast.
Or use AWK. Exists since 1977.
Or you can use Perl. Natively able to read/write ini files, and in general very good at string processing. 6MB in size (or 250KB for embedded versions).
Or use Javascript, PHP, shell scripting, whatever.
But no, there is absolutely no need to invent a new programming language for a task where there are very established tools for.
m4 just shows its age, and we’ve learned what is good (being Turing-complete) and what’s less useful (multiple output streams).
m4 could’ve evolved into m5, m6, etc., but nobody did it, instead people developed tools like jinja. One could argue, this is wrong and should’ve been evolutionary, but the point still is, domain-specific language to render configuration existed because there was use for them. Perl indeed could be such, I guess it did not happen for totally unrelated reasons. If Perl worked, Python would not gain popularity.
Pkl is not conceptually a new invention, it’s a new attempt to solve a real problem.
Have I just not thought hard enough about this problem yet, and a DSL is really needed for it? Or should I go write that lib?
(Mostly-shameless plug, this is what we're trying to solve with Kurtosis: https://github.com/kurtosis-tech/kurtosis )
Wouldn't you want to self host the config?
I'm quite happy that remix culture has finally made it to software tooling. The extra layer of abstraction is a small price to pay for the power it brings.
(I don't know Pkl, but it seems like it scratches part of the same itch that you might use nix for).
That exists since 1960. It's called LISP. The e.g. https://guix.gnu.org/ uses with great success, the Guile Scheme dialect of LISP, to be precise. And FYI the "framework" is:
$ ls --human-readable --size $(readlink $(which guile))
16K /gnu/store/1gd9nsy4cps8fnrd1avkc9l01l7ywiai-guile-3.0.9/bin/guile
Yes, only sixteen kilos, not gigas. guile=$(readlink -f $(which guile))
sizes=$({ echo $guile; ldd $guile|grep /|sed 's+^[^/]*\(/[^ ]*\).*+\1+'; }|xargs -n 1 readlink -f|xargs du -ab|cut -f 1)
sumsize=0
for size in $sizes; do
sumsize=$((sumsize+size));
done
echo $sumsize
Gives 9041632 here.Now I just need to extend your snippet so that it works recursively. A shared object can require other shared objects.
1) Code review (config code can be reviewed by humans)
2) Automated pre-submit checks (config code can passed through automated pre-submit checks - such as preventing huge changes, or giving you a nice diff to look at
3) Auditability / history-tracking (you can look at the history of a config file to see who, when, why changes were made - this may even be necessary for compliance reasons)
4) De-duplication (you can extract common components into templates/functions - this supports variation across envs/regions/customers and repetition across tasks/machines/DBs)
All of these features help build large-scale systems in modern corporate environments.
I'm sorry, but a huge chunk of todays "developers" really are in the business of creating problems.
Of course tinkerers made some tools you can tinker with as time went on, but you don’t have to let those interfere with velocity, just like I will not be using configuration languages.
I used to see this sort of thing a lot at Apple and I used to classify the people concerned as those that work AT Apple rather than people that work FOR Apple.
My team migrated several kloc k8s configuration to pkl with great success. Internally we used to write alert definitions in pkl and it would generate configuration for 2 different monitoring tools, a pretty static documentation site and link it all together nicely.
Would gladly recommend this to anyone and I’m excited to be able to use this again.
https://github.com/apple/pkl-k8s-examples/blob/96ba7d415a85c...
It is so sensitive that basic text editing like copy and paste, tab, in/decreasing indent never quite do what I expect in IntelliJ.
I paste parts of yaml into another yaml and it ends up somewhere unpredictable.
* XML has the advantage of being almost perfect except its too verbose and isn't JSON
* JSON has the advantage of being JSON, but is strangely broken at even basic stuff (i.e. comments, no trailing commas)
* TOML has the advantage of being "better JSON", for people who like rust, but it is still too limited for a lot of scenarios
* YAML is not as limited, but it has the advantage of giving bad people promotions and good people PTSD
* HOCON has the advantage of being the goldilocks child of JSON and YAML, but nobody read the documentation
* <YOUR PROJECT'S PL> has the advantage of being able to do anything and this is a disadvantage
* Etc... for various reasons
In all seriousness, I welcome Pkl! Configuration seems simple and I think another configuration language opens up a lot of pent-up frustration, but I am legitimately very happy to see some fresh ideas in this space.
Incorporating a collection of typed records with a modicum of behavior and logic might be the secret recipe needed to crack the code. The fact that it can produce equivalent JS tells you that they've chosen a very intelligent subset of functionality of programming language features.
My best wishes to the team!
1: https://pkl-lang.org/main/current/language-reference/index.h...
However, I haven't built anything cool like this so what do I know. I'm just procrastinating on the my personal project and browsing hacker news.
It's certainly not cross language capable but when your editor knows exactly what's there and what's not with auto completion from the config file, that was a superior experience than caring about cross language issues that may not even be a problem depending on the project.
If you must, you can easily make an identical copy of the config in the build process to convert it to another language.
a = 1;
and elsewhere:
a = 2;
The ultimate value of 'a' will depend on the order in which those statements are executed, right?
A configuration language should surface that as an error and tell you where all of the conflicting references are.
People who don't know Typescript will still have to learn a new thing in that case, so there isn't really any reason to pick TS over any other existing language
> syntax highlighting in every editor
There is a tree-sitter parser for pkl, so it'll work anywhere where tree-sitter works. And for VSCode and JetBrains stuff, they have official extensions.
Observationally, it seems that all that power eventually gets used, and then you end up with config that has complex interfaces, or becomes non-portable because it's doing arbitrary file reads, or is non-deterministic because of an ill-advised call to random or the system clock. The config then becomes something not maintainable by the rest of the team - just the few who know it.
Config languages seem to need to strike an interesting balance between being complex enough to allow for reasonable DRY code (which helps maintainability at the expense of readability), but not so complex that they're not generally-maintainable.
As for I/O concerns, run it in CI with deno and ensure there is no I/O
Configuration, by contrast, sits at the seam between two systems. It's the top-level parameterization of the abstraction, and behaves more like an API. E.g. imagine if the only way to configure Kubernetes or Docker were in language-specitic bindings - there was no such thing as a YAML lingua franca.
Whereas this documentation for Pkl is entirely about that use case.
Might be room for a tool that exposes just the configuration management side of Nix in a more approachable way... on the other hand it would be a bit silly to use nix for conf files and not also for the underlying packages.
I think your last sentence gets at that too: For Nix, configuration management is just one component of a broader and more powerful paradigm. And it seems to me like putting a square peg in a round hole (or as you say, a bit silly) to try to use it to solve this narrower and simpler problem.
But one who embraces nix fully is one who is willing to commit a lot of time to turning their back on convention. Returning to 90% of the conventions that they worked so hard to leave behind probably won't excite them.
So it's not silly, it's just that the person to do it is culturally unlikely.
"Better" in what way?
Like, maybe I agree in some sort of philosophical sense, of how it's better to learn and expand one's awareness of what exists and is possible in the world. That is, better for students, similarly to how I'd say it is better for students to learn lisp and haskell, because they'll figure out how to use javascript and python and C# or whatever as needed on the job. And I'm a believer in life-long learning, that everyone should do their damndest to carve out time to be a student at all times throughout life. So certainly I'm in favor of learning about Nix and its config structure and comparing and contrasting it to other things like this Pkl.
But I don't think the idea of using Nix's config management instead of a narrowly-targeted config management tool would be "better" in the sense of a professional figuring out how to set up or refactor the config structure within the non-Nix paradigm of the vast majority of organizations. And that's what I've been talking about here.
I'm actually a Nix kool-aid drinker, believe it or not, and I hope its paradigm will sweep the world, but I also have to do what's right by the organizations I work within at each moment in time, and being at the vanguard of the Nix paradigm has not been that, in my view, yet.
> a professional figuring out how to set up or refactor the config structure within the non-Nix paradigm of the vast majority of organizations
Then I don't think nix-as-config-only is a good move. But if you're a professional figuring out how to set up or refactor the config structure of some organization, AND for some reason you've already decided that you're going to introduce a language that nobody in the organization uses, then would I say that nix is a good choice. This is because there are probably other people in that organization that would find nix useful if they were already over the syntax adjustment phase.
If a multitool's screwdriver is just as good as a normal screwdriver (granted they're usually not), why not prefer the multitool?
Generally I'd just suggest using whatever languages are already around, config-focused or otherwise. For instance we have a lot of python in our stack so I've found a library called mergedict that gets us close enough to the composability of nix modules without adding a new language.
Yep, that was the only narrow point I was trying to make initially :)
> If a multitool's screwdriver is just as good as a normal screwdriver (granted they're usually not), why not prefer the multitool?
Under the assumption that the multitool's screwdriver is just as good, then yeah, that might make sense. But as you grant, that is usually not the case. And my original point is that this is one of those usual cases where it is not as good, not one of those rare cases where it is just as good. And the reason Nix is not just as good for this, is that it was not made for this, it was made for something different and bigger.
> For instance we have a lot of python in our stack so I've found a library called mergedict that gets us close enough to the composability of nix modules without adding a new language.
Things like mergedict, while being a common genre of solution to this problem, are not a good solution to it. They are the total mess I said I'm always keeping an eye out for better alternatives to.
I don't know yet if I think Pkl (or Cue or any of the rest) actually fit that bill, but your contention that there is no point looking for solutions to this problem short of Nix strikes me as a classic case of making perfect the enemy of good.
For example, see these CLI flags: https://pkl-lang.org/main/current/pkl-cli/index.html#common-...
And when using the different language bindings, you can specify sandboxing options directly in that library.
But perhaps this is exactly the core feature: everybody who adds pkl to their software implicitly signs up for participation in whatever configuration monstrosity the downstream stack will end up having. Based on the assumption that that it will be a monstrosity anyways, and that a uniform system would be less bad than an unstructured mess.
Next stage of concerns: if it's a heterogeneous stack that shares one configuration graph, the runtime implementation that is linked into parts of the stack can't ever be allowed to evolve. Ouch. And then there's runtime performance, I wonder if that could eventually become a factor, e.g. if short-lived go processes are involved?
It all seems surprisingly ambitious, very far from the "why not json-with-comments" that was my first reaction (shared with many I assume)
[1] digression: e.g. it reminds me of how in the dark days of peak XML java, a lot of typechecking was effectively thrown overboard because for some unfathomable reason everybody agreed that it would be nice to rearrange objects without a helpful compiler in the room
Why? Why would they not have just done the language server first (or only)? All of those have built-in support for it, so separate implementations wouldn't have been necessary; that's the point.
I just don't understand why you'd make that decision on a greenfield project today, especially if LSP support is planned at all?
(Pkl, not Apple :p)
What bout Pkl made it easier to write Terraform/K8s manifests/etc.?
Are there any reasonable examples of such?
I wish Apple would continue to open source much more. Especially old software much like Microsoft does. I would kind of enjoy a Linux Distro that can natively run old Apple software and tools.
That's not a complete list of programs Apple has open-sourced, although clearly open-source software isn't what they're best known for. But clang alone is a worthy contribution which deserves to be recognized.
"We are also releasing two other plugins: our VS Code plugin, and our neovim plugin. Today, these plugins only provide basic editing features like syntax highlighting and code folding."
How about if the LSP doesn't cut it for the kind of they IDE support they want to offer?
https://blog.jetbrains.com/platform/2023/07/lsp-for-plugin-d...
Like 'go to definition' or whatever is surely in every IDEs menu, but it can be done with LSP.
Given that writing anything in Java/Kotlin basically requires the use of IntelliJ, it’s not really surprising that a language built on top of Truffle and GraalVM, all Java technologies, would ship with an IntelliJ plugin, _but not_ an LSP. Because such an LSP would have been useless in the primary IDE used by the language developers.
So if you wanna blame anyone, blame JetBrains for deliberately making hard to use LSPs with their IDE. Presumably to build a moat around their plugin ecosystem.
[0] https://blog.jetbrains.com/platform/2023/07/lsp-for-plugin-d...
I use statically-typed programming languages, and for my purposes I'd rather have a config language where the only types are strings, arrays, and hashmaps, and then push all type validation into the parsing stage.
Representing arrays as maps would impose no additional requirements outside of validation which is already considered as part of the proposal in question.
Basically forcing everyone to learn new tooling (Pkl here, but lots of json/yaml middleware nonsense fits the bill too) just to deal with what really isn't that big of a problem seems like a bad trade.
- scalar value
- feature toggle
- URI/enum option/human readable display text
Having float/long/boolean is trivial to validate in the config language itself, and if they're useful and simple enough isn't it nice to be able to validate your config as early as possible?
Having the ability to pull all that validation forward, and into your IDE, means you can be told about invalid config as you’re writing it. To me the idea of only wanting to validate config at the last possible moment, is a bit like advocating for only using simple text editors, and replying purely on build failures for feedback. Sure you can do it, but why would subject yourself to that?
Pkl is interesting because it makes it possible to describe not just the types, but also valid configs. Effectively allowing you to ship your applications config parsing and validation into Pkl, so your code can just expect valid config. Then Pkl can expose all that parsing and validation logic into your IDE, so you get pointers as you type. Just like you do in any modern IDE for any modern language.
Those languages that arrived with the JSON hype train like yaml or toml might be great for dynamic languages, where you can load them to some native object. But in statically typed languages you are gonna declare your types, in code, anyway. So configuration providing types doesn't really do much.
"Enough" is the keyword here, time will tell I guess.
Being able to use Pkl code-gen to create language bindings means you can take any arbitrary Pkl schema and turn it into native structures in your language, typed based on the Pkl schema. Then you can let Pkl do all the heavy lifting of parsing and validating a specific config, and turning it into native objects in your language.
So no need for double declaring types. Declare them once in Pkl, generate the equivalent types in your language, then just start using those types like any other primitive. The Pkl bindings will handle the loading glue for you.
I'm not hating on Pkl here, we deserve better in this space, so I'm happy with more developments.
But xml itself was not a good language for this, because its legibility is terrible. It's just not a good format for human reading and editing. (But it also isn't a great format for machine interaction either...)
So yeah, I see it as a good thing that this seems to be able to do all that useful stuff you were doing with xml and xsd a decade (and more) ago. But it's (IMO) a much nicer way to do it.
Sadly, nobody ever cared about that.
I think the problem might be separation of concerns...
pkl comes in early, and by design is separated from your app and the details thereof. It seems good for validating high-level or external configuration constraints. But suppose you have some constraints based on implementation details of your app. Now you face a choice: eschew pkl and put that validation logic in the app where you lose the benefits of early validation or put it in pkl (if that's even possible) which implicitly makes it dependent on application implementation details. Of course, we devs aren't great at stopping to consider the deep implications of each config param we add, so which one happens in practice in each case probably depends on the dev or dev group and how much they've bought in to pkl... some will pretty much always add a config to pkl because that's what it's there for, while others will ignore pkl whenever they can. I think this is inherent in the ambiguity the choice presents. There's probably a right choice in each case, but devs will only sometimes be that careful about the location of each config validation.
That's my guess anyway, as to why the previous post wants to just put all the validation at the level it's used. If that's your rule the ambiguity is resolved and it works perfectly for config dependent on specific app concerns and pretty well for config that also has high-level or external concerns, since those are less volatile and when they do change, it generally implies app changes in any case.
My gut says pkl is over engineered for the vast majority of cases and people should not reach for it unless they have a specific problem that it will solve for them.
E.g. so you always write valid k8s manifests.
And then you can extend them with your own additional validation rules for what you think your app needs? I’ve just skimmed the docs but it seems it allows you to be as loose or as precise as possible, plus packaging and publishing those rules for others to use.
Seems kinda awesome.
Helm isn’t a configuration language, it’s a dressed up string templating system with ability to do kubectl apply.
> you can't guarantee your config is valid anyway if your config language can't see what class you're going to feed it to.
Obviously, but that’s hardly an insurmountable problem. We’ve had code gen and language introspection for decades.
A config language that does have the type information can give proper errors. Try for example terraform.
The disadvantage of typed configuration languages is they make assumptions about the format of the data. For you a "date" type might mean ISO 8601, but for me it might mean RFC 3339. Config languages that make assumptions are coupling the language and schema validation together. The alternative is to decouple them: offer a flexible configuration language with a separate schema language. The latter would let you define the schema specifically for your data for validation.
A generic date type doesn’t come with any specific string format. ISO 8601 and RFC 3339 are both ways of representing a date as a string. Which has little to do with Date as a type.
There’s also perfectly good solutions to those problems. Use a type alias to create a date type backed by a string, with formatting constraints (such as a regex). Then people can define dates using any string representation they want!
Incidentally this is exactly what Pkl lets you do. You can use Pkl schema to define in detail how you want to validate your data using various primitives like regex. And then separately create a config template that uses those custom data types. As a dev you can choose how tightly bound your config template is to a data validation schema, define all the validation in line, or import an external module to provide you with richly validated data types.
Tell that to the TOML authors [1].
It's good Pkl has string validation, but what if I don't want a string? What if I want my own literal datetime syntax, like TOML? The grammar for a configuration language could be made flexible enough to accept most anything as a value. In such a design the "string" type itself could be a regex rule defined by a schema.
Keep in mind datetime literals are just an example, the actual number of potential types is unbound.
The near-compatibility of ISO 8601 and RFC 3339 is a rich source of bugs, but that's hardly TOML's fault, and the standard is perfectly clear that the latter is used. TOML, like many configuration and data transport languages, is defined in terms of its syntax, so an abstract Date type doesn't make sense for it.
Providing a datetime format for TOML was probably the right decision, I think there are more people who complain about JSON lacking one than there are who complain about TOML having one.
Where do you store the schema?
https://github.com/madmurphy/libconfini/wiki/An-INI-critique...
This becomes a lot more painful if your work is more polyglot. If you need to define config that needs to be shared between different applications, but they're written in different languages, you'll have a much harder time. Also, say, if you need to deploy your applications to Kubernetes, and your Kubernetes specification needs to provide config files to your application, then you'll still end up in a situation where your statically typed programming language won't help. That is where something like Pkl becomes really helpful, because you have just one place to manage all that complexity--right in Pkl itself.
One thing I like to see is the direction of “declare types and validations in a single place, integrate with any language”.
My daily codebase atm has types declarations in typescript, cue, pydantic and for our database… and it’s driving me bonkers seeing as most types are already declared in Cue to start with. I played a little with packages meant to translate them i.e. Cue -> TS, but nothing worth the effort.
IMO it would be a big upside for Cue to handle these as first class language features.
1. You are limited to N evaluation/reduction steps.
2. The language doesn't include primitives like recursion or loops.
3. You can have recursion or loops, but the language makes you somehow prove that your program will terminate.
I think (1) would be fine, but I don't know any configuration languages that use this approach.
(2) is restrictive/annoying whenever you want to implement any logic in the config language library. Eg. a tool uses a homegrown data format BAML and you need to convert JSON to BAML in the config. Now either you have to write and manually call a preprocessor, or you need to use a patched version of the <config language evaluator> that will have JSON->BAML as a built-in, or you must implement JSON->BAML without loops or recursion. For a more realistic example, imagine that a certain config string has to be HTML-escaped and the config language doesn't provide a built-in for that purpose.
(3) -- you don't want it. There are languages (like Agda) that let you prove things like "this program terminates", but writing those proofs can be harder than writing the program itself.
Recursion is trickier. I think banning it or simply limiting stack depth seems fairly reasonable? In fact I’m pretty sure most Turing-complete languages have a stack depth limit, so unbounded recursion is not allowed for those either. I don’t see a limit being a problem, because again this is a config language.
I don’t see why HTML escaping needs Turing-completeness. It shouldn’t need any unbounded iteration (it should be limited to the size of the input string) or unbounded loops. In general, I can’t think of any typical data processing code where turning completeness is required, but could be wrong. Do you have any practical examples of transformations that need unbounded iteration?
First of all, let's avoid "Turing-completeness" because then we might start arguing about whether a language with unrestricted recursion is or isn't Turing-complete since there are stack depth limits / memory limits / universe will end one day / etc.
I would phrase this question as "why would HTML escaping need unrestricted recursion or loops" -- since in practice config languages either have unrestricted recursion or loops (Nickel), or they don't (CUE, Dhall, Starlark).
For HTML escaping specifically, just having `.map(f)` and `.concat` available (in functional languages), or `for char in string` (in imperative languages), would be enough.
For something like HTML un-escaping, it's already trickier. If you are using recursion, your language needs to understand the concept of the string becoming "smaller" at each step. If you are using loops, `for ... in ...` is not enough anymore.
An even trickier example would be mergesort:
merge(xs, ys) = ...
mergeSort(xs) =
let len = xs.length
left = mergeSort(xs.slice(0, len/2))
right = mergeSort(xs.slice(len/2, len))
in merge(left, right)
It might seem obvious that this should terminate, because of course `.slice` will return a smaller array, but the actual termination proof in Agda is already something I wouldn't want in my config language: <https://stackoverflow.com/a/22271690/615030>(Not to mention that this implementation is faulty and will loop at len=1.)
Limiting stack depth at [arbitrary number] -- this is similar to (1). I don't know why configuration languages don't do it, to be honest.
2a. The language includes limited primitives for recursion or loops.
If that’s done right, somehow proving that your program will terminate becomes trivial.
For example, allowing looping over a previously defined array with (key,value1) pairs to generate many more complex definitions that include common value2, value3, etc fields trivially guarantees termination, but a generic “while” loop doesn’t.
That will make you language less powerful, but IMO shouldn’t be problem for a configuration language.
In this example, I’m not sure you would even need that, as the language has ways to share common config values.
Limited recursion/iteration is ok if all you need is to fill existing values into a template and possibly reduce repetition.
But in a large system with many components I might want to take a single timestamp, parse it, and generate timestamps in five different formats for five different subcomponents.
Or I might want to generate a piece of yaml config for an Ansible playbook that needs it, and now my config language needs to know how to escape yaml strings.
Or a config for a static site generator needs to be able to calculate a hash of a local css file because I’d like to use links like `style.css?hash` (a popular cache-defeating mechanism).
Or a certain path has to be split on “.” and reversed (Java-style com.example.blah things).
Or a Unix path needs to be converted to a Windows path, except [some special-cased paths that live in a map in an adjacent config].
There are endless reasons to want arbitrary logic in my config files, beyond reducing repetition. A lot of things I’ve listed are provided as primitives in various config/templating languages, but you always end up stumbling upon something that’s not provided.
Of course, one could say “You should use a real programming language for this kind of stuff”, and I’m happy that the JavaScript ecosystem is converging on allowing .js/.ts files for configs, because that’s exactly what I want too. But I’d like to have the same features available in projects that aren’t allowed to touch JS.
Cue is not general purpose language, with emphasis on it - because it's a good thing.
Asking for upstream embedded support feels like asking for bash interpreter, why would you need it in the first place?
It's based on completely different, logic based paradigms, use it as it is meant to be used - as top level configuration aiding language. Declare policies and generation in it and interface with other languages/tooling though input/output json/yml.
I'm assuming it's becaus CUE is still unstable?
Anyway, if others are interested in CUE's LSP work, I think https://github.com/cue-lang/cue/issues/142 is the issue to subscribe to
It has always been clear that LSP was high priority, but we have many other high priority work that also needs to be done. Most of the work that we do is driven by feedback and demand from the community.
Additionally, we want to do the LSP right instead of quickly hacking something together. That requires more work than one might think.
While CUE has not reached 1.0 yet, people definitely use CUE in production and we work hard not to break any of their code. I can assure you LSP is missing simply because we had other things to tackle first and not because the language is unstable in a colloquial sense.
For it to gain more adoption it really needs a rewrite in a low-level language (C/Rust), so it can be exposed in various languages through an extension/FFI.
Getting feedback from the community about what other languages they'd want supported first would be of massive help, however.
Rust and Python would be my top picks.
For those who want the justifications for CUE, this is an excellent write up.
[1] How CUE Wins:
However it only being in Go and not implemented with some C ABI is a major downside for adoption, especially when their documentation itself for implementing the core CLI functionality in a go program (to then compile to a DLL for use in non-Go land) is pretty sparse.
1. The only way to use it is to run their Go CLI app to convert the Cue into JSON and then load that. That sucks. I want native support. Jsonnet does this a lot better (https://jsonnet.org/ref/bindings.html), and PKL at least supports 4 languages. Cue only supports Go directly. Not good.
2. Cue has a super fancy type system, but as far as I could figure out there's no way to actually take advantage of this in an IDE, which is like 60% of the benefits of fancy type systems. In a Cue document you can't say "this is the schema". XML had that decades ago (and it has awesome IDE integration with Red Hat's XML extension for VSCode). Even JSON can sort of do it via `$schema`. The docs are a bit scant but it looks like this supports it too. The fact that Cue doesn't sucks.
3. Cue is pretty much only a fancy type system. It's a really elegant and nice type system, but that's it. It doesn't even have functions. So it isn't going to help with a lot of the things that Jsonnet and PKL help with.
This is not really in the same area as Cue. It's a way more direct competitor to Jsonnet and looks better, based on my brief skim.
My only concern with these sorts of things is that they're basically a whole new programming language, but without many of the features you'd want from a real programming language. It's in an uncanny valley.
Does look nice though.
I've fallen back to YAML because at least its already used for a lot of tools, and has comments, jsonschema support in VSCode giving IDE features, language library support, yamllint, and yq for formatting/querying/mass-updating from the CLI
* JSON. No comments. Deal-breaker
* JSONC. No unique file extension so its difficult to distinguish from JSON. Poor library support due to library authors drinking the "comments are bad" koolaid.
* JSON5. This would be an excellent option IMO except that library and IDE support is not great.
* JSON6. This just complicates JSON5 for minimal benefits. Pointless.
* Cue. As described.
* Jsonnet. Pretty good option tbh! However I couldn't get the Rust library to work. It's a low level parser, seems like you can't just plug it into Serde, which is what 99% of people really want. Also I ran into the "uncanny valley" effect where you can do some things but not all. So it tricks you into writing some programmatic config (e.g. with string manipulation) but then you find you can't do that string manipulation.
* Dhall. Weird syntax (backslash to declare functions. I've also heard it is slow. Didn't try this too much.
* YAML. Obviously YAML is the worst option. However I did realise you can use it as basically JSON5 except with a different comment character, which is not too bad.
* Starlark. Actually I haven't tried this yet but it looks promising.
So yeah I have no idea at the moment.
I wonder if it would be worth defining a "YAML JSON5" format, that's basically YAML-compatible JSON5.
Note that CUE has comprehensions, which are morally (but not syntactically) functions (actually closures). They are a way to transform values (which can be types) into other values.
We are also adding real function types to CUE. At least in the beginning these functions will be written in other languages than CUE itself, however.
While we are very principled when it comes to language design, we are also very responsive to finding solutions to user's problems, and we welcome any sort of feedback, especially if it's backed by specific use cases and experiences.
As mentioned in another comment, support for languages other than Go is coming.
This use of the word is so common in certain math/category theory/compsci communities that I was not aware it was unconventional in any way. It has nothing to do with ethics.
I guess the moral of the story is that the moral of the story can be lost if people don't understand the story.
[1] https://eugeniacheng.com/wp-content/uploads/2017/02/cheng-mo...
I really don’t know why this snark is necessary.
Consider that some engineers poured a lot of heart into what they were building, and are probably excited to finally share it with the world.
I am not saying you have to love it, but just brutally putting it down with no justification seems really rough. Snark is easy.
OTOH, curious to see what advantages Pkl gains from not having the constraints of maintaining familiarity with another language.
> The implementations below are not fully compliant to the specification yet. We aim to remove the differences and provide a common test suite.
This does not inspire confidence that I could use this in a project any time soon.
Meanwhile, from what I can tell Pkl has a single Truffle implementation that currently supports 4 languages, it has a syntax that is more familiar to me as a non-Python dev, it has static typing, and it has a dedicated plugin in most IDEs (whereas Starlark just says to install the Bazel plugin). Maybe Starlark is more appealing to people writing Python or already using Bazel, but for the rest of us there's no contest right now.
> Copybara doesn't have a release process yet, so you need to compile from HEAD.
Looks like there's an Arch Linux build maintained by... somebody, but if you're not on Arch then you're going to be building Copybara with Bazel. That this works for them suggests to me that their community has a significant amount of overlap with the Bazel community, so it's not good evidence of Starlark being used outside of the Bazel world.
Lots of projects developed at Google use Starlark. Copybara is one of them. That's where the connection comes from.
Many other companies are also adopting Starlark for their own needs. For example, Meta has invested a lot in Starlark and published their implementation (https://developers.facebook.com/blog/post/2021/04/08/rust-st...), although they don't use Bazel at all.
Starlark was first created for Bazel. The organic user growth comes from people who have seen and used the language, so often Bazel users. But it doesn't have to be.
I really wish Apple would learn to play nicer with the OSS community. I have yet to see them deciding to open-source something backfire on them monetarily or reputationally, and I've seen the act of them abruptly close-sourcing things sour community opinion (i.e. FoundationDB).
Yeah, it's been a long time coming, and it feels great to finally get this out in open source.
FDB is open source too, BTW: https://github.com/apple/foundationdb
It has about exactly the same feature set. Declarative config, type definitions, data validators, reusable modules, variables, transforming functions, loops and other repeat primitives, reading external data like files and envvars, output and input json or yaml, IDE integration, you name it.
Much under-appreciated language btw. I hardly see it used outside TF. Here I think Pkl has an advantage of gaining adoption in applications, by generating types for the application code. Otherwise it will just stay as mystic item in the admins toolbox that others consider overkill.
I've looked at cdk8s but I hate the tsconfig culture, why can't it be simple like Go?
After reading the title, my assumption was that Pkl was yet another newer, better configuration language (a la TOML), but now that I've read the article, it sounds like it's more a language for _generating_ config.
Unless I'm mistaken, it sounds like an abstraction on top of your config files meant to help you build & re-use configuration in a more standardized way, rather than yet another config language into itself.
A problem space I'm familiar with is having a bunch of Terraform or Cloudformation configuration you want to share/repeat in multiple projects. Doing-so can get hairy quickly, as the path of least resistance is to copy-paste a bunch of config you barely understand from some other project, and then perform trial-and-error surgery to find and change a couple of lines to suit your project.
Is Pkl designed to help address that sort of problem? Or am I missing something?
Yes, Pkl is meant to solve this problem. It's a single place for you to configure all your targets. Within the same codebase, you can generate static configuration, and also import the same Pkl source files into a runtime application, so you don't have to copy/paste things around.
I see much more clearly how something like this could be extremely useful.
Instead of copy-paste of json files or aws resources, you can write a terraform module to generate it.
If you need to copy paste a large chunk of terraform module it is time to schedule refactoring.
This is possibly (if not likely) moreso a result of creating our terraform modules in a suboptimal way due to insufficient expertise than a shortcoming of Terraform itself.
It is also largely a result of having a backlog of scheduled redactors that is longer than I'd care to admit.
- jsonnet fixes almost all of the superficial complaints people have about json (no comments, invitations to inconsistent layouts, no composition, no functions)
- jsonnet has a very handy formatter and has trailing commas (simple diffs)
- jsonnet can import jsonnet as well as json so you can "refactor" your configs using code and/or plain data
- json is everywhere and nearly every language has a parser the standard libraries
- jsonnet is not turing complete; I consider this a huge plus as effectively you are always operating in "data space"; everything is a shape transform, nothing more, nothing less
- you can do further slicing and dicing with other mature tools like jq, jc, yq, gron, whatever
- your outputs being plain old json, leverage whatever json schema ecosystem tools you have
- json schema being old, you have lots of codegen tools available
- the jsonnet library has go, python, node, C++ bindings
- super easy to learn and run interactively
the biggest thing that's sorely lacking in this ecosystem are whatever jsonschema doesn't support in its spec for validation, like complex XOR relationships. Sometimes I wish these are declarable in data space, but on the other hand, configs with complex relationships like these often have business code backing them. Another weakness is if you have a large anthology of schemas/templates/source data you need to figure out a management method and hierarchy yourself.Maybe pkl has a nice answer to these but J+J is really quite robust. I'd even go further and say it's beneficial to adopt a schema-first mindset and make it a habit to generate schemas. These tools are so lightweight and ubiquitous, it makes quick work for cranking out a schema and validating everything as you go.
I do like the sound of that. It's always quite tedious to manage configuration in full-stack applications with mixed languages/ecosystems. It seems they already have Pkl plugins for IntelliJ, vscode and neovim and a language server is "coming soon".
Pickle in Python https://docs.python.org/3/library/pickle.html
Tcl pronounced tickle. https://www.tcl.tk/
PCL, was it ever pronounced pickle? https://en.m.wikipedia.org/wiki/Printer_Command_Language
I mean, at this point almost every memorable 3 letter file extension probably already has some claim to it, but still, it would have been nice if Apple had done some research and made this more unique.
However, the CLI talks talk about a "pkl server" mode that is used by the bindings. So it looks like there is a single implementation (written in some JVM language?) that is run in a subprocess. I wish there was more documentation about how this works under the covers.
https://pkl-lang.org/main/current/pkl-cli/index.html#install...
And, it's only a sub-process right now, but we plan on also providing a C library as another way to bind to Pkl.
But if you want to learn more about how this works, feel free to connect with us on GitHub! https://github.com/apple/pkl/discussions
This is most useful when dealing with tools like k8s where deploying a single application might involve 3-10 separate manifests (Deployment, Service, NetworkPolicy, HttpRoute, Autoscaler etc etc). With Pkl you can easily write a simple template that only requests the minimum needed to define an “app” (e.g. name, namespace, mixins for sidecars) and have Pkl generate all the needed manifests for you.
Really Pkl should be seen as a language for quickly building templating tools like Helm. But with type safety by default, and no need for horrible indent hacks.
city = {
"id": 3,
"name": "Foo",
"lat": 3.555,
"long": 4.11,
}
Is the same as: city = {
"id": "3",
"name": "Foo",
"coordinates": [3.555, 4.11]
}
But you want to normalize that.Something like Cuelang will:
- Define the schema that will let you know what you should put inside.
- Allow you to inflate that schema into a full file, providing only the values.
- Will generate always a correct file, with no typo, or format error.
- Will check that the data is correct and tell you if there are any errors.
- Is note theoretically tied to a particular run time or stack.
I'll be looking into cue, but how does it solve that problem ?
Similar to why do we write Python instead of assembly, or why do relational databases typically have things like datatypes and constraints?
- mature libs for most languages
- VScode plugin + Jsonschema for auto completion / schema checking
- yamllint to detect the languages footguns
- yq to query, update in place, and format while preserving comments and sorting keys from the CLI
another common thing is that sometimes you have to define multiple very similar sections in configuration that cannot be handled with yaml archors, eg I have repeated definition dozen times that changes only in 2 numbers that are deeply in structure and name string, and I need to repeat everything because of that and it's pita to modify all other parameters that need to be kept in sync
therefore I think this format looks really nice, although I'm concerned by loops that can be used there, is there possible to create simple config that takes ages to parse or consume very large amounts of memory?
The authors have a fun read[0] about the history of the language, for the curious.
(Never used Pkl myself...)
I've been looking for a way to have typed objects in the config to do config suggestions and type checking.. PKL looks like it can do this for us. And with the JSON output we might even be able to get there with minimal effort.
Is there anyone here with some PKL experience that would be willing to answer some technical questions re the use of PKL for more advanced, nested config?
See Lowdefy:
I've seen their usecase documentation entry. And I understand the benefits this could have.
But I think I need some hands-on usecases, to fully grasp why or how I would use this.
Thanks
> My team migrated several kloc k8s configuration to pkl with great success. Internally we used to write alert definitions in pkl and it would generate configuration for 2 different monitoring tools, a pretty static documentation site and link it all together nicely.
Since neural networks often have many repeating features, using a traditional configuration language requires repeating the same structures a lot, whereas using Jsonnet you can use `std.repeat` instead. You can see some examples of this in the readme of my package.
Language reference doesn't mention schemas either https://pkl-lang.org/main/current/language-reference/index.h...
There’s another repo, the Pkl Pantry, that provides a couple of ready made templates (Schemas) that you can try out: https://github.com/apple/pkl-pantry
Unpopular opinion: yaml is almost as close to perfection as can be gotten. The only thing we could do to make it better is remove features.
They got some things really right. Targeting dynamic languages over static ones is the right choice, though a difficult one. 64-bit floats written in decimal notation are the goodest lowest common denominator we have (sorry), and the ability to embed valid documents in other documents (multiline strings prefixed only with white space) is a game changer. JSON compatibility is controversial, but ultimately very useful.
There are problems that are caused by too many features. The Norway problem is caused by too many features. References and splices are cool but generally confusing. That people do not use tags on their data 99% of the time in the wild strongly suggests that strongly typed configuration is less useful. True, there are too many different kinds of multi-line strings. Finally, if yaml were syntactically specified while preserving that white space feel, we could edit large yaml documents without having to get out our carpenter squares to figure out what level of indentation we're on. Syntactic specification will help our editors figure out what is happening while we type.
I have addressed many of these problems in a new language called NRDL while seeking to preserve the things that yaml got right[1]. I feel we should learn from the mistakes and the success of those configuration languages that came before.
See if you can figure out what that approach would be, and adopt it in future.
Granted the conveniences of Python syntax for code are mostly lost when trying to express tree structured data, and yaml flips that on its head.
(Mumble, grumble, something about s-expressions...)
But it’s still better than templating yaml.
Worth noting that it is specifically CPython that has been called impossible to sandbox. (2014 discussion: https://news.ycombinator.com/item?id=8280053.) It may be possible to sandbox PyPy. PyPy has a sandboxing feature its website calls a "working prototype" (https://www.pypy.org/features.html#sandboxing). If someone invested in it—potentially a huge effort—it could plausibly become good enough for configuration. But, IMO, Starlark is a better choice here because it was designed for isolation from the start. If you wanted to invest in Python-as-config-for-Python, a good use of your time might be improving Starlark for Python.
For in-house stuff, totally agree, just use the python code itself as the configuration.
People rarely waste time because they put a number where they should have put an IP address. People waste a lot of time because they don’t know what IP address to put in there.
Look at any real life production nginx or kubernetes config and ask yourself an honest question — how much would a static type system actually help me in writing this config? We understand what types the configuration fields want — that’s trivial. We spend all of our time finding the correct values for those types.
For example, something like: "this number is be between 1 and 10" means you have to come up with a novel way to add validation, because it isn't built into the language.
Also, Pkl is meant to be usable everywhere. General purpose languages tend to be tied to their own ecosystem--imagine telling a Go developer that they need to install Python, set up virtualenv, and run pip install so they can configure their application. We'd like Pkl to be a simple tool that can be brought anywhere, and easily integrated into any system.
I'm also incredibly high.
Have a look at PEP 20 – The Zen of Python.
Python is actually horrible at following it, Go doing a much better job.
No need for a novel mechanism - there are plenty of available solutions to add validation to python
> imagine telling a Go developer that they need to install Python
Pkl doesn't come preinstalled on machines - so you'll have to install it as well
> set up virtualenv, and run pip install so they can configure their application
This is the real friction point, but is it a bigger friction point than having to adopt yet another DSL?
Yesterday, I created a virtualenv, then ran `pip install`, only to see it fail. I found out that even `pip --version` was failing. I discovered that running `python -m ensurepip --upgrade` would fix pip. It did fix pip, but `pip install` still didn't work properly. I figured out that pip was reporting a different version of python than the one virtualenv is using. Running `python -m pip install --upgrade pip` upgraded pip, which should have been accomplished with the previous ensurepip command. Finally, everything was working properly.
I experienced these problems after years of experience in python. To answer the question. Yes, it's worth adopting yet another DSL than using python.
For all the flak python gets, dependency setup is pretty simple if you're not flailing around aimlessly
python3.12 -m venv --copies --clear --upgrade-deps ".venv"
source ".venv/bin/activate"
python -m pip install --upgrade pip setuptools wheel
python -m pip install --editable .And to think "There should be one-- and preferably only one --obvious way to do it." is in the Zen of Python...
What novel way to add validation in python?
* It's been dragged into static typing kicking and screaming.
* You import the whole Python infrastructure/packaging catastrophe.
* It's not sandboxed.
If you wanted something Python-like you would you Starlark.
This looks very easy. I'd be more hesitant about Dhall or Nix (though obviously Nix comes with even bigger benefits so it might be worth the awkward language).
Edit: and moreover, you probably do not want config files that can run arbitrary code with full access to the Python standard library.
There's probably enough interest and demand for node bindings to be spearheaded. God knows how many config files you have to tinker around with in SPA land. And of course using it for docker/k8 configs could benefit just about any language.
1) Because the developers of Pkl are Python haters
2) Because the developers of Pkl are so overawed by Python that they can't imagine Pkl contributing anything useful to the Python ecosystem
In either case, having suffered so much using Ansible and its excrable YAML scripting, I may use Pkl together with Python.
Certainly language bindings are useful, and if there’s demand likely someone will create them.
Is it really that deep?
All the listed languages are compiled and statically typed. Python is neither. Neither is JavaScript, another popular programming language which is also not listed.
Since "popular" does mean that very many people are using it, I think it would be wise to try to serve the Python community.
Look at the languages that they do support - Go, Swift, Kotlin, Java. These are all robust languages for writing production grade software. That's probably why - the people at Apple using this don't need it for their hacky Python scripts.
Systems like pkl and skylark live on their own and isn't well integrated with systems outside it (for example, an object inside a configuration might want to generate a log source definition in the central logstash config, or a new object in the monitoring system).
Integration with resource creation shouldn’t be needed. If you look at terraform it actually does so and it’s surely nice, but that’s not strictly necessary, it’s just a result of terraform resources themselves being configured in the same configuration language as you write your config file templates. Supporting everything also comes with compromises, like only having a half-assed understanding of how to deploy Kubernetes manifests, instead of focusing on the job of only generating them.
I truly believe every company is still reinventing the wheel because of a lack of serious foundational knowledge in most engineers, so they are doomed to recreate subpar, shitty alternatives.
More seriously, I don’t understand how anyone tolerates 5k line yaml files but I haven’t found any support for moving away from it at $bigcorp. Right now we use an undocumented shim layer between Helm and Argo for maximum inscrutable templating. It’s an endless footgun with the impact spread so thin it’s invisible to the business.
There's Clojure's extensible data notation: https://github.com/edn-format/edn
The general tooling wasn't there, apart from clojure. I tried one of the edn libs for python/node and it always felt second class. The full power was just never there outside of clojure projects.
It's like how everybody still uses QWERTY (including me) and are happy to buy better keyboards, but they must still be in QWERTY
You only need Scheme, quoting and quasi-quoting, and s-expressions.
`((username "foo")
(password ,(getenv "PASS")))
Want to be fully declarative because you are afraid of Turing-complete configs? Simply abort if the top-level list is quasi-quoted.[1] https://www.gnupg.org/documentation/manuals/gcrypt/S_002dexp...
It is kinda commonplace that you don't write configuration in the real PL. Maybe it's less obvious, when your working language is Go/Java, since they are compiled, so obviously you have no choice but to keep configuration in an interpreted format. But you also don't do that in Python/PHP/Javascript. You could just write configuration using full power of Python, right? But even when you do keep some configuration as Python code (because it's only developers who are tweaking these constants anyway, etc.), you usually prefer a plain dict for that, not a bunch of dynamically generated classes with multi-level inheritance.
So, ok, maybe it makes sense when you have to deal with a over-engineered fucked up third party tool like k8s, which you have to configure in YAML, and you could just make it less verbose and have some schema? Well, I don't know, maybe. But the point holds. The good thing about YAML/JSON/etc is that they are very declarative (even when they are used a as imperative DSL syntax, as in GitHub Actions or k8s). If you see that port = 6001, it's 6001, that's it. Your entire DB configuration is right there, in front of you.
The biggest selling point of PKL seems to be that "sidecars" example. And, I mean, sure, it's precisely the problem we intended to solve. But it also shows that you (oh, not you, but your co-worker, of course) could write pretty much anything. It doesn't look like a configuration anymore to me. It's exactly keeping your "configuration" in the source-code of your app, in a real PL, using dynamically generated classes in a cycle and whatever. The thing, we don't usually do for some reason.
I've got a stream of json in one schema; I want to make ETL for this to write it out to elasticsearch, S3 (in a specific different schema) and postgres targets, using a mixture of "bespoke python", vector, and logstash.
Should I use this or should I roll my own goo using ERB or mustache or similar?
Or should I quit and go work some place that's using gRPC?
https://github.com/apple/pkl-pantry
Had more documentation. Like there's a prometheus template; is that for use only in k8?
However I can’t help but wonder why systems don’t simply start adopting full programming languages once they hit a certain complexity. Having to use another language just to reproduce YAMl or what have you in a repeatable fashion is a symptom of the problem IMO
I am definitely lamenting the fact that SDKs (CDKs?) aren’t already exposed in most common languages as a way to take advantage of the robust features of full programming languages.
They either aren’t at all or they use some hybrid format (terraform etc) or they’re Byzantine in nature or ignore good API conventions
Without wide adoption, it's at best an interesting research project. A really cool project I want to work with, but not something I can reasonably bring up.
Imperative DSLs created out of XML+XSD or <put-any-declarative-config-with-schema-here> fears which become exploited by expressiveness and nightmares to maintain after a while.
This is why I still prefer Maven (XML) instead of Gradle (Groovy DSL) for Java project configurations.
And the definitive question: can pkl configure pkl?
And there doesn't seem to be any online playground to test
local `My Variable` = 42
Do you happen to have a link to the missing/blank reference page on this?
EDIT: perhaps I misunderstood the question - if it's about whether you can use non-ASCII characters in identifiers without quoting, the answer is "sometimes":
``` local `abde` = 42 // Only works when quoted
local `teʝ` = 42 // Works fine (as expected)
local teʝ2 = 42. // Also works fine ```
It might be interesting to expand the docs to cover the precise rules here without having to resort to ANTLR grammar, as you say.
For example, does
x² = 42 work? Or
move↓?=true
or some emoji
I guess not given your first example (though it's not rendered here properly, looks like 4 ascii letters), which is rather unfortunate as configs could be more readable with symbolic names,
https://pkl-lang.org/main/current/language-reference/index.h..., both links are broken. Another thing is that the doc page should have an additional pages with a single section/page view otherwise it's too hard to find a word in a single huge doc
a) nobody will bother
b) that one time they do, the constraint will be wrong in some circumstance, and the constraint will get edited out anyway.
> duration3 = 5.ms // milliseconds
grumpy sounds
The biggest drawback was limited number of types, imho. It seems like encoding issues were another drawback which led Apple to use XML when OS X launched. JSON want a thing yet, so XML probably looked like a good option.
0 - https://code.google.com/archive/p/networkpx/wikis/PlistSpec....
Though, even if they weren’t going to provide a server OS, it seems like they could still offer IDE support for the droves of web devs working on Macs a deploying to Linux (like I assume their devs do).
- the validation system
- ability to split large configuration files into smaller files and enhance readability (I know Java / Spring Boot apps already have this ability using profiles)
- multiple language support
Wonder if it’s possible to have more complicated validation functions. The example given validates 1 configuration (“port must be greater than 1000”). But there are times when some configuration is valid by itself however in the presence of other configuration the configuration(s) will possibly be ignored.
Given:
- configA
- configB supersedes configA in app
When:
- both configA and configB set in pkl
Then:
- at compile time, should throw a validation error
Criticisms:
- documentation (in typical Apple fashion) appears to be lacking. Couldn’t find anything on the validation system
- missing LSP support
- yet another config language to support/learn
If you say
a: 5
a: 3
That’s an error.
If you want the ability to "override", you have to write it like this, for example:
a: *5 | int
a: 3
This also works for struct/list members, so you can set defaults for all fields, and stuff like that.
https://github.com/apple/pkl-go/blob/main/go.sum
no thank you.
no. the whole point of go.sum is to see everything. you could have a go.mod with a single item, but if that item is a giant module with hundreds of third party imports (as in this case), its quite misleading.
> I've seen much, much worse worse, as a daily Go user I'd say it's fine
uh where? I am a daily Go user, both professional and personal. and this is one of the most bloated modules I have ever seen.
Apple has done nothing cross platform or open source or community oriented so if they come out with something that's intended to be more general is has no base, no users, no audience of non-Apple developers to land on.
I'm not anti Apple - I love MacOS and Apple is my main machine. I'm just pragmatic about Apple technologies - they are all for usage inside the Apple bubble.
Cups - https://en.wikipedia.org/wiki/CUPS
Swift - https://en.wikipedia.org/wiki/Swift_(programming_language)
Zeroconf - https://en.wikipedia.org/wiki/Bonjour_(software)
I think they're bad, opaque opensource maintainers, but they did release some popular things that have communities on other systems.
I do wish they just contributed to nickel or something else though rather than doing their NIH as usual.
Entire timeline of JavaScript engines.
https://egbert.net/blog/articles/javascript-jit-engines-time...
or:
I didn't include Firefox because it's just an NCSA Mosaic fork, not theirs.
The point being, whoever does most of the work and maintenance owns something, not who came up with some early predecessor.
Swift is a language entirely in the control of Apple, mainly targeting Apples platforms. With little to no community engagement.
Bonjour is not really cross platform, and not really open source either as it has lots of strings attached to the license and terms one can use it under.
(I wouldn't say that Apple has done "nothing" -- but to credit them for doing much is also a stretch)
And I think CSS animations and transforms mostly came out of Apple; at any rate, they're very similar to the animations and transforms in UIKit that originally came from NeXT.
They’re contributing to a ton of OSS that’s NIH:
https://opensource.apple.com/projects/
K8s, spark, cassandra, netty, zookeeper, solr, containerd
Did Nickel exist in 2018? (Someone here said that Pkl did.)
Can I write a loop in this? If? Elvis operator? A simple map lookup?
https://pkl-lang.org/main/current/language-reference/index.h...
> If?
https://pkl-lang.org/main/current/language-reference/index.h...
> Elvis operator?
https://pkl-lang.org/main/current/language-reference/index.h...
> A simple map lookup?
https://pkl-lang.org/main/current/language-reference/index.h...
(is there an xkcd for that?)
i.e. you can write code statements and it transpiles down to other formats
Then there is need to generate that new configuration file as things getting complicated.
The current approach (of all these current languages, pkl looks like is the same) is to painfully refactor the ones you want to change to a parameter file, i.e. a manual data/code separation and you template that.
It would be nice just say this configuration file is now a template file with A B C fields as parameters, and load it up in our new configuration file for templating with good trackability and performance.
It is also easy to migrate to other languages, as configuration now can turn into code cheaply.
you can click it, it'll open link in your browser where you can read what it refers to.