Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

For reference: In D, you could just take a HTML parser then run it at compile time without writing any new code.


Yes - D is what C++ should have been. By C++ 23, we will be about 80% there :|

Considering that one of the greatest C++ programmers of all time (Andrei) is now a D person, that's proof enough.

But for some reason, this sort of C++ shenanigan is intensely satisfying to me.

I guess it's a narrow complexity fetish.


Can D be used without GC?

(Note, by "used" here I mean utilizing most of the language, and the full ecosysstem of libraries, or at least most of it; not a mode that is supported in theory but useless in practice.)


Yes, if you're willing to spent some effort on it. Somewhat simpler you can just avoid most GC allocations so you don't run into any performance issues with the GC. http://dlang.org/blog/the-gc-series/

Mind to share why a GC seems to be unaccceptable for you?


It's not that it's unacceptable in general (although there are certainly scenarios - real-time, embedded etc - where it is). It's that a language that effectively mandates GC cannot be a true C++ replacement. So I can't in good conscience accept a "better C++" or "what C++ should have been" label.

This isn't to say that D is not a great language. It is, but I see it more as competing against Go (which is also not "a better C"), and to some extent Java, C#, Kotlin, and Swift.

The only true contender to the "better C++" label that I know of is Rust.


You can already write C++ in D, all the features are there. On top of this, you get features that C++ should have had years ago for free: Static Reflection, UFCS (Less boilerplate), Meta-Programming that isn't a hack and lots of static guarantees (Purity, GC usage etc).

The standard library could have been implemented completely without the GC, but the GC makes programming simpler so it's not worth it. Practically, this may be a problem, but it's a little unfair to compare to use this against D (only the language) given that Phobos is not standardized in the same way that the C++ Standard Library.

In a very specific way (May or may not be good in your opinion), I think D is closer to C++ than Rust is: D maintains the basic idea of C++ e.g. Syntax + semantics are still C based, the type system is less complex than that of Rust.

However, Rust has better static safety guarantees than D. D's system is still being developed, although does already offer much more than C++.


The problem is the stop the world pause. Imagine the following scenario: Thread 1 is a realtime thread and allocates memory up front and even uses @nogc for the rest of the code. Thread 2 continuously allocates memory. Thread 2 starts the garbage collector and must stop Thread 1 even though it doesn't allocate anything. Basically the only solution to the stop the world pause with D is to run two instances of your program and then use IPC even though the language provides a way to disable the gc. D's solution is to generate less garbage but it doesn't solve the real problem with current garbage collector implementations.


> Basically the only solution to the stop the world pause with D is to run two instances of your program and then use IPC even though the language provides a way to disable the gc.

There is a solution to your stated problem that is to deregister Thread 2 to the runtime. No registration -> no stop-the-world for this thread. Thread 2 will not be able to "own" GC things or allocate with the GC but will perfectly be able to use them. I did this in a video game for the audio thread.


Writing in languages with a GC is quite cumbersome.

Unless you are writing a proof of concept without a care in the world for your code you still need to reason about memory just the same as in a language without a GC. Only problem is that you constantly have to fight the GC and you don't get any help from the language. And at the same time you loose valuable constructs such as RAII - for no good reason.

Similar to how static language are gaining I think that we are going to realize that garbage collection has been a huge mistake. It is such a relief that we now got Rust, and the lost opportunity that is Go is nothing but tragic.


> Writing in languages with a GC is quite cumbersome.

On the contrary, the GC simplifies program for the vast majority of use cases. If it becomes a problem, then don't use it.

> And at the same time you loose valuable constructs such as RAII.

Forgive me for being argumentative, but have you looked at the D website? D has RAII, as advertised on the website homepage.


In the same way as dynamic languages simplifies programming for the vast majority of use cases. Or not, it's just short sighted and introduces unnecessary complexity.

When it becomes a problem it is already too late, because you can't just change your mind - "oh, I guess I'll think about memory now". Because that is something you should always do regardless if a GC is present or not. And if you haven't, well, prepare to spend the remaining liftetime of that project battling with it. And if you did, why bother with the GC in the first place?

It is not difficult, and forces you to write better software.

I was talking about GC in general terms, not specifically about D. That RAII example seems a bit convoluted though. Maybe D is a great language even when ignoring the GC, but often enough using a language in a way that isn't officially meant (and I bet most do make use of and expect the GC to be available?) is usually a bad idea.


D's GC is designed in such a way that's it's very easy to avoid. It only operates when allocating, if you want it to not collect from a thread just disconnect that thread from the runtime.

Using a GC does not constitute not worrying about memory!

There is no official way to use D; the only real problem with not using the GC now is safety (Exceptions pass pointers and can therefore leak across library boundaries).


With Vibe.d you can use Pug / Jade templates and they will be parsed at compile time so you don't have to interpret them at run time.


That negates the whole point of templates and constexpr -- they're purely functional subsets and explicitly designed to be so.

(You can argue that purely functional code is useless in real life and that C++ shouldn't strive to be purely functional, but that's another argument.)


C++11 constexpr where purely functional, C++14 adds (controlled) mutation.


Likewise in Common Lisp, arguably much more easily.


Well LISP has no distinction between meta-programming and programming. so as long as you like parens, you can do anything


There is no macro needed here. You just write the code, then execute it at compile time. This is designed properly, it's not any where near as hacky as C++'s constexpr


No need for macros, eval-when is a special operator designed for this:

    (eval-when (:compile-toplevel)
       ...)
You can also have read-time macros or load-time values, depending on what you need.


how ... macros?


Macros, or (eval-when (:compile-toplevel) [your code here]). Caveats apply.


Vanilla macros are one option.

  (defmacro html (string)
    (some-html-parser-library:parse string))
Or you could use a function and a compiler macro to make it more of an optimization -- if the argument is known at compile-time just parse it then, otherwise wait til runtime:

  (defun html (string)
    (some-html-parser-library:parse string))

  (define-compiler-macro html (&whole form string)
    (if (constantp string)
      `',(some-html-parser-library:parse string)
      form))
Actual example, using `string-upcase` to stand in for an HTML parsing library:

  (defun html (string)
    (string-upcase string))
  
  (define-compiler-macro html (&whole form string)
    (if (constantp string)
      `',(string-upcase string)
      form))
  
  (defun foo (arg)
    (print (html arg))
    (print (html "compile-time"))
    nil)

  (foo "run-time")
  ; "RUN-TIME"
  ; "COMPILE-TIME"
  
  (disassemble 'foo)

  ; disassembly for FOO
  ; Size: 128 bytes. Origin: #x10106A207D
  ; 7D:       498B4C2460       MOV RCX, [R12+96]                ; thread.binding-stack-pointer
                                                                ; no-arg-parsing entry point
  ; 82:       48894DF8         MOV [RBP-8], RCX
  ; 86:       488D5C24F0       LEA RBX, [RSP-16]
  ; 8B:       4883EC18         SUB RSP, 24
  ; 8F:       488B55F0         MOV RDX, [RBP-16]
  ; 93:       488B0576FFFFFF   MOV RAX, [RIP-138]               ; #<SB-KERNEL:FDEFN HTML>
  ; 9A:       B902000000       MOV ECX, 2
  ; 9F:       48892B           MOV [RBX], RBP
  ; A2:       488BEB           MOV RBP, RBX
  ; A5:       FF5009           CALL QWORD PTR [RAX+9]
  ; A8:       480F42E3         CMOVB RSP, RBX
  ; AC:       488D5C24F0       LEA RBX, [RSP-16]
  ; B1:       4883EC18         SUB RSP, 24
  ; B5:       488B055CFFFFFF   MOV RAX, [RIP-164]               ; #<SB-KERNEL:FDEFN COMMON-LISP:PRINT>
  ; BC:       B902000000       MOV ECX, 2
  ; C1:       48892B           MOV [RBX], RBP
  ; C4:       488BEB           MOV RBP, RBX
  ; C7:       FF5009           CALL QWORD PTR [RAX+9]
  ; CA:       488D5C24F0       LEA RBX, [RSP-16]
  ; CF:       4883EC18         SUB RSP, 24
  ; D3:       488B1546FFFFFF   MOV RDX, [RIP-186]               ; "COMPILE-TIME"
  ; DA:       488B0537FFFFFF   MOV RAX, [RIP-201]               ; #<SB-KERNEL:FDEFN COMMON-LISP:PRINT>
  ; E1:       B902000000       MOV ECX, 2
  ; E6:       48892B           MOV [RBX], RBP
  ; E9:       488BEB           MOV RBP, RBX
  ; EC:       FF5009           CALL QWORD PTR [RAX+9]
  ; EF:       BA17001020       MOV EDX, 537919511
  ; F4:       488BE5           MOV RSP, RBP
  ; F7:       F8               CLC
  ; F8:       5D               POP RBP
  ; F9:       C3               RET
  ; FA:       0F0B10           BREAK 16                         ; Invalid argument count trap
`html` is only called once, to handle the call that isn't known at compile time.


Yes. We parse JSON at compile-time with std.json (in D standard library). It probably wasn't even designed with that goal. From a D point of view, compile-time parsers are almost boring since anyone can write them.


Out of curiosity, is it possible to debug that in compile time?


Yes. pragmas, static asserts


I mean through an actual debugger, not printf debugging.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: