[HN Gopher] Austral Programming Language
       ___________________________________________________________________
        
       Austral Programming Language
        
       Author : frutiger
       Score  : 98 points
       Date   : 2023-07-27 19:08 UTC (3 hours ago)
        
 (HTM) web link (austral-lang.org)
 (TXT) w3m dump (austral-lang.org)
        
       | thesuperbigfrog wrote:
       | Has Austral been used for any real-world, production projects?
       | 
       | If so, what?
        
         | nicechianti wrote:
         | [flagged]
        
           | timeon wrote:
           | Maybe there was supposed to be 'which' instead of 'what'.
        
       | IshKebab wrote:
       | > No arithmetic precedence.
       | 
       | Interesting. I've wondered about this when making an expression
       | parser. Obviously it makes parsing way easier and mistaken
       | precedence is often a cause of bugs (especially in C where some
       | of the operator precedence is plain wrong). But on the other hand
       | that's got to be quite annoying surely?
        
         | int_19h wrote:
         | It really depends on how you implement it. I don't think that
         | requiring parentheses in something like a+b*c is annoying, but
         | if they also prohibit a+b+c, that's a different story.
        
         | zetalyrae wrote:
         | In my experience you don't use nested arithmetic often enough
         | to make it annoying. Most arithmetic in computing is basically
         | `count := count + 1`?
         | 
         | What is more annoying to me is looking at an expression that
         | mixes arithmetic and logical/comparison operators and mentally
         | trying to recover the parentheses. Because precedence is not
         | just PEMDAS: it involves all binary operators in the language,
         | including logical and bitwise ones.
        
       | convolvatron wrote:
       | I can understand the intent behind most of the design 'no's -
       | except for subtyping. why is this a problem? I was just looking
       | at the union/sum distinction and the same thing came up. maybe
       | this is a good learning moment.
        
         | Joker_vD wrote:
         | Well, the language has no implicit type conversions so there is
         | no much point in having subtyping either.
         | 
         | On the other hand, subtyping interacts very, very badly with
         | both type inference (it almost instantly becomes undecideable,
         | in practice as well as in theory) and with typeclasses (again,
         | decidability issues).
        
         | dataangel wrote:
         | PL designers like to cast features that make their job harder
         | and user's life easier as "anti-features"
        
           | otabdeveloper4 wrote:
           | This.
        
         | mamcx wrote:
         | > No subtyping.
         | 
         | Adding it open a can of worms. Your type-inference/checking
         | must account for "similar but..." types, you need to follow
         | hierarchies/graphs/trees,etc, you need a way to "re-import"
         | code that "belongs to the super type" (and probably recheck
         | it?), it not always mesh well with other features (or make it
         | harder).
         | 
         | Aside: One of the big reasons to make a bit list of "NO" is to
         | avoid the temptation of "add some sugar to make this easier"
         | but without fully understanding the consequences until becomes
         | later.
        
         | hansvm wrote:
         | Type systems invariably have soundness issues when subtyping is
         | allowed. They might've fixed it in the last couple years, but
         | as an example I'm pretty sure Java and C# both have bugs where
         | some methods should be co/contra-variant and others should not
         | but the language only supports subtyping acting one way or the
         | other for the whole type. For a concrete example:
         | 
         | 1. Suppose Civic is a subtype of Car
         | 
         | 2. Suppose you have a List<Civic>
         | 
         | 3. Suppose a method asks for a List<Car>
         | 
         | 4. You can "clearly" provide that original list of civics
         | because every single thing in the list is a Car, and it's a
         | list of those things, so it adheres to what the method seems to
         | want -- maybe the method computes average cylinder count or
         | something.
         | 
         | 5. Everything we just described is fine and dandy so long as
         | the list itself is immutable (the individual cars could still
         | be mutable safely), but running some of the mutable list
         | methods will cause runtime crashes and segfaults. E.g., if you
         | append a toyota camry to the list then you've somehow snuck a
         | camry into the original List<Civic>.
         | 
         | In that example, some of the methods would be safe if you
         | interpreted a List<Civic> as a List<Car> (like grabbing the car
         | at a particular index and finding that the particular car is a
         | civic), but others require the subtyping relationship to go the
         | other direction (e.g., if you interpreted the List<Civic> as a
         | List<BlueCivic> and appended a BlueCivic then the invariants
         | expected by `append` work in both cases).
         | 
         | That sort of thing just scratches the tip of the iceberg, and
         | as a rule of thumb all your type systems are unsound,
         | _especially_ if they involve subtyping. Things you would hope
         | would be caught at compile-time are punted off to scary runtime
         | heisenbugs that might not be detected for ages. The type system
         | is helpful at reducing errors but woefully incomplete even for
         | the things it's supposed to catch.
        
       | seagreen wrote:
       | Did not expect docs to be this exciting...                   On
       | July 3, 1940, as part of Operation Catapult, Royal Air Force
       | pilots bombed the ships of the French Navy stationed off Mers-el-
       | Kebir to prevent them falling into the hands of the Third Reich.
       | This is Austral's approach to error handling: scuttle the ship
       | without delay.
        
         | KRAKRISMOTT wrote:
         | The British would probably be happy to do that even without the
         | Third Reich. There are still British people today pissed about
         | the French surrendering too quickly.
        
         | jonathankoren wrote:
         | Given how controversial the British attack was on their allies
         | even after the French assured them that ships would not be
         | captured, I guess this passage is, as the kids say, "shots
         | fired". :)
         | 
         | https://en.wikipedia.org/wiki/Attack_on_Mers-el-Kebir
        
       | iroddis wrote:
       | This quite a shallow observation, but I really wish new languages
       | would cut down on verbosity and boilerplate. Making people type
       | out "function" instead of "fn", "def", or nothing at all feels
       | like unneeded friction.
       | 
       | I'm not looking for APL levels of terseness, but I also don't
       | want to have my code mistaken for an essay filled with what
       | amounts to scaffolding.
        
         | linguae wrote:
         | The language's syntax seems to be influenced by languages
         | designed by Wirth, such as Pascal, Modula-2, and Oberon. These
         | languages have many fans, but they have quite verbose syntax
         | compared to either languages influenced by C or those
         | influenced by the ML family.
         | 
         | Personally I prefer more terse syntax, but I've heard some
         | people defend the style of more verbose languages like Pascal
         | and Java when writing large programs.
        
           | eterps wrote:
           | Looks more like Ada to me.
        
         | dustingetz wrote:
         | \ the ultimate
        
         | ralfn wrote:
         | Have you ever seen the d3 javascript visualisation examples?
         | The author has a physics background, so the code ends up being
         | very verbose with lots of comments about simple programming
         | stuff, yet incredibly terse when it comes to heavy mathematics.
         | I would have done the opposite.
         | 
         | It made me realize that people naturally want to be more
         | verbose when they are less comfortable with the concepts and
         | less verbose when they are very familiar with the concepts. It
         | also made me realize it is totally subjective and there may not
         | be a right answer, that it depends on someone's background and
         | familiarities.
        
         | amw-zero wrote:
         | For every person that says this, there's 5 people that say the
         | opposite.
        
           | iroddis wrote:
           | You're right, and that's part of the reason I remarked my
           | comment's shallowness. It was more of a personal lament
           | rather than a strong criticism of what looks like a really
           | interesting language.
        
           | wredue wrote:
           | That's because it fundamentally doesn't actually impact all
           | that much.
           | 
           | By most people's account, the actual coding part of
           | programming isn't really a majority of their time. Domain
           | research, speccing, debugging, testing, planning, etc. the
           | microscopic savings in key presses are just really such a
           | strange thing to even debate about when you think about it.
           | 
           | Major tersness pushes also tend to cause "symbol soup" and,
           | personally, I find symbol soup very hard to digest.
        
             | Yasuraka wrote:
             | >the microscopic savings in key presses are just really
             | such a strange thing to even debate
             | 
             | It's about readability
        
               | cpeterso wrote:
               | Studies of program verbosity have shown that long vs
               | short names have little impact in other programmers'
               | reading comprehension or code maintainability.
        
         | Yasuraka wrote:
         | Reminds me of this talk:
         | 
         | https://www.youtube.com/watch?v=5kj5ApnhPAE
         | 
         | And the quote to go along with it:
         | 
         | "I'm always delighted by the light touch and stillness of early
         | programming languages. Not much text; a lot gets done. Old
         | programs read like quiet conversations between a well-spoken
         | research worker and a well-studied mechanical colleague, not as
         | a debate with a compiler. Who'd have guessed sophistication
         | bought such noise?"
        
           | jll29 wrote:
           | "I'm always delighted by the light touch and stillness of
           | early programming languages."
           | 
           | Except for COBOL, of course, which is one of the oldest.
        
             | masklinn wrote:
             | Or Fortran being terse in all the wrong ways.
        
         | stronglikedan wrote:
         | I'm the opposite. I find the code much easier to read when it's
         | verbose, including very long, descriptive function names and
         | the like. And with modern IDEs and autocomplete, it's not
         | really making people type out anything longer than the first
         | couple of letters anyway. The gains are on the backend, where
         | people are reading the code. And don't even get me started on
         | unnecessary aliases in SQL!
        
           | RussianCow wrote:
           | With syntax highlighting being ubiquitous, does it really
           | matter whether the keyword is "function" or "fn"? And at that
           | point, why not make it terse instead of taking up more space
           | and adding unnecessary noise?
        
             | stephenr wrote:
             | If you're going to take that approach why have any keyword?
             | 
             | `foo(){}` is just as clear as `fn foo(){}`
        
         | hyperhello wrote:
         | I'd like it to just look exactly like C, but with new ideas in
         | some incompatible syntax showing it is not part of C.
        
           | all2 wrote:
           | Zig? Or Nim? Both of those are Algol-ish.
           | 
           | Ooh! What about [0]? Gambit Scheme has what they call `six-
           | script` which is essentially a refactoring of Scheme grammar
           | to fit into an Algol-esque syntax.
           | 
           | [0] http://gambitscheme.org/latest/manual/#GSI, see section
           | 2.6.1
        
           | hardwaregeek wrote:
           | Ehh C is a pretty miserable language to parse. No function
           | definition keyword means you have to get pretty far to
           | realize that you're parsing a function. Pointer syntax is
           | ambiguous with expression grouping. And let's not even get
           | started with the preprocessor. I'd say a good model for
           | simple syntax is Go. Rust has a nice compromise of syntax
           | too, but there are some awkward edge cases.
        
             | jimbob45 wrote:
             | Shame they never fixed this. Would have been nothing to add
             | a fn keyword at the beginning of all function definitions
             | and then use the MS EEE strategy to bring it into the
             | standard. Of the many changes the committee could make with
             | C, I have to believe a function keyword would be among the
             | least contentious.
        
         | khiqxj wrote:
         | (replying to everyone in this subthread, not you) youre
         | literally all wrong. i mean wtf why do people who know nothing
         | have to drop their opinions into these old topics. no, you dont
         | want verbose english in your code. that died out with COBOL.
         | no, there are not 5:1 people asking for longer keywords, unless
         | you count absolute beginners and (often literal) grammar nazis
         | who consider themselves superior because they like to be overly
         | verbose. the way c and its offspring make use of keywords is a
         | pragmatic choice that has nothing to do with proper design, but
         | its pragmatic, what youre asking for is just overkill and no
         | longer pragmatic, based on the whims of some irrelevant people
         | commenting on forums saying "w-well if i saw else if instead of
         | elif that would have literally stopped heartbleed"
        
         | zetalyrae wrote:
         | I'll say the Ada-style syntax grows on you.
        
       | khiqxj wrote:
       | why are these new languages always built to solve problems in
       | windows or unix or whatever
       | 
       | and the main problem in those os are that there are 21 languages
       | and thats before you even start talking about build systems,
       | config files, and query languages and serialization. how would
       | adding a new language to fix memory leaks make anything better? i
       | see much worse problems here like you still have to build an sql
       | query out of a string. the whole rust having this was just to
       | make c people more comfortable to using a post 90s language (i.e
       | memory safe)
        
       | matu3ba wrote:
       | Looking nice, I like the rationale.
       | 
       | Some questions from my side:
       | 
       | - 1. As far as I understand, there are multiple models for a
       | linear type system. Which one does Austral implement? Is it
       | verified to be correct?
       | 
       | - 2. Since there is a static checker: What are the limits on 1.
       | expressivity and 2. scalability?
       | 
       | - 3. What is the intended memory model (pointer and
       | synchronisation)?
        
       | eikenberry wrote:
       | I see 'async' as an anti-pattern and that linear types are good
       | for concurrency, but I don't see anything about concurrency
       | primitives. Is there a plan for concurrency?
        
       | renox wrote:
       | I still didn't see in the documentation what's supposed to happen
       | in case of overflow..
        
         | justincredible wrote:
         | [dead]
        
       | mrkeen wrote:
       | This language looks super promising. With the exceptions of 'no
       | type inference' and 'no arithmetic precedence', I really like its
       | anti-features list.
       | 
       | With regard to 'no arithmetic precedence', I tried
       | printLn((1 + 2) + 3);
       | 
       | and                   printLn(1 + 2 + 3);
       | 
       | Sure enough, the first one compiles, but the second doesn't.
       | 
       | Also, (n-1) is a parse error unless you put a space after the
       | minus.
       | 
       | I got curious if recursion was properly handled, given it wasn't
       | in the anti-features list, but no luck:                   module
       | body Foo is                  function go(acc: Nat64, n: Nat64):
       | Nat64 is                 if n = 0 then                     return
       | acc;                 else                     return go(acc + n,
       | n - 1);                 end if;             end;
       | function main(): ExitCode is                 printLn(go(0,
       | 135000));                 return ExitSuccess();             end;
       | end module body.
       | 
       | yields                   Segmentation fault (core dumped)
        
         | Scarbutt wrote:
         | I would add 'no macros' being a no no too.
        
         | colanderman wrote:
         | I prefer the rule in my own languages of "no arithmetic
         | expressions whose meaning can be changed by adding
         | parentheses". So `x + y - z` is allowed but `x - y + z` is not.
        
           | gpm wrote:
           | If operators are over-loadable, you support floats (in a non
           | ffast-math mode), or you treat overflow in most non-modular-
           | arithmetic ways, (x + y) - z and x + (y - z) are different.
           | 
           | Maybe it's worth saying "they're close enough to the same
           | that parentheses should be optional", but I can definitely
           | see the argument for just requiring them regardless.
        
             | colanderman wrote:
             | Valid point, though my current language supports neither
             | overloading, floats, nor non-modular integer overflow, and
             | parentheses are permissible where not required (for extra-
             | semantical cases where order does matter) :)
        
           | danaugrs wrote:
           | This is a cool idea that I hadn't heard or thought of before.
        
         | zetalyrae wrote:
         | I wouldn't rely on TCO being available, the bootstrapping
         | compiler right now just emits very simple C (though GCC/LLVM
         | might eliminate the recursion if they can).
         | 
         | Ideally I'd like stack overflow to be a clean abort rather than
         | a stack overflow (just to make the error message more explicit)
         | but I haven't got around to adding that.
        
         | joshmarlow wrote:
         | That's curious - one of the example programs computes the
         | fibonacci sequence. The language is clearly still a work in
         | progress. I wonder what the difference is in your recursion and
         | what's listed?
         | 
         | https://austral-lang.org/examples/fib
        
           | mrkeen wrote:
           | > I wonder what the difference is in your recursion and
           | what's listed?
           | 
           | How deep you recurse :D
        
       | pbarnes_1 wrote:
       | This is cool but everything is too verbose.
       | 
       | `austral compile hello.aum --entrypoint=Hello:main
       | --output=hello`
       | 
       | vs
       | 
       | `go build`
       | 
       | Etc etc.
        
         | TOGoS wrote:
         | On the contrary, I like it when the interface to my tools err
         | on the side of precision at the cost of verbosity. I can always
         | write a shell script or Makefile that does all the boring stuff
         | once I've learned what the inputs mean, but if a tool only
         | provides an overly simplified interface, it is much less
         | obvious what it's doing, or what the other options might be, if
         | any.
         | 
         | What does `go build` do? What files does it implicitly rely on?
         | I have no idea. But I have a pretty good idea of what that
         | `austral` command is going to do without having read _any_
         | documentation about it.
        
           | badrequest wrote:
           | You might, but I don't. In the argument
           | `--entrypoint=Hello:main`, where does `Hello` come from? Is
           | it some root module? What about `main`, is that some default,
           | or the name of a file without an extension? This strikes me
           | as just enough verbosity to be confusing, and not enough to
           | be explicit.
        
             | beepbooptheory wrote:
             | Right above that line in the docs is a pretty clear view of
             | 'Hello' and 'main'. Its a file "hello.aum" which declares a
             | module 'Hello' with a function called 'main'. You compile
             | the file to be an executable by giving it function to run
             | as the entrypoint. This really couldn't be clearer I dont
             | think.
        
           | ithkuil wrote:
           | Feel free to invoke "go tool compile" manually. "go build" is
           | just a frontend
        
         | zetalyrae wrote:
         | The idea is the compiler has a bunch of explicit flags, but the
         | build system (which doesn't exist yet) will have the `foo
         | build`, `foo run` etc. commands and find the files using a
         | package manifest.
         | 
         | Essentially like `cargo` vs. `rustc`. I have a little prototype
         | of the build system in Python but haven't pushed it up yet.
        
           | adamrezich wrote:
           | what's encouraging you to conceive of the build system and
           | the language as separate things? I never understood why most
           | people making new languages seem to want to have each of
           | these be distinct--why not just define the build using the
           | same language?
        
             | zetalyrae wrote:
             | So there's differing views on this, Zig famously has the
             | build system built in.
             | 
             | To me, having them separate forces you to keep things
             | simple, because the build system can't communicate with the
             | compiler except through compiler-provided interfaces.
             | 
             | Also, I think I like about languages like C, Rust, is that:
             | if I wanted to, I could implement the build system without
             | forking the compiler. In C specially because Make will
             | print all the compiler invocations for you. It lets people
             | build tooling that is not part of the compiler.
             | 
             | I think it's good from a simplicity perspective that
             | language users can figure out what set of compiler
             | invocations a build file "compiles down" to.
        
       | fs_tab wrote:
       | > designed to be simple enough to be understood by a single
       | person
       | 
       | Interesting that this was mentioned. Are there languages that are
       | not simple enough to be understood by a single person?
        
         | pharmakom wrote:
         | Yes. C++ is it's entirety is (probably) not understood by
         | anyone.
        
       | imglorp wrote:
       | Flashbacks to TA'ing freshman programming 101 in Pascal: every
       | student got hung up on when to use a period or semicolon or end.
       | And from Austral's fib example (snipped)                   module
       | body Fib is             function fib(n: Nat64): Nat64 is
       | if n < 2 then                 else                 end if;
       | end;         end module body.
       | 
       | We here all understand BNF, Ada, Modula, etc and parsing but
       | imagine explaining to the first day student: Why is there no "end
       | function" like for the other contexts? When do I use a semicolon
       | vs a period to close a context? You shouldn't need the "railroad
       | diagram" to understand the syntax.
        
         | zetalyrae wrote:
         | Declarations don't need `end function` because it's always
         | clear what you're closing.
         | 
         | Statements need an `end if`, `end for` etc. because it lets you
         | find your way in nested code. The rationale for the syntax
         | explains it a bit: https://austral-
         | lang.org/spec/spec.html#rationale-syntax
         | 
         | FWIW I will probably get rid of the `module is ... end module.`
         | bit because it adds unnecessary nesting.
        
           | [deleted]
        
           | imglorp wrote:
           | I appreciate the explicit nest around module. I was just
           | suggesting a symmetric and consistent syntax that can be
           | stated simply, to align with the stated goal.
           | 
           | Dare I say, the (important) bit of Lisp syntax fits in a
           | sentence! Yeah they hide the complexity in the library
           | instead...
        
       | angiosperm wrote:
       | I appreciated that it listed "no destructors" immediately after
       | the top-line "no garbage collection", so I didn't need to read
       | any further. What it means is it offers no ability to encapsulate
       | resource management, so not useful for me. That doesn't mean it
       | is not useful to others.
        
         | lionkor wrote:
         | didn't read it yet, but that are other ways, specifically
         | something like `defer` in go or zig
        
         | epage wrote:
         | At least linear types means you'll never forget it. It also
         | solves the problem of what to do when your destructor needs to
         | error (e.g. closing a file).
         | 
         | The big downside is the verbosity of covering every branch of
         | your code with your explicit close calls unless another
         | mechanism is provided.
         | 
         | And it doesn't seem like succinctness is a top priority for
         | this language.
        
         | adamrezich wrote:
         | the page "What are linear types?" seems to address "resources"
         | and the management thereof. not my cup of tea (but then,
         | neither are destructors and garbage collection), but it's an
         | interesting idea.
        
         | rprospero wrote:
         | From my reading of it, it does have what you're looking for.
         | Specifically, while there are "no destructors", you are
         | required to call a function to consume the value. Failing to
         | consume the value is a compile time error. You can roughly
         | approximate thinking about this as having destructors, but
         | you're required to explicitly call them and the compiler won't
         | let you write code that doesn't call the destructor.
        
         | ralfn wrote:
         | [dead]
        
         | zetalyrae wrote:
         | No, on the contrary, Austral is entirely built around resource
         | management. The central concept is linear types, which is about
         | ensuring 1) resources are disposed of and 2) resourced are used
         | according to their protocol, e.g. no use-after-free.
         | 
         | There's "no garbage collection" because Austral lets you have
         | manual memory management without the danger, like Rust.
         | 
         | There are no destructors in the sense of special destructor
         | functions which are called implicity at the end of scope, or
         | when the stack unwinds. Rather, you have to call the
         | destructors yourself, explicitly, and if you forget the
         | compiler will complain.
         | 
         | This sounds verbose until you start paying attention to all the
         | mistakes you make all the time that involve, in some way,
         | forgetting to use a value. The language makes it impossible to
         | forget to do something.
        
       | jll29 wrote:
       | For me, this is of interest:                 Austral's module
       | system is inspired by those of Ada, Modula-2, and Standard ML,
       | with the restriction that there are no generic modules (as in Ada
       | or Modula-3)        or functors (as in Standard ML or OCaml),
       | that is: all modules are first-order.            Modules are
       | given explicit names and are not tied to any particular file
       | system        structure. Modules are split in two textual parts
       | (effectively two files), a        module interface and a module
       | body, with strict separation between the two. The
       | declarations in the module interface file are accessible from
       | without, and the        declarations in the module body file are
       | private.            Crucially, a module A that depends on a
       | module B can be typechecked when the        compiler only has
       | access to the interface file of module B. That is: modules
       | can be typechecked against each other before being implemented.
       | This allows        system interfaces to be designed up-front, and
       | implemented in parallel.
       | 
       | It was a mistake how C++, Java and other languages forgot to
       | split interface declaration from implementation definition, IMHO.
       | Good to see that Austral learned from Modula-2.
       | 
       | Before I can form an opinion regarding Austral, though, I would
       | need to see some larger programs implemented in it, for instance
       | some low-level systems code, a generic data structure, some high-
       | level business logic.
        
       ___________________________________________________________________
       (page generated 2023-07-27 23:00 UTC)