Show HN: A SQL database implemented purely in TypeScript type annotations
github.com
github.com
The end result is actually pretty approachable.
I think it's because there are a whole bunch of APIs in javascript where there is a function like `.get("foo")` that internally calls a function called `getFoo` or similar. And this functionality allows such functions to be accurately typed. It basically allows strong typing for stringly typed functions (which are pretty common in dynamic languages)
https://devblogs.microsoft.com/typescript/announcing-typescr...
Anyway, it seems both impressive that someone did this and that it actually works. Weird how typescript has the nicest typelevel programming I know, even nicer than some dependent ones. It probably is the only language one where autocompletion properly works with computed types?
You're making me blush!
The list of integers could be replaced by using tuples and reading their lengths to do basic arithmetic. Might run into recursion limits though.
Is that anything like church encoding?
https://www.wikiwand.com/en/Church_encoding#/Church_numerals
type two = [0, 0]
type three = [0, 0, 0]
type five = [...two, ...three]
type TupleToNumber<T extends any[]> = T[“length”]
const num: TupleToNumber<five> = 5JSON parser https://twitter.com/buildsghost/status/1301976526603206657
This one is more realistic: Typed route params https://twitter.com/danvdk/status/1301707026507198464
I wonder if this strategy could also be used to have strongly-typed GraphQL queries without the need to have a type generation step that you run separately. Currently when using GraphQL with Apollo, it's great that you get types corresponding to all queries, but having to run the type generator and maintain types in __generated folders is definitely an extra hassle.
I am salivating at the potential use cases for this...
My mind is exploding at the possibilities.
https://devblogs.microsoft.com/typescript/announcing-typescr...
I can see javascript slowly expanding from the front-end into the back-end, mostly thanks to projects like TypeScript that appeal to the back-end crowds, but also because of the whole serverless hype which IMO is justified.
Now there still is a strong "cultural divide" between the software and the data-science communities. And when you see how strong the innertia for python's projects is.. Well I guess we won't see this changing anytime soon.
Write the core AI code in Python because that's what's practical to do. Write the app code in JS because that's what's practical to do. Sell the product to customers that want to use it and probably don't care what's underneath.
If backend changes frontend has to change as well or compiler will yell. Same for machine learning input and output.
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d07d...
Since types can get really complex, I feel a need to be conscious of avoiding complexity when writing types myself, unless the value is enough to justify a hard-to-read type.
What this really demonstrates to me is the difference between starting with TS vs migrating to it. Code that has to think about the types the first time it is being built is going to be better structured, on average, than purely dynamic code.
https://youtu.be/jmPZztKIFf4 at 49:50.
Ok, we might have went a bit too far
It’s more a hybrid data structure where only relationships and certain fields become columns (which can be indexed and searched) and most of the other data becomes a Json column.
I wasn’t completely satisfied with the final results, but it does at least allow me to add additional data fields without having to migrate the db structure and to use Postgres sql to filter and page data.
I think the goal of being able to define a true database in typescript is great and I look forward to trying your code.
It’s also pretty cool if you are able to extract types from a string using the type system - that’s next level typescript wizardry.
Since the type system can parse the SQL strings, then it becomes possible to include those strings as a source for the ast. Then the code generator can use the typescript parser to parse all the types and the sql. From that it can generate all the required code to create the runtime queries.
I don’t like ORMs because they make it very difficult to write complex queries (and my own attempt did not solve that very well - I still had to manually type any complex queries). However, if this could essentially allow writing a complex query in raw sql and have it type-checked at code-time, it becomes easier to use. Then the code generator would convert that into a type-safe runtime library which solves 2 problems: 1. The poor editor performance of the type system inferring so many nested types. 2. Actually having a runtime that executes the queries.
The outstanding problem would be migrations...
Anyway, this is certainly a thought provoking project.
However it was a learning curve to figure out how to do it at first, but I didn’t have to drop down to raw SQL
If you think these are similar I don't think you understand what this is / what it's actually doing.
I agree my wording could have been better.
Actually, I wrote a lot of code in TypeScript and I know my SQL but I have absolutely no idea what is going on here.
So I'm guessing there's no real practical use case for this, and is a nice flex from the author.
It would be nice if that were made dead clear in the Readme, something like "Why? Because it's fun"
It's more of an experiment of thoughts to generate types that are defined by the result of the SQL query, bending a compiler to a new strange behaviour that happens to be SQL like.
If you think this is useless because it doesn't generate any code then I'm assuming you're not familiar with typescript because type level libraries isn't supposed to do do that anyway. There are already libraries that generate open api client and graphql client but they generate type definition via an extra build step which libraries like this can eliminate.
And this typescript feature was made to parse strings so no this is not "bending the compiler"
As this project demonstrates type systems are far more than just glorified comments. Indeed, they are in fact a rigorous scaffolding that seemingly magically "intuit" the intention of code without having to run it. This project playfully explores the extent of Typescript's particular powers.
https://devblogs.microsoft.com/typescript/announcing-typescr...
type Protocol = 'http' | 'https';
type DomainExtension = 'com' | 'org' | 'net';
type DomainName = 'example' | 'google';
type Domain = `${DomainName}.${DomainExtension}`;
type Path = string;
type ApiEndpoint = `${Protocol}://${Domain}/${Path}`;
This would seemingly get out of hand very quickly...I know Deno is supposed to be first class TypeScript but under the hood it's still a JavaScript runtime with all the baggage that comes with that.
AssemblyScript is extremely interesting but last time I played with it I concluded it wasn't yet ready for anything serious, or is that no longer the case?
A first class TypeScript runtime with no JS overhead would be a dream come true. I just hope it happens someday, or that other type systems get these amazing features.
Object prototypes is a weird part of JS that we really could do without. If you really want that, use class syntax.
The Date object. Need I say more...
Little things like Object.keys() should return a (keyof T)[] rather than a string[] but can't due to JS edge cases.
Want first class tuples/immutable arrays.
Many other new syntaxes that can't be implemented due to the need for JS compatibility.
I'm sure there are others that I can't think of right now...
I think we can all agree the Date API sucks, but life can still be good if you just give in and include a date library. Also, there's some light at the end of the tunnel with Temporal proposal coming along [1].
Object.keys returning string[] is purely a TypeScript design decision, coming from how TypeScript chooses to model object subtyping [2].
type Foo = {
a: string
}
const keysOfFoo = (obj: Foo) => Object.keys(obj)
const foo = {a: '', b: 5}
keysOfFoo(foo)
The fact that this passes type checks is a conscious TS design decision that comes with both advantages and disadvantages. It wouldn't have to be this way; Exact types [3] could potentially be used to describe that if the type is exactly Foo, it's safe to assume Object.keys(foo) is (keyof Foo)[].For first class tuples (and records), there's a stage 2 EcmaScript proposal coming along [4]. There's of course also the readonly [] type in TypeScript if you only need the safety of not accidentally pushing to an "immutable" array. First class language support could have nice additional features though, like strict equality.
Anyways, if these are the things you don't like about JS/TS, I don't think AssemblyScript will be the answer for you, as the goals of that language seem to be entirely different from "fixing" old JS cruft. Apart from the i32, i64 stuff I guess.
[1] https://github.com/tc39/proposal-temporal
[2] https://github.com/Microsoft/TypeScript/pull/12253#issuecomm...
Runtime types can still get you there. I’ve been happy with myzod for this purpose, which feels like writing TypeScript while getting you everything you need to validate at runtime in a very performant package.
Basically AssemblyScript try to fix JS runtime as well. See this: https://twitter.com/AssemblyScript/status/114604718874680934... and https://twitter.com/AssemblyScript/status/114466636945316659...
On this subject specifically, see the records & tuples proposal (currently at stage 2): https://github.com/tc39/proposal-record-tuple
Also, the array access not returning an optional type (I wish there was a strict option for that) often gets me.
We need a middle ground: “TypeStrict“ that can clean up some of the edge cases.
If you need strict integers, you can create your own fake type:
type Int32 = number & {__type:'Int32'};
I do this with strings sometime when I want a string subtype that can’t be accidentally assigned by a normal string.
https://devblogs.microsoft.com/typescript/announcing-typescr...
[1]: https://devblogs.microsoft.com/typescript/announcing-typescr...
I was just searching for that and all I could find were old github issues saying it was working as intended.
Thank you!
This was something that confused me when I first started learning Typescript but is actually not an edge case but a fundamental feature of Typescript's structural typing.
A type T is only guaranteed to be at least (a subtype of) T. Which in the case of objects means it has at least those fields, but potentially more. For instance
interface FooBar {
foo: string
bar: string
}
interface Baz {
baz: number
}
const fooBarBaz: FooBar & Baz = {
foo: 'foo',
bar: 'bar',
baz: 3
}
const printFoobar = (fooBar: FooBar) => {
for (const key of Object.keys(foobar)) {
if (key !== 'foo' && key !== 'bar') {
// If Object.keys was (keyof T)[] Typescript would claim this branch is unreachable
}
}
}
printFooBar(fooBarBaz)
It's really worth thinking about Typescript types not as nominal objects like in Java or Haskell, but contracts about minimum functionality. Embracing this in terms of function arguments and return types makes testing and composing typescript code much easier. For example, if you were writing a AWS Lambda function in Typescript that only uses the body of the incoming event. export const handlerOne = (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
console.log(event.body)
return {
statusCode: 200,
body: event.body,
}
}
interface MinimalEvent extends Pick<APIGatewayProxyEvent, 'body'> {}
export const handlerTwo = (event: MinimalEvent): Promise<APIGatewayProxyResult> => {
console.log(event.body)
return {
statusCode: 200,
body: event.body,
}
}
handlerTwo only requires a handlerTwo({ body: '....' }) call in a test, instead of having to fill in all the extra properties of the APIGatewayProxyEvent that aren't even required. You might be tempted to use a type coercion in the test instead e.g. handlerOne({ body: '....' } as APIGatewayProxyEvent) but the problem with the coercion is that is in not checked at all by the compiler, it's essentially like temporarily using an any type. So if handlerOne is updated in the future to depend on more of the structure of APIGatewayProxyEvent Typescript will be none the wiser that the test requires updating (although granted hopefully the test would fail).Anyway, that's a long winded explanation of why Object.keys shouldn't return (keyof T)[]. If you want to program generically over the Type of something I'd suggest making use of a runtime type library like io-ts instead which gives you a data structure representing the type to iterate over, etc.
const x = { a: 1, b: 2, c: 3 }
const y: { a: number, b: number } = x
console.log(Object.keys(y))Your requirements list makes me wonder why you want to stick with anything related to JS at all? With all the transpiling options, web assembly, etc.
I'm thinking of OCaml for example. Why not use OCaml rather than TS?
Edit: Actually the idea is so obvious that you have ReasonML that can compile both to JS and assembly and is basically OCaml with a JS-like syntax: https://reasonml.github.io/docs/en/what-and-why
Isn't that exactly what you're thinking of?
But I would love runtime checks for types. Even if that was a compiler step I could flag on—but that makes it a great deal more complex both to implement and reason about, likely—without starting fresh anyway.
const shout = (message: string) => console.log(message)
into something like
const assert = require('assert'); const shout = (message) => { assert(typeof message === "string"); console.log(message); }
of course this doesn't work for more complicated types
IMO, runtime type analysis is a code smell. There are few scenarios where the simpler and more efficient solution involves querying type metadata at runtime. Particularly in a language that directly supports dynamic dispatch, you should think hard before adding anything resembling an "if typeof(mything)" check.
(Reading through the thread, there’s apparently some libraries that can do this; I’ll have to try them out next time I need this.)
See for example Tetris implemented in Pokémon Yellow via runtime code injection:
https://www.youtube.com/watch?v=Vjm8P8utT5g
Their compiler missed that!
So I think there's good reason to switch from talking about types as "assumptions", and think of them as compile time "assertions". Something lower-level in the stack could break the high-level code, but that doesn't make the high-level code useless, just imperfect.
Types can and do affect the way programs run at the lowest level. Perhaps someone with a better understanding of compiler theory can elaborate further on this...
Then, I noticed it has the pipe operator! I'm going to be giving this a try.
But if you look closely again, the beaut & wide adoption of TS is because it has no "runtime" or no extra libraries or APIs to learn. Its the JS.
This also makes it easy to iterate quickly on Type System and bring new features. Having its own runtime means, that wouldn't be easy.
But I completely agree that TS right now has perhaps one of the best and most intuitive Type System than any other language.
I really did just wonder why anyone would anthropomorphize a programming language, by ascribing a gendered pronoun to it.
Duck-typing that creates a minefield instead of providing correctness?
The unknown type that does the same?
TS is a step forward from JS, but JS's bar is so infamously low that making something better isn't a big achievement, especially compared to other languages with normal type systems.
type EX = Query<"some query string", db>;
and see the that the type as defined by the TS language server (i.e. in your editor) is the result of "running" the query without actually running the code. It's a little insane and somewhat akin to "type providers" in languages like F#.For example, add this below `EX1` (line 66) in the TS Playground:
let names = (persons: EX1) => persons.map(person => person.name);
Now change the column alias in `EX1` to something other than "name" and see what happens. You see that? There is now a compile-time error.Same thing but actually readable and maintanable would've been better implemented as a compile-time (build-time) script, basically source code generation.
Source code generation wouldn't really provide the same end-user ergonomics and, frankly, would barely resemble the same project. How would that work? You generate source code from a SQL string and a data source? At that point you might as well just... write the actual code. The point of this package is that type information is dynamically assigned according to arbitrary "SQL" queries at compile-time[0].
Have you gone through short exercise I outlined below? Surely you can't think it would be better to manually generate type information than have it automagically available and enforced instantly as you work?
The closest thing I've seen to this is "type providers" in F#. Where, given a database connection, a "Provider" can offer compile-time contracts between your code and the schema of the database. What it does not do is provide contracts against arbitrary projections of the database (SQL queries). Of course one can write code that queries and transforms the data in a type-safe way, but these "transformations" must happen in F# for the compiler to infer any new types.
This project takes the above to a different level and infers the type of the result of an arbitrary (subset of) SQL operation before it is ever executed. It's fairly impressive.
[0] This has been pointed out more than once already, but this project does offer compile-time guarantees
I feel typescript is in many ways closely tied to JS because many of the advanced typing features don't make sense outside of existing javascript patterns.
We all talk about simplicity so what makes type systems different that we get excited about their advanced and complicated features.
I'd be interested to hear more about the TS features that you find to be JS-bound. My experience with TypeScript gets deeper every day, but I haven't yet run into anything that was obviously only necessary because TypeScript is a layer over Javascript. I'd appreciate the benefit of some additional perspective there.
My understanding is TS was designed specifically to statically type existing JS patterns in popular libraries and frameworks and while that's great, newer libraries rarely need such flexibility if they aim for type safe designs from the start.
When I use typescript I often get the feeling there are multiple ways of typing the same function, which is reasonable given that one of TS goals was to embrace the JS ecosystem and that meant covering multiple overlapping use cases.
Consider Enums and Union types in TS, at a higer level both express the same thing (this value can be one of these sets of values) but in practice there is lots of discussion on when to use which. I have not seen sum types (as they are usually called) being implemented in two different ways in other languages.
I think one of the ways to think about TS is that it was designed to help programmers better understand their JavaScript code by adding types while reinforcing their designs in a safe manner so if TS didn't have to interface with existing JS code many of the cool tricks it's picked up would not be useful I think.
Finally on the point of expressivity, I'm interested to know how TS helps in modelling application logic to make invalid state impossible.
Well, I'd argue the type system surfaces the pain faster than the untyped variant. In the untyped version, you have to read the code to see that, "it's either the number 4, or a function which may produce the number 4."
Once you see you need a weird type signature to represent 4 OR a function which produces a number, then your spidey-sense kicks in and says, "this is getting gross, maybe I should either always return just the number, or always return a function which produces the number." This avoids a branch in the code that consumes the result where you check the type and do something based on that. At a meta-level, this is the type system telling you something about your program that you might otherwise miss if you were so focused on getting something working.
I’m not sure your request fully makes sense. TypeScript is extremely expressive. You can specify basically any type you what... stuff like “The Number 4 Or A Function That Returns The Number 4 Or An Object With Any Attribute Which Is Equal To 4” and TS will just work with that.
This SQL compiler is a perfect example of how far that can be taken. But I think it’s important to realize how this differs from a strongly typed, compiled language.
A language like Go doesn’t just need to know you’re passing an “array of structs with attributes x, y, and z” for fun. It actually uses that information to generate a binary. The types are not just a check that happens before the real compiling begins, they are how the compiler thinks about structuring the executable.
I would love if a compiler expert could chime in here, but my understanding is you could never write a Typescript compiler that was “strongly” typed because there is no data structure that can efficiently represent arbitrary types like “4 Or An Object With An Attribute Set To 4” in any meaningful way.
These kinds of “wacky types” are things you can statically analyze but not really “compile” per se. That’s why it’s a good fit for being transpiled down to a language with no types at all.
TL;DR, TypeScript is not really a “typed programming language”, it’s a static analysis tool. It’s not really designed to “run”.
There is not really a fundamental difference between types and static analysis.
Haskell has a much more expressive type system than Elm, but still lags behind Typescript unless you turn on a truly gargantuan number of extensions.
It's also a bit weird to compare the languages because Typescript has a very different typing regimen than Haskell or Elm. Typescript is entirely structural while Haskell is almost entirely nominal and Elm is mostly nominal (with the exception of records).
Typescript has an extremely expressive type system. It is in fact so expressive I'm amazed that it's gotten so much adoption when languages like Haskell are still considered "advanced." I suspect it has to do with the semantics of the languages rather than the type systems.
IIUC: it's not a total order, Haskell (even '98) has things TypeScript doesn't[0], and TypeScript has things that Haskell (even with extensions) doesn't[1].
[0]: eg. higher-kinded types [1]: eg. (convenient) row polymorphism
RE row polymorphism, Typescript doesn't quite have what users of ML-like languages are asking for when they want row polymorphism (which is usually parametric polymorphism rather than subtyping). But it's close.
As for convenient... well I'd argue by the time you've got the whole cornucopia of GHC extensions at the top of your file nothing is quite convenient at that point.
How would you distinguish this from the following?
function foo<T>(x: T & {field: number}): T {
...
}
> As for convenient... well I'd argue by the time you've got the whole cornucopia of GHC extensions at the top of your file nothing is quite convenient at that point.That was one part of my point. That said, I think the problems implied by language extensions are often exaggerated (probably not deliberately so, most of the time).
The other part of my point was that even with all the extensions you need, I've not found good row types in Haskell, although the particular failings vary by approach. Which isn't to say I can't get something that works well enough for my particular situation most of the time.
function foo<T>(x: T & {field: number}): T {
return x;
}
function bar<T>(x: T & {field: number}): T {
const x0 = foo(x);
// type error because x0 doesn't have field
// would compile fine with row types
const x1 = foo(x0);
return x1;
}
Yes it's true. I also sorely miss the lack of row types in Haskell (I miss them even in something like Idris where you can create them more easily, but it's still not great compared to first-class support).EDIT: I may be being dumb. You could probably fix this by adding an intersection type again on the right-hand side. I think there was something else I was missing from TS, but I'll have to noodle on it a bit more.
But yeah, I agree that duplicating the intersection produces another interesting question.
In any case, "convenient approximation of row types" probably still applies to TS more than Haskell. And possibly also "more convenient row types", but that remains TBD.
> TL;DR, TypeScript is not really a “typed programming language”, it’s a static analysis tool. It’s not really designed to “run”.
I think you have an interesting point but you've come to the wrong conclusion. Assembly language doesn't really have any notion of types (there is some notion of sizes as some instructions operate on, for example, single words and some instructions like SIMD ones operate on multiple words at a time) yet we still consider C, C++, Rust, Pascal, etc to be "typed programming languages". It's really only languages that compile to some kind of VM which have a notion of types at the runtime level and even then, many VMs erase some of those details like the JVM and generics.
In C++, for example, we have `decltype`, `typeid`, type traits, RTTI, and so on -- all of which are available at runtime. Not to mention that in certain special cases, we also have the de facto storage of type information that makes it into binaries (e.g. discriminated union types).
(I don't consider myself a compiler expert by any means, but I've been doing it for nearly 20 years, so I feel relatively safe weighing in here.)
In short, if you were compiling a function like that for bare metal (or anything relatively like it), there would be no one function that takes in all of those types and acts on them. Instead, you would compile that function once for each combination of incoming types. The version that only takes the number 4 would not have any inputs at all and may end up entirely constant (depending on what it does, of course). It's then up to the caller to make sure the right variant is called. If you have strong types up the chain, this is easy and completely overhead-free -- they'll be compiled in their different versions, too. If you have a weaker type constraint (e.g. just any Number, rather than THE Number 4), then you'd do conditional dispatching at the call site.
For what it's worth, this is essentially how templates work in C++. It's also why compilation can take so damn long and can produce gigantic binaries, because when you have a function with 2 inputs of 4 different potential types each, you're up to 16 variants. Change that to 5 inputs of 4 different types and you're at 625 variants.
All that said, static compilation of TypeScript in its current form would be very difficult to make efficient, simply because JavaScript types in the abstract don't map nicely to hardware; Arrays can be extremely simple and linear or ungodly complex with holes and property overrides, and the 'right' thing to do with a Number is often different from the fast thing to do. That's why JITs are so valuable; they allow you to get around the ambiguity.
Edit to add: I should mention, it's possible you won't actually generate all combinations. You might know that certain combinations are impossible to hit due to other type constraints, or you might just know that they're unused (which leads to problems when you don't have the original function definitions to turn to; that's why C++ templates have to be in headers (I think that's true, at least? I might be wrong on that; I just use the magic, I don't understand it)).
Hi, an amateur here. When I started to learn JS and later TS I always disliked this expressiveness. Like when the first parameter you pass to JQuery can be anything in the world: a DOM element, or collection of elements, or an object, or a function - and only then you declare what you want to do with it. Yes, the syntax is short. And you pay for it with a lousy code readability and steep learning curve.
My question is, is there a name for this "expressiveness"? Is there a name for the opposite? I am looking at the definition of AssemblyScript: "...compiles a strict variant of TypeScript (basically JavaScript with types)". But the word "type" is again used recursively here. Is there a name for this "strict type"?
That specific part is called overloading in OOP (java). Don't know about the rest.
The ultra expressiveness of TS may not be a virtue. It may simply encourage us to be creatively academic with our designs, and do novel, non-obvious and hard to understand and maintain things in the code.
With some of the newer TS releases, I can hardly fathom through the release notes why such a complex degree of metaprogramming has utility for anyone, and what kind of corner cases people must be diving into. They are actually quite hard to follow, and I find myself trying to parse through the need for such a thing, literally unable to conceive of an example use case. Perhaps without a strict philosophy such as they have in Go, the designers just go on ahead and implement their favorite intellectual exercise, or something that helps them with a task in the TS compiler itself, which may not generalize very well.
The errors aren't really errors, just warnings about unused types
const sql = 'SELECT ...';
type SQL = typeof sql;
// type SQL = "SELECT ..."Shameless plug: we're also working on project in the same domain. It actually uses the TypeScript compiler API to generate/update your SQL schema's and a typesafe (mongo like) API on top PostgreSQL. It's still very early stage, but if you're interested: https://samen.io
Just curios, are there real-world use cases for stuff like this?
It's a very niche use-case, this is definitely more of an impressive showcase of the new Template Literal Types feature of TS :)
I've used this with a template JSON document that has an instance of all possible types in the runtime json.
Sometimes you might have to loosen the type slightly, but it works great for a quick start.
function fn(query:string){
const stuff = // do some stuff with `query` variable
return createType(stuff);
}
type MyType = FromJS<fn("SELECT id, name AS nom FROM things WHERE active = true")>;
This way we could compute types on the fly with JavaScript instead of creating monstrous types in TypeScript types system.However, there are Java frameworks that basically generate a type system from your database so whatever columns you query ends up being static types that can be determined at compile time. https://www.jooq.org/
Java is really quite cool. I wonder if we'll ever get a typescript version of jOOQ
I use DynamoDB Data Mapper in production (https://github.com/awslabs/dynamodb-data-mapper-js) - would be cool to see something like this for SQL. Effectively define a class with TS decorators to automatically generate tables and columns.
> MySQL / MariaDB / Postgres / CockroachDB / SQLite / Microsoft SQL Server / Oracle / SAP Hana / sql.js
void main()
{
import std.algorithm, std.conv, std.stdio;
"Starting program".writeln;
// Sort a constant declaration at Compile-Time
enum a = [ 3, 1, 2, 4, 0 ];
static immutable b = sort(a);
// Print the result _during_ compilation
pragma(msg, text("Finished compilation: ", b));
}
It can also reflect at compile time on user-defined annotations, which can store arbitrary metadata on any symbol, to generate arbitrary code at compile time. They serve a similar role to procedural macros from Rust, but they're easier to use than the earlier. Thanks to mixin templates, you can inject arbitrary symbols, making D's UDAs just as flexible as Python's decorators.... which is implemented in JavaScript ... er ... don't know if that changes your desires to use this in PRODUCTION.
This is a work of art.
There are a lot of uses of compile-time code. One I have been interested in is generating bindings to other languages or generating serialization code, or even generating bytecodes for VMs with specific bytecodes for accessing your native data structures (instead of adding a level of indirection).
Dynamically transforming types could be useful to generate complex types based on other complex types.
This project takes it to the extreme by adding an SQL interface to it