4 ms·
Once all of this compile-time metaprogramming and code execution starts to happen, it always makes me ask: doesn’t this just conclude with dynamic typing? I’m c
by amw-zero 6y ago
Once all of this compile-time metaprogramming and code execution starts to happen, it always makes me ask: doesn’t this just conclude with dynamic typing? I’m currently a static typing lover. But it’s almost as if we’re just looking for the full power of a programming language. Why not just use the language itself instead of a weird, stratified compile time language?
- hedora 6y agoThe weird stratified compile time language lets you create DSLs that generate and typecheck the program you’re trying to write. I’ve written a lot of C++ (and no Rust), but if the compile time language is turing complete, then you can push all sorts of correctness and type checks to compile time. This is really, really powerful. Even with unsafe C++, I’m used to writing very high level, high performance concurrent code. If it crashes after it’s passed CI testing for a week or so, it’s probably a hardware bug. (Of course, there are exceptions, but those fall outside of the parts I have the compiler check for me.)
- amw-zero 6y agoBut what you can prove at compile time is rarely useful. You can use phantom types, smart constructors, and the like, but after a while you need a runtime to do anything interesting. An example: this function is only valid on positive, odd integers in the range 3-91. With any static type system, this has to be a runtime check anyway. I’m not an expert on dependent types, but that sounds like what those try and solve. But at that point, you just want compile time to be a full program, which has all of the same pitfalls as the runtime of a program.
- _bxg1 6y agoAny static type system prevents you from doing some subset of things that you could do with a dynamic type system. But there are obviously lots of things you get in exchange for that power. Type system evolution, usually, is about trying to colonize a larger and larger portion of that possibility space and put it under the domain of "statically checkable". It will never cover the whole thing, but it's still really valuable work and great strides are still being made.
- josephg 6y agoNot necessarily. The cleanest version of this sort of thing I’ve seen is zig’s comptime. Basically you can annotate loops, conditionals, function parameters, etc to be evaluated at compile time instead of runtime. You’re still writing zig - it’s the same language; just some of your code runs in the context of the compiler. One really nice use of this is printf. In C, printf needs a remarkably large amount of code and it’s quite slow. In zig, printf marks the format string as comptime. Then the compiler runs the code looping over and evaluating the format string, then unrolls the result into the binary. The output ends up being a few terse calls to write / format with no extra machinery at runtime. And you get clean compile errors if the format string doesn’t match your arguments - all with no special casing in the compiler. More detail, with code: https://ziglang.org/documentation/master/#Case-Study-printf-in-Zig https://ziglang.org/documentation/master/#Case-Study-printf-... I think nim has something similar. I’m surprised rust went with macros instead of something like comptime - though I assume there’s some trade offs I’m not aware of.
- akiselev 6y ago> I’m surprised rust went with macros instead of something like comptime - though I assume there’s some trade offs I’m not aware of. Rust has procedural macros which are Rust libraries built and run compile time that can transform raw tokens into valid Rust code (or more raw tokens for nested macros). Procedural macros can annotate any statement or code block - although some specific ones might still be on nightly only.
- _nhynes 6y agocrate-level inner attribute macros are still unsupported [0], so if you want those, you'll need to do the codegen before the compiler takes a pass or from within the compiler itself (using, say an after_parsing [1] hook). They're really helpful for making a deeply embedded DSL. [0] https://github.com/rust-lang/rust/issues/54726 https://github.com/rust-lang/rust/issues/54726 [1] https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/trait.Callbacks.html#method.after_parsing https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver...
- smabie 6y ago
- lmm 6y agoDynamic typing would be throwing the baby out with the bathwater. Look at stage polymorphism (e.g. MetaML) for the right way to do this.
- megameter 6y agoIf you follow through on this thought further you end up in the realm of the dynamic languages with very strong metaprogramming capability - the Lisps and Forths. With these, the reduction of the language is so complete that changing domains to the meta-mode is as easy as falling off a log. And nothing stops these languages from having a combination of useful static checks, high performance output, and fluidity of expression: they are powerful enough to be parsers at one instant, code generators at another, and optimizers at a third. The main reason not to do it that way is the "you're on your own" nature of dynamism, in and of itself. If you don't come up with a good structure for checking things, the language isn't going to do it for you. And this is fine within the context of the quick hack or the home-grown type system, but less useful for proving broad, general properties about the code. Particularly when this is an industry where the majority of programmers are assumed to be hugely inexperienced, with less than 5 years under their belt, and trusted to build fairly complex systems. Classical static type systems(your Pascal, Java, etc.) only really know the world in terms of combinations of primitives; they don't prove much beyond simple matching of input to output. But that's yesterday's approach - it's not what industry is looking towards. What the newer static languages(of the Haskell and Rust ilk) have converged upon is to bolt a powerful constraint solver engine to the type-checking system, which in turn shapes the whole language around pleasing the type-checker. The resulting minimum code quality is often higher, but also semantically torturous.
- Reelin 6y agoWhat does dynamic typing have to do with the language used for CTFE? (Have I misunderstood you?) D has fairly permissive CTFE which uses the language itself; it remains statically typed. Haskell is statically typed while the type system can be made Turing complete by using certain language extensions. The Terra language (embedded in Lua) is statically typed and is metaprogrammed using Lua. Racket is certainly dynamic, but Typed Racket is ... well it's still dynamic, but type annotations are statically checked.