Salta al contenuto principale


#BabelOfCode 2024
Week 9
Language: Ada (Spark?)

Confidence level: High

PREV WEEK: mastodon.social/@mcc/114463342…
NEXT WEEK: mastodon.social/@mcc/114582208…

So I start reading the manual for Ada. I think: This is *great!* This has all these features, conveniences etc I was wishing for for years in C++, and didn't get until I jumped to Rust. I might have been using this for games in 2010 if I'd dug into it then!

Then I start writing an actual Ada program. It turns out to be basically a pain in the ass.


#BabelOfCode 2024
Week 8
Language: Fennel

Confidence level: High

PREV WEEK: mastodon.social/@mcc/114433465…
NEXT WEEK: mastodon.social/@mcc/114463342…
RULES: mastodon.social/@mcc/113676228…

Lua is my favorite programming language. Because of Reasons¹ I have stopped using it, but it is still a jewel of design to me. Any description of what I want in a programming language will sound like "Lua, but—"

Fennel is "Lua, but with LISP syntax". Which is… if anything the opposite of what I want. Hm.

¹ I NEED TYPES


Questa voce è stata modificata (5 mesi fa)
in reply to mcc

Now, it's possible that the reason I'm unhappy is I jumped directly into something Ada is not designed for. However, that thing I jumped into was "load a file at runtime into a string whose length is not known at compile time". I don't think this should be so hard in a recently-revised language. (I seem to be tragically consistent in this challenge at accidentally picking languages whose input systems prefer structured data-- like Ada and Fortran-- on puzzles where I'm parsing raw ASCII art.)
in reply to mcc

To pick at this. "Software" can mean a do of different things! There's environments where you spend lots of time poking at text strings— text files in a world where the UNIX "everything is a file" philosophy won, webdev. There's really important environments where they don't! If you're writing backend software interacting with databases and network services, maybe it turns out Ada is *perfect* for this environment and you don't care about the friction on files and variable-length strings. Dunno.
in reply to mcc

Anyway. I was able to open a file whose name is given on the command line, but not read lines from it (the line reader only wants to work with stdin). I was not able to take an arbitrary-length string from stdin (I hardcoded to 10,000 bytes, the length of my largest input). I wasted a bunch of time on it, decided this wasn't important, and moved on. I'm trying to write "professional" software in this challenge and Ada, at least for the UNIX-y cmdline-and-files environment, did not reward that.
in reply to mcc

Compromises on basic things continue. I want to create an array whose size is not known at compile time. I believe I can declare this with

Mem : Array (Natural range <>) of Integer range -1..Integer'Last;

But this errors there's no initialization. You can initialize an array to a fixed value with (Others => -1), but then it doesn't know the size. I wind up making a sub-procedure *just* because that is the only syntax I can find for initializing an array to a size. github.com/mcclure/aoc2024/blo…

in reply to mcc

Small observations:

- Inner procedures and functions of a larger procedure and function can access the outer procedure's local variables. That's nice. I suspect this is hiding some sort of horrible restriction on recursion, but it's nice.

- The error messages, at least in "gnat" the open source Ada compiler, are NOT good. I think this is downstream from the language having lots of minor similar-but-distinct concepts instead of single powerful concepts. There's a lot of jargon and lots of edges

in reply to mcc

inner procedures (in gnat) require executable stack because creating a closure puts a trampoline on stack so that you would not need "fat pointers" like Rust does

this is also why you can't compile GHDL to WebAssembly

Questa voce è stata modificata (5 mesi fa)

reshared this

in reply to mcc

it's a part of the ABI now! i think this is like, the overwhelming cause for executable stacks today
in reply to mcc

Something I don't know how you'll feel about: You know how in every programming language except Lua you index arrays from 0, and in Lua you index arrays from 1? In Ada, if I'm understanding this correctly, you choose whether your code indexes arrays from 0 or 1. *On an array by array basis*. You could mix 0- and 1-indexed arrays in the same code. You could have an array which contains 10 elements at indexes 10 through 19 inclusive, if you wanted. An array is a map with integer keys in a range

in reply to mcc

Man writing Ada is *really* making me think I was too hard on Haskell

Oblomov reshared this.

in reply to mcc

I've never used Ada, but I wrote VHDL professionally for a year or so 23 years ago. I really got it then. I've not looked at it since.
Questa voce è stata modificata (5 mesi fa)
in reply to Dr David Mills

@dtl I read the VHDL spec once, and it made a lot of sense. But also the Ada spec made a lot of sense when I read it, and when I tried to actually write it I liked it less.
in reply to mcc

Maybe the problem with Ada was that the things it was trying to do were too advanced for the tools that were available to the designers at the time.

I'm having an awful problem with a very straightforward bit of code because I want to scan over some code iterating a variable erratically. But I don't have any good value to give *before iteration begins*— because Ada integer types are range-limited, I can't use "0". This would be no problem at all I had Option<>. But I don't have Option<>.

in reply to mcc

Guh. Part 1 done. It was ok once I actually had my data loaded into memory, but every moment up until there was pulling teeth, and honestly the ergonomics weren't *great* after that. The final insult was it taking a startling amount of time trying to figure out how to convert an Integer to a Long_Integer when it turned out my result was over 32 bits. It's Long_Integer(), but StackOverflow was a bunch of "how do I cast in Ada?" "*bragging* In Ada, you don't NEED to cast!"

github.com/mcclure/aoc2024/blo…

in reply to mcc

I pushed through Part 2 just to get it over with. I could have done a very clean, efficient implementation in any other language, but Ada makes creating new arrays enough of a pain I just wound up like… doing it the dumb nested loops way. Whatever. I realized in literally the final line of code I wrote that part of why I was struggling with loops was I didn't know "exit" existed.

It works. It's even relatively efficient. I don't feel proud of this code.

github.com/mcclure/aoc2024/blo…

in reply to mcc

There's a lot I'm curious about in Ada. I'd really like to know more about its builtin Task primitive. I find the basic "in the small" writing of Ada frictionful enough I have lost my curiosity about attempting to experience its high-level primitives. I was originally intending to do part 1 of this puzzle in Ada and part 2 in Spark, but I don't… I just kinda want to stop. This puzzle has taken very almost the whole week, and part of that is WestJet's fault (long story), but I want to move on.
in reply to mcc

A problem I anticipated with this "Babel of Code" project and sure am hitting now: The AOC puzzles are a great way to learn a language, but they'll always focus on *only part* of a language. When I hit smalltalk, I'll have little opportunity to use objects. Multiprocessing, or verification capabilities like Spark, can be applied in *some* puzzles, but it's hard to know *which* puzzles until it's half complete. Like, what formal properties does THIS puzzle have to verify?

adventofcode.com/2024/day/9

Oblomov reshared this.

in reply to mcc

#BabelOfCode 2024
Week 9.5??
Language: Spark

Against my better judgement, but since it was one of the main things that intrigued me about Ada, I decide to port my "day 9" solution to Spark.

I put at the top of my program:

with SPARK_Mode => On

And run it. Nothing happens. Or rather it runs normally. I assume that this sets an attribute read by the Spark toolchain, and is ignored by basic Ada.

The docs suggest running gnatprove. Which… isn't in debian apt? I think I have to go to Github?

in reply to mcc

gnatprove-x86_64-linux-14.1.0-1.tar.gz turns out to be 412MB gzipped, and contains both a complete installation of GCC and a Python 3. That seems like why dpkg exists, but it seems the AdaCore corporation really, really wants me to be using gnatprove as part of their IDE. Which I guess is also why gnatprove unlike gnat itself can't be run on single files and I have to make a project file. Whatever, fine.

It's running. It gives me hundreds of validation errors. Which I guess is what I wanted.

in reply to mcc

Spark is immediately proving itself interesting. My very first error:

puzzle.adb:47:21: medium: range check might fail, cannot prove upper bound for 1
47 | Mem_Idx := 1;
| ^ here
reason for check: value must fit in the target type of the assignment

What this means: Mem_Idx has type
Mem_Idx : Natural range 1..Mem_Len;
where Mem_Len is a Natural argument of the procedure. But *nothing guarantees Mem_Len is nonzero*. That's a real uninterrogated assumption!

in reply to mcc

More odd friction. It turns out Ada/Spark don't allow integer narrowing on a argument parameter. Why? They just don't. OK, so I create a typedef Nonzero for the integer range of Mem_Len. Now anytime I say I have a Natural of range 1…Mem_Len, it complains I used Natural and not Nonzero. Bro!! Bro u r introducing an unnecessarily strong binding between structural and nominal typing!! It could have detected Natural range 1..Mem_Len necessarily conforms to Nonzero, but instead I have to annotate it.
in reply to lambdageek

@lambdageek I'm going to try Idris… I was thinking of doing another Haskell week to try Linear Haskell (which I think is just GHC with a flag) would Liquid Haskell layer atop that happily?
in reply to mcc

Gonna nerd-snipe you with F* 😉

fstar-lang.org/

"is a general-purpose proof-oriented programming language, supporting both purely functional and effectful programming. It combines the expressive power of dependent types with proof automation based on SMT solving and tactic-based interactive theorem proving."

E.g. proving function equivalence:
floss.social/@janriemer/114446…

Crazy language! 😄

@lambdageek

in reply to Jan

@janriemer @lambdageek Hm. And it runs on the CLR? Is it a subset/superset of F#?
in reply to mcc

@lambdageek
It compiles to OCaml by default, but can also compile to F#, C and even WASM (using a tool called KaRaMeL 😋 github.com/FStarLang/karamel)!
in reply to mcc

I'm not sure.

github.com/ucsd-progsys/liquid…

it looks like maybe it will work.

although I don't know if there's any meaningful interaction between the linear and refinement types

in reply to mcc

Continuing with the "couldn't you do the math yourself?" complaints, it now turns out I can't add Natural (range 0..Natural'Last) to Nonzero (range 1…Natural'Last). These types share a common upper bound and neither may be zero; this addition is safe in all cases!

I guess I expected working with a theorem prover language would have lots of paperwork. I don't know if I expected this. (Also *this* issue is Ada not Spark?) Would Idris have been able to figure this one out? I bet Prolog would have.

in reply to mcc

Okay I guesss I got frustrated too early. It turns out that I can avoid all this nominal typing paperwork by, instead of attempting to refine the value of Mem_Len by its parameter type, refining it by *contract*. I add a contract that Mem_Len > 0 and Spark no longer complains about assignments. That's entirely fine. It would have been nice if Ada had been more helpful, but the point is whether Spark offers capabilities to keep the friction/frustration levels beneath the ragequit point. Fiiine
in reply to mcc

I realize I'm focusing on entry-level nitpicks here, but I do notice this is not the first time I've run into a frustrating limitation in some construct of Ada only to discover I can avert the limitation by using a different, similar construct. It speaks to an actual problem with Ada/Spark: There is too much syntax. I believe that Ada was designed around readability to the point it harms writeability (and doesn't help readability as much as it should, as the eyes glaze at all this boilerplate).
in reply to mcc

are you ok for some insight from an Ada/SPARK dev?
in reply to DesChips

@DesChips I'd be curious yes. However if you were about to explain the type/subtype distinction to me, I have received that help already in the time since my post: mastodon.social/@mcc/114535020…

Thanks


@eggrobin Hi, sorry… are you saying that

`type Nonzero is new Natural range 1..Natural'Last;`

is introducing the hard "no mixing!" nominal type, whereas

`subtype Nonzero is new Natural range 1..Natural'Last;`

is just like creating a shorthand expansion for `Natural range 1..whatever`, and would have evaded my error above?


in reply to mcc

Sub-types are indeed important ^^ What I noticed first is that you use anonymous subtypes and constraints a lot, where I would define explicit types. For example, with the array of integers:

type Mem_Length is range 0 .. 20_000;

type Any_Mem_Element is new Integer range -1 .. Integer'Last;
Invalid : constant Any_Mem_Element := -1;
subtype Valid_Mem_Element is Any_Mem_Element range 0 .. Any_Mem_Element'Last;

type Mem_Array is array (Mem_Length range <>) of Any_Mem_Element;

in reply to DesChips

Then you can declare your array like so:

Mem_Len : constant Mem_Length := 42;
Memory : Mem_array (1 .. Mem_Len) := (others => Invalid);

You can also check for valid elements, e.g.:
for Elt of Memory loop
if Elt in Valid_Mem_Element then
--- something
end if;
end loop;

Questa voce è stata modificata (5 mesi fa)
in reply to DesChips

For SPARK, this will remove the need for a precondition in your Run procedure because Mem_Length values can never be negative.
in reply to DesChips

@DesChips This is fascinating! I'm confused by the '(others => Invalid)' value being assigned to Memory in the line

Memory : Mem_array (1 .. Mem_Len) := (others => Invalid);

Mem_Len (set to 42) is limited to the range 0 .. 20000. In the assignment of (others => Invalid), what are the 42 elements of Memory assigned to -- Invalid? Or something else (or undefined, which seems very anti-Ada)? Or is Invalid returned if Memory is accessed with an index outside 1 .. 42, i.e. 0, 43 .. 20000?

I guess I'm mostly confused by what 'others' refers to.

in reply to arclight

@arclight When "others" is alone in the array literal (aggregate) it means all the elements of the array. Which, in this case, I set to the value of the "Invalid" constant. The word "others" makes more sense when you provide values from some elements of the array and a default value for all other elements, e.g.:

Memory : Mem_array (1 .. Mem_Len) := (1 => 2, 4 => 0, 16 => 13, others => Invalid);

learn.adacore.com/courses/intr…

in reply to DesChips

@DesChips @arclight But what is the "Invalid" constant? Does it correspond to an actual byte value, or is it a compile-time phantom that the compiler demands be replaced later in code?
in reply to mcc

@arclight

It's just a constant declared to be the value -1 (matching what you have in the code screenshots in this thread). It's an actual byte value that you can store in a variable of type Any_Mem_Element.

A : Any_Mem_Element := Invalid.

and then I excluded this value from the Valid_Mem_Element subtype by defining the range from 0 to last:

subtype Valid_Mem_Element is Any_Mem_Element range 0 .. Any_Mem_Element'Last;

in reply to mcc

If this is a fixed-width type, you could overflow, right?
in reply to Jordan

That is an unrelated problem. (And one that Ada/Spark overall seem worryingly unconcerned with. If I understand the docs right, it only checks for type overflow *when the expression is assigned*? So if you say z := (a + x) * y and (a+x) int-overflows to 2, and 2*y is within the type range, that passes the runtime checks despite being wrong arithmetic. That's disturbing enough I honestly *hope* I am reading the docs wrong.)
Questa voce è stata modificata (5 mesi fa)

mcc reshared this.

in reply to mcc

@jrose if I'm *not* reading the docs wrong, then I interpret this as downstream from Ada trying to introduce guardrails for program correctness in an era before coders had really developed best practices for correctness. (Or at least before security vulnerability authors got really good.)
in reply to mcc

You are reading the docs wrong, see AARM 4.5(10) ada-auth.org/standards/2yaarm/… and 3.5.4(24) ada-auth.org/standards/2yaarm/….

What this is saying is that the implementation could compute the sum a + x in a type wider than that of a and x, where that sum does not overflow.

Questa voce è stata modificata (5 mesi fa)

mcc reshared this.

in reply to EndlessMason

@EndlessMason If that were a problem then there would be a type error on adding two Naturals.
in reply to EndlessMason

@EndlessMason I mean, put a different way, that is a real issue, but it is an issue of a kind the language doesn't actually check for.
in reply to mcc

I guess you figured it out by now, but the issue here is confusion between types and subtypes; the point of defining a new type (type T is range blah, or type T is new U [range blah];), besides allowing for different representations, is to disallow mixing them (as noted above, here I think it makes sense to have two types).

To describe constrained values, use a subtype (subtype T is U range blah;). The type Integer has subtypes Natural (0 .. Integer'Last) and Positive (1 .. Integer'Last).

in reply to Robin Leroy

@eggrobin Hi, sorry… are you saying that

`type Nonzero is new Natural range 1..Natural'Last;`

is introducing the hard "no mixing!" nominal type, whereas

`subtype Nonzero is new Natural range 1..Natural'Last;`

is just like creating a shorthand expansion for `Natural range 1..whatever`, and would have evaded my error above?

in reply to mcc

Exactly (well, no `new` in the subtype declaration).

Also, that subtype Nonzero has a name already, it is called Positive, RM A.1(13) ada-auth.org/standards/2yaarm/….
It is used very often, e.g.,
type String is array (Positive range <>) of Character
ada-auth.org/standards/2yaarm/….

Also note that since Ada 2012, you can have subtypes that are fancier than ranges with Static_Predicate, see the example in RM 3.2.4(40) ada-auth.org/standards/2yaarm/….

Questa voce è stata modificata (5 mesi fa)
in reply to mcc

The thread made me mildly curious, so I took a stab at it.

Caveat, while I am on WG 9 and an observer on the Ada Rapporteur Group, I am there only as a liaison from Unicode; I have only written Ada recreationally, and not much of it at that. My father used to chair the ARG and work on Ada compilers; most of what I know is from him.

github.com/eggrobin/ada-playgr…

in reply to mcc

All my Ada multitasking experience was with the original Ada 83 rendezvous model, which I hear is less commonly used now.
in reply to mcc

“you don’t need to …” and “you just …” my two least favorite responses on StackOverflow.
in reply to mcc

I haven’t looked seriously at ADA in 30 years but it was at one time considered to be The Future™️. To many of us, it was what we considered the ultimate “bondage and discipline” language. But it did have good ideas that informed future languages.

I still don’t want to program in it now.

in reply to mcc

Ada is the language that was used for the intro classes for the CS program I attended in Uni. I already had experience in Java at the time but someone really recommended I go through and do the Ada courses.

I dropped out and didn’t get seriously back into programming for well over a decade because of it. It set the stage for a very unappealing journey at Uni and whomever told me I should do it anyways: I hope they rot in hell.

in reply to mcc

I have this mug that I got from when my dad was working as a SWE in the 70s. I've never written Ada or I imagine I might have to cross out the heart.
in reply to mcc

In Fortran arrays start at 1. Originally you were stuck with that, at some point it was changed so they both endpoints can be specified. Julia also starts arrays at 1 (though I think this can be changed, only tried Julia briefly).
in reply to mcc

A lot of the Pascal-influenced languages do that; it may go back to Algol, I don’t remember. You can do stuff like

TYPE FooA ARRAY[7..10] OF FOO;

and set the valid indexes of the array type. When ISO Pascal introduced Conformant Arrays, you could declare functions with formal parameters like

PROCEDURE MyProc(VAR x : ARRAY [low..high : INTEGER] OF INTEGER);

and low and high would be local variables set to the minimum and maximum index of the array (which may not start at 0).

Fun!

reshared this

in reply to mcc

yes, I feel seen!

Also, to add one more example, I love flexible array indexing as defined in AWK: indexes can be any number: zero, negatives, sparse series, etc. – but arrays generated by awk itself (e. g. by using 'split') are indexed starting on 1.

in reply to mcc

I always remember that Pascal is also 1 indexed. (And Matlab I think, since, sorta, math itself is.) This fact about Ada is super wierd and interesting!
in reply to mcc

You can do this in lua too for the same reason, it's just that all the array-style convenience methods assume a 1 base.
in reply to Roger BW 😷

@RogerBW This will break in conjunction with builtins such as # and in standard VMs may not be as efficient as using arrays in the expected way.
in reply to mcc

Yes, I count # as an "array-style convenience method". Also ipairs.
in reply to Roger BW 😷

@RogerBW Oh, I see. I think it's more than convenience though because if you pass one of your arrays into not just builtin, but *any* other code, it won't work as expected. Contracts are a part of data types
in reply to mcc

different from what everyone else is mentioning, this makes me think of Swift where arrays start at 0 but subsequences have the same indices as the collection they sliced so array[5..<10] has indices starting at 5.
in reply to mcc

It's not true that Lua is the only one. Icon also indexes from 1.
in reply to Oblomov

@oblomov MATLAB might do it because it's a mathematical convention to count matrix indices from 1.

@mcc

in reply to Riley S. Faelan

@riley
and Julia follows MATLAB quite closely (also in row vs column major, plus a lot of sub-indexing methods). Also in Julia there's a package that allows you to create arrays with arbitrary index starts, so I guess that would make it easier to port Ada or Pascal programs. It also has (recommended) array traversal iterators that work regardless of indexing choice, which is actually Very Nice™.
in reply to James Widman

@JamesWidman @riley
true, although that's *technically* because it's lisp and the 0-th element is the head.

In[1]:= l = {1, 2, 3, 4, 5}

Out[1]= {1, 2, 3, 4, 5}

In[2]:= l[[0]]

Out[2]= List

in reply to Oblomov

@oblomov @riley while we're at it, i guess nothing stops C++ users from writing:

T& operator[](size_t ix) {
assert(ix >=1);
return this->ptr[ix - 1];
}

in reply to mcc

Early PC BASICs let you pick 0/1 indexed on a program by program basis.

Is Ada by variable, or by value held in the variable? Like if it's by value, do functions with array args have to inspect the value before accessing index 5?

in reply to PunnO)))

@ieure Ada has typed indexes so you must either restrict to a type that is limited to the allowed values OR the compiler will insert a runtime check and panic if not.
in reply to mcc

Swift does a funny thing where "Collection", the protocol that defines array-like things, has a property "indices" that is an integer range, so it can happily support array-like things that index like Ada. But Array, the actual built-in array implementation, always indexes from 0. But that is a design choice in the standard library, and you could make your own that doesn't do this. If you really wanted. Not sure you would want, though?
in reply to mcc

if I remember you can put an enum in there too!

Tbh I sorta understand why this isn't common anymore. Explosion in types for mild benefits

in reply to mcc

As already said, Fortran arrays start with index 1 unless you declare a different range. But also, arrays are column-major order whereas C-based languages use row-major.

Both design choices (on the part of C, to be sure) make sure you'll have extra fun if you need to port complicated numerical code from one to the other.

Questa voce è stata modificata (5 mesi fa)
in reply to mcc

that is the most “design by committee” thing I ever heard
in reply to abadidea

@0xabad1dea I will admit I sincerely like the idea of an array whose members run 1000 -> 1010, or something, I can think of cases where that would simplify gnarly code. But the risk of a mix of 0 based and 1 based arrays terrifies me. I lived through that in Lua (LuaJIT allows you to handle 1-based Lua arrays and 0-based C FFI arrays in the same code) and it was upsetting.
in reply to mcc

Well, the main gimmick of ada is the typing. Types are supposed to mean much more than "a range of values" - the idea is that all of your types, even the ones that seem 'trivial' like integers, also encode intent. That includes the types you use to index into arrays.

When you do array(1 .. 5) you're actually creating an anonymous subtype of Integer. If you have two different types where one is not a subtype of the other, and you declare an array with those types, you have to cast between the types to switch between the two. That's Ada's way of forcing you to actually take those cases into account.

in reply to duk

@duk @0xabad1dea Unless you've been using Natural or Integer, in which case the subtype criterion fits and you have no protections at all except a panic check at runtime.
in reply to mcc

Yep! At that point you're SOL.

There are surprisingly few guardrails in quite a few places if you don't use SPARK - especially around dynamic memory allocation.

in reply to duk

@duk i'm thinking about upgrading my program to SPARK, except I'm not sure what i'd do once i'd got there, short of just going "well… it compiled in SPARK… that's nice"
@duk
in reply to abadidea

@0xabad1dea This. This was what I felt right when I was looking for more safety-oriented programming languages: Ada did not seem to be designed with intent on getting rid of footguns.
in reply to Marcel(le)

Ada was designed in the late 70s / early 80s. We are aware of more footguns todays than at that time. Especially in the areas that involve an OS like memory allocations or multithreading (OSes were not a standard component of computers at that time).
Also, in Ada, it’s easier to load binary file as you can easily describe the memory layout of types and thus specify a binary serialisation, than to interpret text files.
in reply to 👻 Feufochmar

@Feufochmar @mu @0xabad1dea "in Ada, it’s easier to load binary file as you can easily describe the memory layout of types and thus specify a binary serialisation, than to interpret text files."

I will admit that the absence of a standard way to do this in C is one of my biggest pet peeves about the language.

in reply to mcc

Or there's Visual Basic where you dim an array(10) and get back a thing with 11 elements, I assume because they wanted to support either.
in reply to mcc

Perl would like you to know about $[, which lets you set the array base index to an arbitrary value on a lexically scoped basis.
in reply to mcc

I think that's a Pascal thing? Delphi, at least, can do that too.
in reply to mcc

Have you tried APL? This language (thankfully not replicated in most of its successors like J or BQN) has a global variable called ⎕IO (index offset) which can be set to 0 or 1 to indicate the start index of arrays.

Changing this global variable changes the language behaviour, globally.

Now, it not only changes the way you index variables, but functions that return indexes, or work with indexes also change. So a lot of basic functionality actually remains unchanged when this variable is changed. However, if you want to write a more complicated function that work independently of the value of ⎕IO, you have to be very careful.

In practice, a project decides which one to use, and then you'd better never change it or you'll have a lot of problems.

It's also always fun to discuss APL in a chat, and someone shares some interesting way to solve a problem. You try it and it doesn't work, and of course it's because they used ⎕IO 1.

in reply to mcc

Once upon a time this was considered a standard feature of nested function definitions. See Algol 68. At the compiler building course at university one was expected to implement this. Without restrictions on recursion. There are at least 2 techniques for this, iirc.
in reply to mcc

Apologies if this has already been pointed out in the replies, but you can just use a block statement, no need for a subprogram; so, instead of your procedure call, you would have
declare
Mem : array (Positive range 1 .. Some_Size_That_Is_Known_Here) of Integer range -1..Integer'Last;
begin
-- Do things with Mem.
end;
Questa voce è stata modificata (5 mesi fa)
in reply to mcc

There is a function form of GetLine so I would imagine that the following would work:

Line : constant String := Get_Line; -- From stdin.

or, if you have a file path:

F : File_Type;
begin
Open(F, In_File, "file/path");
declare
Line : constant String := Get_Line(F);
begin

Questa voce è stata modificata (5 mesi fa)
in reply to Pascal Leroy

@PascalHLeroy i tried this and it took stdin input anyway, so it hung until the stdin was received 🙁
in reply to mcc

oof, we need to do that in a lot of projects.
in reply to gaytabase

@dysfun It has a nice fancy text parser built in where it can take like, space-delimited records of ASCII numbers. What if you want like… I dunno, comma-delimited records or a custom parser? Then suddenly everything seems to suck
in reply to mcc

My first language was Algol 68. Strict typing has some disadvantages.
in reply to mcc

I'm very unfamilliar with Ada overall, so take this with a grain of salt; you've probably come across this context already - but I think your point about "There's really important environments [...] maybe it turns out Ada is *perfect* for this environment" is possibly spot on.

I understand that one of the main drivers of Ada's brief popularity was its adoption by the Department of Defense for safety-critical systems. Indeed, it's the main language for the Space Station's MDM processors.

In that setting, NASA's software reliability group (until recently) had a strict set of predicates called the Power of Ten[2]. Rule #3 is: *"Do not use dynamic memory allocation after initialization."* I think that meshes very well with the friction you're referring to. The same rule is laid out in MISRA standards for safe automotive software.

(in practice, I think it's highly debatable whether these prescriptions actually work - there are some very safe embedded systems that use other paradigms - but that's the working hypothesis for many of these projects)

[1] en.wikipedia.org/wiki/The_Powe…

[2] space.stackexchange.com/questi…

Questa voce è stata modificata (5 mesi fa)
in reply to Daniel Correia

@0xdbfb7 The fixed-memory environment is a legitimately interesting one. But it's very much not the only environment! For comparison, consider networked server software. This is an important use case which naturally needs use of dynamic memory allocation, virtual memory, and runtime file I/O, but also would benefit immensely from the safety guarantees Ada/Spark claim.

I understand some languages are narrow-application, but Ada is sold as, and feels designed as, a general purpose language…

in reply to mcc

Ada was introduced when most people were still using files with fixed-length records and it took years to develop and publish a useable standard. It was used by people who cared more about safety than flexibility. I came into contact in the nuclear electricity generation industry. They still won't be vibe coding.
in reply to mcc

this is why i don't use VHDL 😁

"it seems good but is a pain in the ass to actually use" is like, The ADA/VHDL experience

Questa voce è stata modificata (5 mesi fa)
in reply to mcc

Yes, Ada and VHDL (for digital hardware design) both came from the US DoD and show it; very bureaucratic and repetitive in how you have to code them.
in reply to mcc

when I was in college the primary language we were taught was Ada because it was the future (backed by the DoD). Never used it since then, and not even that much in college.
in reply to mcc

Checking your list of candidate languages, one that's not currently listed but seems likely to inspire a similar sense of "interesting in theory, annoying in practice" is Eiffel: en.m.wikipedia.org/wiki/Eiffel…

I learned a lot about OOP in general by learning Eiffel in particular, but I never used it for any actual practical purpose.

in reply to Alyssa Coghlan

@ancoghlan You know, I'm not sure how I forgot Eiffel, as I've been hearing about it for so long! Thanks for the thought.
in reply to mcc

hopefully this comes off as helpful and not an "um actually ada's great" cause ergonomics and learning materials are almost non-existent, it makes up a lot of its own jargon, and as you said, gnat's errors are dogshit.

`Text_IO.Get_Line` has overloads that take a file handle (or `File_Type`) and return a `String`

Also `Option` kind of exists with the catchy name `Ada.Containers.Indefinite_Holder`

godbolt.org/z/KKoGK97M4

in reply to Euclidian Ace

@euclidian In my first version of the code, I found and called the Get_Line overload that takes a filehandle (or at least, I was looking at the Text_IO package definition and sincerely believed I had done so). It *ignored* my filehandle argument, and the entire program blocked waiting for input from STDIO. I do not know what I did wrong, but neither the documentation nor the compiler messages did anything to hint to me what it was I might have done wrong.

mcc reshared this.

in reply to mcc

I used Ada in university for an introductory course. The two things I remember are a) multi-hour compile times due to the load on the single machine that had an Ada compiler license and b) my program usually worked correctly once I finally got it to compile.
Questa voce è stata modificata (5 mesi fa)