[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)