Hacker News new | ask | show | jobs
by inamberclad 1022 days ago
Hah, my perfect niche!

I think that an ada-like language could make a real resurgence in embedded programming. Ada gets all the things about bare-metal right that C got wrong. However, it's held up by legacy tooling, clunky syntax, and obtuse compiler errors. Adacore has gone a long way towards alleviating those issues over the last few years, with alire and the ada_language_server. Time will see where this language takes us.

2 comments

I completely agree. 'Design-by-committee' gets a bad rap, but Ada's designers got a lot of things right when it came to bare-metal programming. The ability to specify the in-memory representation of a type is one of my personal favourites. I don't think Ada's syntax has aged well. I wish it would get a 21st-century overhaul. I don't think that's likely though, but we can all dream. AdaCore do great work, and contribute a lot back to the open-source community.
For what it is worth, Ada was not "designed by committee" more than most languages. It was designed by a design team, and in each revision, there was a strong technical leader of the design team, with the whole design team sharing a strong design aesthetic. I doubt you could say even that about many mainstream languages these days.
Thank you for clearing this up. My use of the phrase 'designed by committee' was more of a reference to a common criticism of Ada, than to any actual historical fact. Not that I was actually any the wiser, I admit. I'm a big fan of your work. Thanks for taking the time to reply.
Usually many forget that their beloved C, JavaScript, Webassembly, OpenGL, Vulkan, Web, POSIX,... are equally design by committee.
The benevolent dictator cachet comes mostly from Python microcosm.
And Perl before that.

It's not a bad way to go, in practice, as long as it lasts.

It's unclear exactly what you mean by your remark that Ada's syntax hasn't aged well, let alone what a 21st-century overhaul would look like.

Is there any chance you'd expand on that? I'm curious to know your thoughts.

They probably mean that they can’t get past the fact that it looks more like Algol or Pascal than C. Which is, frankly, a pretty silly argument.

I’ve heard this exact argument about VHDL versus Verilog, with the former being explicitly based on Ada’s syntax and the latter being explicitly based on C’s. (Turns out though that VHDL is also strictly better than at least traditional Verilog, as it requires separate interface specifications which lead to improved modularity.)

If anyone were to actually try to create “Ada: The Next Generation” I’d encourage you to just go all the way to S-expressions. Focus on the completeness and correctness of _the system_ and stop worrying about superficial complaints—or brush them off with a suggestion that they can use any syntax they want and just translate it to the standard one via tree-walking.

I like your use of "Ada: The Next Generation". I replied to your comment in GP. I'm not really too hung up on the Pascal-ish syntax. I don't think these things matter too much. If I had to design the syntax myself, I'd probably go in a different direction. That's just my own opinion though.
Modern Ada 2012 is full of parentheses now. if-, case-expressions, expression-functions, and recently raise-expressions, quantifiers-everything, delta-aggregates, the thing looks more and more like my olden ocaml code. In a good way.
It always makes me grin when newbies complain that VHDL is too verbose. The pain of hooking up the Xilinx AXI interconnect in about three feet of (pre-system)verilog is something I will not forget in a hurry. Having wrapped it in VHDL with nice neat record types, I can now hook it up in just a few lines of VHDL.

I think of it a bit like rat's nest wiring vs. nice neat labelled cable looms.

Sure. I write a lot of Ada, and I'm a big fan of the language overall. My criticisms are minor, and it doesn't stop me enjoying the language: I feel like the 'begin', and 'end' tokens can be a bit verbose, and their verbosity doesn't add much to the language's clarity; I also dislike the fact that I need to place the same subprogram specification in both the specification and body file; I'm also on the fence about some of Ada's pointer semantics; For what it's worth, I'm also not a big fan of the particular style that the Ada language server auto-formats code into.

I'll use this chance to say that I'm a fan of Ada's declarative blocks, which you could say are part of its syntax. There's a lot to like about Ada, and I'd encourage anyone interested in bare-metal programming to give it a try. Even if you don't intend to use it long-term, there's a lot of good language design ideas in Ada that can be learned from.

To address a sibling comment alleging that I'd rather it look like C, that's not necessarily true. I know this is much more controversial, but I'm actually more of a fan of Python's syntax.

Hey, thanks for following up.

I always felt that repeating myself to the compiler (down to `procedure subprogram is begin`…`end subprogram`) or otherwise being verbose was something of a feature in the vein of defense in depth than the syntax not aging well. It never seemed to me like it was meant to impose clarity on the code, just that you were meant to file your subprograms and types in triplicate. It's useful as a backstop against trying to do things quickly instead of doing them deliberately.

At least, Ada's use of verbosity stands in stark contrast to COBOL, which does have the explicit aim to make things clear.

I'm mostly of the same mind as you in re Python's (and Haskell's and F#'s) whitespace-oriented ways of doing things, at least until the real world creeps in. I've had problems with early block termination because of mixed tabs and spaces that weren't readily apparent, and the language's toolchain wasn't particularly helpful in diagnosing them, leading me to lean on external tools. (COBOL at least has the heritage of being column-oriented, so indentation being significant is less problematic there.)

I think it's evident to most people that write Ada that the language is too English-heavy. This gets in the way of actually representing the computations. This was a design decision when the language was created, since the emphasis was put on maintainable code
Is this meant as a joke, too English-heavy?
I think it’s a reference to keyword heavy in an effort to mimic natural language similar to what COBOL tried to do.
They likely mean the pascal-like syntax
Standard Pascal has 35 reserved words compared to ANSI C 89 which has 32. Not a big difference in terms of English-heaviness.

https://wiki.freepascal.org/Standard_Pascal

https://en.cppreference.com/w/c/keyword

It was designed by committee, but as long as the original author was involved he had a veto right and he used it very often, which alleviated the "committee" effect.
> Ada gets all the things about bare-metal right that C got wrong

I'm curious, never had a look at Ada, can you elaborate?

I think it mostly refers to the build in functions in Ada for Bit fiddeling. You can represent registers and bitmaps in Ada data structures and use relatively simple to understand functions on them instead. https://learn.adacore.com/courses/intro-to-embedded-sys-prog...
There are a lot of aspects where Ada is better than C. Just a few things that came to mind:

General lack of dumb C stuff like switch fallthrough, null terminated strings (Arrays in Ada are passed with fat pointers), undefined behavior, no need for memcpy, no preprocessor bullshit etc.

Ada is more like C++ in functionality so it has Generics, Tasks (Threads), Exceptions, Packages, Strong types, Design by contract etc (All much much saner than C++ stuff)

Despite all of the features it's very embedded friendly. Language allows you to disable features you dont wont with Restrictions Pragma (Very long list of restrictions you can apply: https://docs.adacore.com/gnat_rm-docs/html/gnat_rm/gnat_rm/s...). You can also fairly easily change runtimes: https://docs.adacore.com/gnat_ugx-docs/html/gnat_ugx/gnat_ug....

I know berating C is trendy, but it feels a bit gratuitous and uncalled for in your comment...

> Ada is better than C

> lack of dumb C stuff

Back to your comment, strong types and generics look super nice for embedded. Not sure I would like fat pointers, exceptions and threads in my embedded code though.

Tasks in Ada aren't threads, per se, unlike how many people describe them. They can be implemented with threads, but they don't have to be. For embedded and real time systems you likely have a task system suitable for that sort of environment (I'd hope) and Ada compilers targeting such systems will use an appropriate task system.
>I know berating C is trendy, but it feels a bit gratuitous and uncalled for in your comment...

Sorry about that, English is not my first language so i might sound rude sometimes. I actually dont hate C, but if you used it you know its flaws. I think stuff i mentioned about C is objectively bad, hence why i called them dumb stuff.

>Not sure I would like fat pointers, exceptions and threads in my embedded code though

Fat pointers are just pointer + size of an object. You have to pass array size anyway, so its just convenient. Obviously, if you need to just pass a pointer there are ways to do that.

As for exceptions and threads: As i said, you can disable or modify them just by using Restrictions pragma. Its a language defined thing. For example, Ada 2022 defines two profiles for safety-critical hard real-time computing:

https://en.wikipedia.org/wiki/Ravenscar_profile http://www.ada-auth.org/standards/22rm/html/rm-d-13.html

> Sorry about that, English is not my first language so i might sound rude sometimes

No offense taken, not a native here either ;-)

> if you used it you know its flaws

Indeed, I've been using C for almost 20 years now. I won't say it's without flaw for sure, it has its quirks, but overall I do think it's quite okay for the job.

> Fat pointers are just pointer + size of an object

Yeah I know what fat pointers are, I even resort to handcraft some form in C for some neat performance hackery on x64.

But the thing is, we're talking embedded here. Most ucontrollers I use have 8b address space, no MMU or any form of virtual memory, separated instruction/data bus (MVHA). That kind of thing don't play well with funky fat pointers.

Sure if your definition of embedded is "64b ARM" all is good, but I guess we're on a spectrum.

Exceptions are pretty much non existing as well, since that would require some form of runtime, which you often just cannot afford on a small chip (if not just form the sheer size of it).

Threading is a no go as well. To get threads, or any form of multitasking really, you have to rely on an operating system, which by definition is a bit weird to have on an embedded IC.

I personally think that its 'representation clauses' are a really awesome feature for bare-metal programming. It's a shame other languages haven't borrowed this idea.
Representation clauses are by far the biggest feature for embedded programming:

https://learn.adacore.com/courses/advanced-ada/parts/data_ty...

http://www.ada-auth.org/standards/22rm/html/RM-13-1.html

Wouldn't it be nice in C to be able to define how a struct is laid out in the machine representation? In Ada, you can and it is part of the standard, so it is portable:

https://learn.adacore.com/courses/advanced-ada/parts/data_ty...

It's difficult for someone with 0 knowledge of the language to really understand representation clauses. It does seem to be similar to enum values in C++?

    // Ada
    for Day use (Mon => 2#00000001#,
                 Tue => 2#00000010#,
                 Wed => 2#00000100#,
                 Thu => 2#00001000#,
                 Fri => 2#00010000#,
                 Sat => 2#00100000#,
                 Sun => 2#01000000#);
    // C++
    enum Day {
        Mon = 0b00000001,
        Tue = 0b00000010,
        Wed = 0b00000100,
        Thu = 0b00001000,
        Fri = 0b00010000,
        Sat = 0b00100000,
        Sun = 0b01000000,
    };
As for the record representation, my understanding is that it is equivalent to having a normalized __attribute__((__packed__)), where smart compiler padding is disabled and you can arbitrarily decide the memory layout of your struct?
Representation clause is just language defined way of how struct (Record in Ada) Enum or Array are actually laid out in memory. So, for example, you can define a struct that represents a register, overlay that register address and use it like this:

  procedure Enable_USB_Clock is
  begin
     Registers.PMC_Periph.PMC_SCER.USBCLK := 1;
  end Enable_USB_Clock;
Example taken from here: https://learn.adacore.com/courses/Ada_For_The_Embedded_C_Dev...