Ada, the next Python-killer

With list comprehensions like this, it may be game over for Python:

function Is_CRLF (C : Character) return Boolean is
  (C = Ada.Characters.Latin_1.CR or C = Ada.Characters.Latin_1.LF)
with Inline;

function Without_CRLF (Str : String) return String is
  ([for C of Str when not Is_CRLF (C) => C]);

Shorter:

C in Ada.Characters.Latin_1.CR | Ada.Characters.Latin_1.LF

1 Like

Shorter:

package L1 renames Ada.Characters.Latin_1;
...
C in L1.CR | L1.LF
1 Like

Except that it is CR LF vs LF. So the use case is that you need to test if the text at the current position is CR LF or LF and then advance the position by 2 or 1 characters correspondingly returning True or else return False.

The truth is:

  • Constructed examples are to kill people who are brain dead already. :grinning:
  • Production Ada code is no longer that Python code, provided same functionality and safety.
  • Fancy functional stuff and cutting corners are harmful.

Python is used a lot of different ways. In addition to its straightforwardness, the ecosystem is what makes it great (matplotlib, numpy, pandas, pytorch, scikit, LangGraph).

I use python for cross-platform automation in place of shell or batch or powershell scripts. There is nothing else that even really comes close to the built-in libraries you get out of the box for building tools for this. I’ve seen Ruby used this way before and done experiments with F# scripts (and .NET Core libraries) so you call it a contender as well, but Python sort of stands alone.

I did show Anteforth to my local AI/Lean Startup this week and they were really impressed how you can write executable code that is provable (though not in the same sense as Lean). They were impressed by Contract_Cases (especially 'Old in postconditions/result checks) and quantified expressions.

2 Likes

This I don’t understand, it’s just stripping a string off of characters with any of two matching character values.
If you are criticizing the algorithm, you missed the point (and the post tag).

Please clarify, perhaps it’s me who is missing something.

Now, I don’t know if edge cases like these are safe:

Without_CRLF ("")
Without_CRLF ("" & Ada.Characters.Latin_1.LF)

Other than that, I see no reason why such constructs shouldn’t be used in production.

If you mean trimming strings that is done differently.

Trimming uses a character set, and yes, if you want to trim delimiters then you need to use Unicode categorization for that.

As I said. Anyway you turn it, it is a constructed example with no use case. E.g. when parsing some text source, then recognizing line ends is done differently. When trimming for whatever unlike reason it is a set and the test requires strings.

Time to rename the language Mongoose, I guess.

It is not a constructed example, I lifted it straight from a standard library of another programming language.

I need to think about your words because my experience in this is limited and I just don’t know how to respond further.

The Python logo https://www.pngall.com/wp-content/uploads/2016/05/Python-Logo-PNG-Image.png suggests some serious medical problem. Maybe Scarab? :grinning:

1 Like

To be fair to @dmitry-kazakov, the post was pretty much two one-line code-snippets and a title; that’s awfully little to make as “the point” — granted, since I’m an intuitive type and a bit of a language nerd, I did get your point, which (as I understand it) is essentially:
Given the new constructions such as ‘list comprehensions’ (in addition to the already established pre-/post-conditions, type-invariants, and type-system), Ada is poised to be perhaps even more usable (in ‘smart-people’/non-programmer land) than Python.

Yes the example given was very minimal, but you’re missing the forest for a specific leaf on a specific tree — the given functions strip out CR & LF from a given string and perhaps would have been better illustrated with Whitespace:

-- ASCII is 7-bits.
Subtype ASCII is Character Range Character'Val(0)..Character'Val(2#0111_1111#);
-- DEL (Character #127), and everything before Space are non-graphic characters.
Subtype Non_Graphic is ASCII 
  with Non_Graphic in Character'Val(0)..Character'Val( Natural'Pred(Character'Pos(' ')) ) | ASCII'Last;

function Without_Nongraphics (Str : String) return String is
  ([for C of Str when not in Non_Graphic => C]);
1 Like

What surprised me was that a post tagged as “fun” proclaiming Ada as Python-killer would be taken as anything else than a “fun” joke.
I will refrain from making such posts in the future.

Which doesn’t stop me from appreciating the concise capability iterator aggregates offer as your example only confirms.

Meh, don’t be put off by it.
There’s a lot of different views of what “fun” is.

Except that in real life you would match a space separator (Zs) or tab: U+0009, U+0020, U+00A0, U+1680, U+2000 + etc. Thus either Wide_Wide_String or a specialized map that matches the longest key. Furthermore, a realistic case is that you would consider any sequence of blanks and comments to be whitespace. So you end up with rather complex thing that depends on what you parse.

It all boils down to the software decomposition paradigm, and my actual point is that functional paradigm is bad for software design. It does not scale, it is inefficient, it is unreadable and unmaintainable.

Why do you think I limited my example to ASCII?
Obviously to bypass that as irrelevant to the example, but if you want something that is Unicode, Byron’s definition for Identifier uses the Unicode classifications, perfectly conforming to the LRM’s definition.

Because it was written for ASCII.

FWIW I didn’t notice the tags and thought you were serious at first. I don’t remember how it was I realized you weren’t entirely serious. An emoji wink might have helped.

But, I am a bit slower than molasses in winter, so maybe I’m missing a larger joke. And I got it eventually, and tried to play along, so :person_shrugging:

I disagree about the functional paradigm.

In a little program, I have written

let () = 
   In_channel.read_lines source_filename
   |> List.map ~f:line_to_inst
   |> List.group ~break:(fun a b -> String.(a.cat<>b.cat))
   |> fun list -> Out_channel.with_file ~f:(write_xml list) dest_filename

The first line read the file (returning a list of line), the second parse each line (line_to_inst is the parser which convert a single line to a record), the 3rd convert the list of record to a list of list of record, grouping the list about a criteria, the same category (cat field), then writing an XML file.

The presentation is familiar to any Unix shell script writter (gen_file | grep xxx | sort | gzip > a.gz)

In a typical procedural paradigm, I guess I would open the file, start a big loop. In this loop: read a line, parse it, do someting if it is from a different category than the privious… Most aspect would be entangled. Sure, it would be more scalable than my program, but my use case doesn’t need myriads of lines to be read.

And many functionnal languages can use a procedural approach when needed (generating an HTML file with a Buffer object (StringBuilderin Java) is typically a procedural paradigm application). The idea is to use the best paradigm depending of the constraints.

Here, you note multiple function parameters (each preceded by ~f, the with_file deals with opening, closing… even in case of exception. This can make the code safer. Python has a similar thing (with open(…) as file:)

I understand why like this OCaml code.
At the same time, it is a hard sell, especially on this forum.

I can’t stop laughing that I made this thread as a joke and it devolved into this discussion.
All the more so given that the original isn’t even a functional pattern, it is a loop over a collection that uses Ada’s aggregate.

function Without_CRLF (Source : in String) return String is
   (if Source = "" then ""
    else
       (if Source (Source'First) in Ada.Characters.Latin_1.CR | Ada.Characters.Latin_1.LF
        then "" else Source (Source'First) & "") &
       Without_CRLF (Source (Source'First + 1 .. Source'Last) )  );
2 Likes