Since Ada is used for safety-critical systems programming you could argue that it is very serious about it.
>> And tool integration! One of the coolest things Eiffel sorta did was use contracts to infer tests. If you have contracts, you can use a fuzzer to get integration tests for free.
How about tools that extend design-by-contract to formal verification?
If Adacore had published a full featured free compiler for students/universities by the time Ada 95 standard was published, I am sure that Ada could have occupied the space C++ takes today.
Mostly what Ada lacks is not the features but the comunity effect, we saw that when comparing feature parity with C++ and we see that again when comparing feature parity with Rust.
There are a lot of briliant features and tooling in Ada, but unless there is a community effect (or a gigantic industry sponsor e.g. golang) it is difficult to convince people to switch over.
I can't remember who said it so I will paraphrase, the best language/target to program in is whatever your friends/colleagues are using.
>> If Adacore had published a full featured free compiler for students/universities by the time Ada 95 standard was published, I am sure that Ada could have occupied the space C++ takes today.
Agreed. If there had been more free (gratis) or low-cost Ada compilers at that time Ada would be far less niche than it is today.
There is increased interest in Ada today thanks to Rust. There is also interplay between Rust and Ada/SPARK features with Rust getting some Ada features and SPARK getting some Rust features.
Ferrous Systems is working with AdaCore on the Ferrocene Language Specification to formally document the Rust subset that Ferrocene will use:
> If Adacore had published a full featured free compiler for students/universities by the time Ada 95 standard was published, I am sure that Ada could have occupied the space C++ takes today.
> If Adacore had published a full featured free compiler for students/universities by the time Ada 95...
Didn't they? I wasn't around then, so I'm working with what information I can find online. According to Wikipedia it was fully validated in 1995[1], and I'm pretty sure it was always a free compiler based on GCC. I think the dust had settled on the battlefield of the language wars long before Ada 95. It was/is still common for European universities to teach Ada as part of the computer science curriculum. That still hasn't managed to give Ada the industry presence it deserves. In my opinion it's by far the superior language. It's just losing out for human reasons.
> ...gigantic industry sponsor e.g. golang...
It had the biggest industry sponsor at one stage: The US Department of Defense. Back in the late 70s, and early 80s when the DoD was directly sponsoring Ada's development, they had incredibly deep pockets. I'd say this is why Ada is so comprehensive today. They had the money, and motivation to design the language by committee down to the most minor details.
I guess that prior to the FSF releasing GNAT, and the DoD dropping its Ada mandate, compiler vendors had a captive audience, and could charge a serious premium for their software.
I've read that the hardware required to use an Ada compiler was far more expensive than its competitors as well. I've heard more than one person say that this was a serious barrier to its adoption in the wider industry.
(Lots of formal verification languages are based on contracts, like SPARK, Frama-C, and Dafny. I'm interested in uses for contracts that aren't just formal verification, though!)
An example off the top of my head would be writing a serializer/deserializer pair and explicitly saying they are inverses of each other, as opposed to having to implicitly say that in a round-trip property test. Another idea would be being able to say that two data representations are isomorphic under a given transformation, so I can define a function for one transformation and call it on the other. I don't know how useful this would be in practice, but it seems interesting to me!
>> writing a serializer/deserializer pair and explicitly saying they are inverses of each other, as opposed to having to implicitly say that in a round-trip property test.
That capability would be very useful. There are many cases where I have had to define serialization and deserialization functions for numerous objects and had to create so much "boilerplate" unit test code like you describe. It made me wish I was using a Lisp with built-in reader and printer functions.
>> being able to say that two data representations are isomorphic under a given transformation, so I can define a function for one transformation and call it on the other.
This strikes me as very Haskell-ish, but could be quite useful to ensure certain properties or invariants.
When I read "contract-based language"I immediately thought of Ada. I could be wrong but I think SPARK is incorporated into the Ada 2022 standard at this point, it could really be thought of as part of the language.
It's the furthest thing from a "dynamically typed language" but it can probably also check the "math" requirement here too.
Its not public language, but at Risk Management Systems, I worked on Contract Definition Language- an external DSL for computable insurance, re-insurance contracts. This was used to simulate insurance losses when catastrophic events happen (earthquake, hurricane)
RMS never played nice on interoperability. I wouldn’t bet on the CDL to become a useful standard, because it is under-powered, under-specified and stagnant.
The “open” alternative is OASIS. Their Open Exposure Data standard [1] is complete and supported by many industry participants. Unfortunately it’s a database based format, rather than a text based DSL, but still betterthan the alternatives.
Better still would be to take progressively typed languages, combined with contracts and property-based testing, and iterate toward a more precise contract for all of your code whenever it became necessary or whenever you got bored.
What are the consequences of deciding that no Foo's are allowed to be red? Hmm, that's interesting/unintended...
The main difficulties with statically typed languages seem to be both 1) quickly arriving at a workable prototype, and 2) dealing with the consequences of regrettable decisions made early on. Those two being in tension contributes a lot of drama, and not just in analysis paralysis.
> When I’m trying out lots of different equations, keystokes matter a lot.
That's a pretty weird way to do math, honestly. Shouldn't you figure out what equation you need and then just type it? If you're off by one or whatever, adjust it then, you're still only typing it twice.
input = 1
out1: input + 1
#out1 is now 2
input = 4
#out1 is now 5
out2: out1.replace(+, -)
#out2 is now 3
# let's pull an APL
input = 4 2
#out1 is 5 3
#out2 is 3 1
The reactive programming idea a la Excel but text-based looks really neat. I'd love to play around with that kind of thing.
Not really – you won't get into an inconsistent state when everything is reactive. One thing that might lead to catastrophe is having cyclic dependencies, but reasonable runtime/compiler can detect those.
> you won't get into an inconsistent state when everything is reactive
That's like saying "There will never be bugs".
There will be bugs.
And when you come across one and you can change a variable and have another variable affected in some other part of your program and you have no idea how these two variables are connected, you are going to have a nightmare of a time debugging.
You would only have no idea those variables are connected if the tooling for the language can't help you figure that out. Ideally, in such a language you could just query the program state to determine everything that would be influenced downstream. Also step-by-step execution and record/replay or program state can help debug those tricky issues. New paradigms of programming call for new paradigms of tooling and debugging, and figuring out what those are is part of the fun!
Of course there will be bugs. But they won't be caused by the system NOT updating the variable when it should be. But it is still up to the programmer to create a valid reactive graph.
> you have no idea how these two variables are connected
Simply traverse the reactive graph to reveal how/if two variables affect each other.
Cycles and dynamically changing bindings are still problems, but they can be tackled.
This would probably be terrible for application code. But when you are doing quick math/science exploration with mostly linear program flow and want to change a few things upstream, it could be useful. Everything has its place
Flutter can be used for both mobile and desktop apps, although mobile is definitely the primary focus. In my view, Flutter basically equals Dart. I don't know what else people use Dart for.
I use Dart for custom cli tools and recently deployed small service that feeds data to my backend service. It's simple and nice to write, no learning curve to speak of.
Yes it does[1] and it works on Desktop, mobile and web.
JavaFX has SceneBuilder[2] which also fit the description but for some reason, it never caught on. Gluon seems to have that work not only on desktop, but also on mobile.
Of course... it's a very different tech. Flutter's graphics are based on Skia[1], same as used in Android and Chrome. Dart compiles to actual executable binaries, so it's not at all like Electron.
Imba is a compile-to-javascript language that makes HTML and CSS first-class parts of the language as well as react-like custom components. Its the fastest way to create UI that I've found.
Well there are many of them like MATLAB, IDL, scilab, R and others. Python is verbose because it's not a calculator language but a general purpose language. I find J unreadable.
R is my go-to for this, and one I recommend often. It makes a nice calculator language (with only a little weird syntax), has a very rich standard library of functions, and is reasonably fast.
The great thing about R is that it scales well with task difficulty. It works well for a couple numbers. Then you can use data frames, and the Tidyverse (https://www.tidyverse.org/) packages to do a lot of data analysis (especially dplyr, which is extremely powerful). Finally, if you want to look at your data, the built-in plotting capabilities are solid, and ggplot2 is both extremely powerful and reasonably easy to use.
I don't need you to be able to deduce what the J is doing. What I need is to write the computation as fast as possible so I can get the answer I need to know and then get back to whatever else I was doing. If I was writing something I needed to share with someone else, for sure I wouldn't use J!
If you mean with a Graph language, a complex data structure language, you might have a look at https://github.com/FransFaase/DataLang which gives some ideas about modelling complex data structures and where I talk about the different kind of reference that you might want in such a language.
The language I want is a specification language (a bit like Pluscal or P) which allows me to specify distributed system behavior, then ask both correctness (liveness/safety) questions and quantitative behavior questions. I wrote a bit about it here: https://brooker.co.za/blog/2022/06/02/formal.html
You might want to revisit PHP. It is getting better with every release. We have enums, match expressions, union types and much more now!
As for the type system, haven't worked Hack but I absolutely love the gradual typing experience that PHP offers. Yeah, we had some things that could not be expressed but they are slowly being added, again we have finally union types!
I would even say PHP might be the only mainstream language that offers a first class gradual typing experience.
In Python your type hints are a lie as they are not enforced at all and different linter will give you different results and there is no standard.
In JS, you have to use a whole other languages that compiles down to it, slowing you down with an extra compile step.
Meanwhile PHP just works. Just use PHPstan for linting but even if you don't you get at least runtime checks.
PHP might get better. But currently every new PHP release breaks compatibility with some things extensively used in old code bases. It's so much work! I wouldn't use it for new projects just because of that.
PHP tends to be remarkably conservative in this regard. Things which are scheduled to be deprecated will typically just throw a warning for a major version or two before being removed entirely, and the migration documentation is very thorough for each major and point release. Yes, the older a code base is, the more of a pain it will be to migrate it to work with a major release without warnings, but that sort of technical debt will be the same regardless of language.
I don't think that is true sadly. I have never seen a change as offensive as https://www.php.net/manual/de/function.implode.php, where they simply swapped the parameter order of a core function. That is not conservative.
For other languages, Ruby for example feels a lot more stable. I'd expect that tcl, perl and most lisps are as well. But in fact I am still searching alternatives for the times I don't want to use ruby.
They swapped the parameters (after a long period of either order being valid, then the old order being deprecated) to more closely match those of explode(). It was a bug fix if anything; the old form should have been more "offensive" than the change.
It broke programs for some sense of aesthetics. First rule of infrastructure is to not do that, like Linus' "Don't break userspace" command. By definition that can't be a bugfix.
I don't know if it's changed recently but that's something I really appreciated when I wrote PHP: it was unabashedly singular in it's purpose as a web scripting language. The source article talked about VB6 as being a language where the GUI was a first class thing; for PHP the only focus was on making applications around web requests. No useless bloat while trying to be a general purpose shell scripting language; no delusions of grandeur of trying to be an enterprise business logic tier language... it did it's one task, did it well, and left other languages to other tasks. I think we would be better served to have languages will small standard libraries focuses on specific tasks rather than gigantic one-language-to-do-everything but about which nobody could possibly know everything (ahem C# ahem).
> 1. Pretty much everything is request-scoped. This makes it a lot harder to leak resources when everything is torn down;
I write PHP every day (just took a break from that to be here) and the request scoped content is both a nice thing and a pain in the ass some times. It's great that each request is on it's own - really handy for cleanup like you mentioned. There are some thing I want to be shared though, like a DB connection pool, a caching layer (one layer below my Redis layer), and clients for various other services.
Not the end of the world, but an example is AWS Secrets Manager libraries for Python support secrets caching, but they can't offer that in PHP since we won't be able to share the objects. You can use the file system, but that comes with its own hosts of quirks.
That said, PHP really is a fine language. I'm personally not a fan of the use of truthy/falsy values, but they've really dove head first into solid type support. Lot's of good progress has been made with it.
I hadn't seen this before, but I'll absolutely look into this, thank you!
As for why AWS doesn't use it (if that's your question), I assume they didn't want to deal with the need for a system dependency also. That's something that is kind of a pain to me with PHP as well. Some PECL, some Composer makes mentally tracking and managing deps a bit trickier.
The reactive programming language one is really interesting. A compiler ought to be able to compile "normal" straight-line code into conventional efficient code. It's hard to know how that sort of thing would "pollute" the rest of the code, though, like, will every program turn into a rat's nest of dependencies such that it's just impossible to manage or what? Hard to tell without trying, especially since developing such a language would also require significant effort into developing a standard library and best practices to go with it even if the language was in hand today. And one hell of a debugger. Circular dependencies are a problem, too; I'm inclined to ban them but that's probably beyond what a type system can help you with, or at least, any type system I currently know, so it's going to be an adventure.
Still, there's a lot of things that would be more useful with such a language. In-memory highly-correct cache invalidation would become trivial code to write. In fact I think the cache would actually update itself live. Out-of-memory cache invalidation might be pretty easy. Certain auditing things would be easy. UIs would probably take some work to get right but would be interesting, at the very least. Game programming would also be interesting; a lot of game patterns would be incorporated into the language at that point.
Probably need to be lazy; I suspect proactively recalculating everything all the time would be a bad idea. Haskell has pushed lazy programming a long way, but "re-thunkifying" a previously calculated cell would be a new frontier.
Come to think of it, while I think the performance could be improved and you'd want to lift up to a full language eventually, I think a good Haskell programmer could bash together a prototype of this in a few days and start playing with it. It's going to need a lot of playing with before someone casts it into concrete as a language specification and corresponding compiler and runtime.
I'm attempting to build such a language (you can find a link in my profile if you're interested. I don't want too much attention yet since I'm planning on a first release later in the year). You're right it can become difficult to follow code when it's not presented in a linear fashion. The flip side though is that you can write code in smaller chunks that can do a lot, so you write far fewer lines overall. The other thing is that it opens the door to more advanced tooling than we're used to as developers: things like time travel debugging, saving program state and querying it, or running hypothetical execution paths to find the best one. Bret Victor is famous for demoing prototypes of these debugging tools in one of his talks, and they are finally becoming real.
My language turns out to be very fun to write UIs and games in, so I'm glad you honed in on those two examples. Actually one way to view it is that it's a programming language with a game engine as a runtime. But robotics is the primary application I'm targeting.
In fact I had to choose between lazy and eager evaluation, and I've chosen eager because I want latency guarantees. With a lazy evaluation scheme, I was unsatisfied with the windup that would occur in some situations that would totally blow performance. And we don't have to recalculate everything all the time; we only have to recalculate the paths that have an input change. This may lead to recalculating everything, but most of the time we only have to focus on the execution path that is "dirtied" by the updated input.
Anyway if you want to test drive such a language as you're imagining, send me an email and I'll give you a tour/demo. The language isn't really user-friendly yet so there are some pointy edges that make it unusable to new users at this time (which is the main reason I don't want more attention on it until that is resolved.)
You might like optimistic evaluation for this. Once you've got a thunk, send it into a thread pool. When you need the result, it's either been handled in the background already or you compute it on demand (and ideally cancel the in progress copy).
I think the Lisps, and especially Common Lisp, in addition to Smalltalk gives you a lot of the things you mention here. That interactive development environment where you can compile any code at any time is almost a given in these, with example programs such as GNU Emacs and Nyxt, that can be reprogrammed on the fly in release mode too. It is pretty interesting to look at and play with, if you fire up something like Emacs + Slime/Sly and SBCL for Common Lisp or the bundled IDE for a Smalltalk such as Squeak or Pharo.
The reactive programming idea reminded me of Ken Tilton and "Cells", which exploits the flexibility of CLOS (the Common Lisp Object System) to create a reactive programming language on top of Common Lisp.
The reactive language was how I thought Mathematica notebooks should work when I first encountered them, and I was very disappointed to find out they don't. Maybe there's a Jupyter extension to do it?
For what it's worth, Svelte https://svelte.dev/ is a Javascript-to-javascript compiler that adds reactive statements to the language (by using the $ label):
$: b = a + 1
Since it's a compiler, it knows what inputs were used in that statement, and what outputs were mutated. The statement is re-run every time its inputs change.
This is then used to make a web development environment where "react to input changing" is much more natural than with e.g. React.
Mobx works fairly well like this and in our case we use it for the UI where it works well. At the centre you have your pure data and then a bunch of transformations on the way out to your interface (eg, you have a list of all cars, but you have a computed function of all cars available for sale sorted by price). Then at the top, only stuff that’s actually currently used for rendering needs to be computed at keep hot.
Dafny is a really fun language and I've done some stuff in it before. I was really sad when MSR stopped working on it, but I think AWS has picked up the slack?
I'd like to see more languages with built-in, language level support for unit tests. Pyret (https://www.pyret.org/) does this but is considered a "learning language". I'm aware that to many people co-locating units tests with the functions they're testing is "bad" but I find it to be quite the opposite. The two are so tightly coupled that having the unit tests in a separate file, sometimes in a separate source tree, is counterintuitive to me.
You can do this in Rust. Just put a namespace tagged as I think #[cfg(test)] or the like and they can live in the same file but won't be compiled into the non-test binary.
It's true that Racket unit tests are an optional module within the standard library, but the command line tools and package system work directly with it. You can place unit tests right alongside the regular code but within a specially-named "test" submodule, so the unit test code doesn't run at runtime by default, except in certain contexts (package installation, "raco test" command, or running the file from within the Racket IDE).
Yea, I like this about Racket unit tests. It really lowers the barrier to writing tests, and you don't have to go creating separate test files in specific locations.
It could be interesting to think about unit tests and contracts together. In theory a function is valid for anything allowed under its contract, which could make generative testing (aka property-based testing) more powerful and automatic.
It would also be nice to have system-level contracts. Like, say, "every item in list X should have an entry in dictionary Y". Or "no button should be located outside the visible viewport". These are the sorts of things that people procedurally unit test, but expressed in a declarative way you can imagine all sorts of tooling.
Lua is based around tables, which, aren’t quite graphs but seem relatively close enough. What is a graph if not a collection of points associated with their edges?
As far as semantic function relations, I could swear Ada has something like this, but it’s been more than a decade since I’ve touched any.
I had hopes in Shein but it was way too ambitious with way too less design (in the sense of UI/UX and art).
Shein never became a serious language. It's highest point was when @deech made a talk about it and it also promoted their klambda but it had constant problems on every single aspect of the system and the design, from code size expanded as macros to unusable APIs due inconsistent design of libraries and features the author collected from everywhere. I even bought the book and felt deceived after it's unsuitability for anything other than academic examples on how to implement itself.
The closest examples I can thing of to these, in sequence:
- Idris (dependent types rather than contracts, but squint and the UX is the same)
- Python's decorators enable some of this, but Aspect oriented programming may also be what they are looking for. Boomerang is totally different but also an exploration in this space.
- Io, or even JavaScript fit this description
- there's a calculator like this I am forgetting the name of at the moment
- this looks like Dependent typing to me, see Idris above
Liquid Haskell is much closer to contracts than Idris, if you haven't heard of that already. I'm interesting in exploring contracts independently of types, though.
Re the "everything is a graph" idea, Mathematica has been building primitives for some time toward this idea where literally everything is a graph: https://www.wolframphysics.org/
E.g. contracts language could mean better understanding of substructural stuff so we can handle "real resources". But coming up with some syntax for this is the last step.
The one I want to see is an analytics PL where dataframes are 1st class citizen. The only ones that do that right now are SQL. But really pandas shouldn't be a library in python, it should be in python itself with a specific syntax
Dataframes aren't first class in Julia? I guess it's provided by a library. But Julia has really good vector and matrix support with broadcasting, like R and unlike Python, which relies on NumPy for that (Pandas builds on top of NumPy).
The R and Julia syntax are better than Python for vectorized operations, although NumPy and Pandas mostly make up for it using the magic methods.
In terms of keystroke efficiency, nothing beats the array languages. One might think APL is a little too terse, whereas Pandas is a little on the verbose side.
imba looks interesting, and promises full js interoperability, but i went through the docs and could not see anything about how that was done (specifically, using existing javascript libraries from imba).
I'll keep my moaning about VB6 and JavaFX to myself. These days I think HTML5 and a decent theme is probably enough for everything. Yes it can sometimes suck, but there doesn't seem to be much love for heavy app development anymore.
Inform 7 is the closest thing I can think of to "everything is a graph". Basically, objects in the world are vertices and relations are edges. When you first encounter relations they don't necessarily seem that exciting, but it turns out that having relationships between objects be a first-class concept really changes the way you think about and design systems.
I'd like to see a language with baked in support for dependency injections, so that my functions can take two types of parameters: its "real" parameters, which it needs to perform its function, and "dependencies", that are passed implicitly by the runtime.
Basically, a formalization of Dagger/Guice into a language.
As a part of the language standard / standard library, or as a third party crate? I'm confused how this would work, as as far as I'm aware, making this a language-level feature would require significant changes to the ethos of the language.
There are some examples, e.g. the Bevy ECS doing more or less dependency injection for you (and ensuring against things like aliased mutable references), which is pretty neat.
Edit: I think I like it. It doesn't solve dependency injection like e.g. Guice but the examples given, e.g. providing implementations for traits defined in external crates that take arbitrary state, is compelling.
Monadic functional languages have native support for dependency injection and typeclasses/interfaces provide interoperability and easy mocking or alternative implementations with type-checking / propagation of which dependency they are using. (As to avoid mismatching between mocks and a real implementation for example)
This article is just about mocks and doesn't really touch on dependency injection at all, and Haskell simply doesn't have the mechanisms to supply the kind of implicit passing that useful DI requires.
> I also like the idea of modifying function definitions at runtime. I have these visions/nightmares of programs that take other programs as input and then let me run experiments on how the program behaves under certain changes to the source code. I want to write metaprograms dammit
I've been working on an extremely dynamic programming language for a while. Ricing away on the syntax to make it as flexible as possible, whilst maintaining readability.
One of the things that already works is some insane metaprogramming. It doesn't really have functions. It has callable lists of things. Which means you can modify them at runtime (still working on the names for the functions for doing that).
It is stack-based, so it's a little quirky, but as a taste of some very simple meta-programming:
block
varname:
add! 1 $ $varname
: $varname
end
inc-env:
a: 1
inc-env! a
Bare-words are a symbol-type. So they only resolve from the environment when you ask for it with the $ instruction, which is how the above works. A little tedious, but does enable all kinds of dynamism.
Well, now it's been said... Scheme is my all-time favourite language. The regularity of Scheme's syntax, and the way scoping works in the language, are certainly influential on the way I'm going about designing this weird thing.
It is simpler in some ways, mostly because scheme has a "syntax tower" where even the most basic things can be understood in terms of other things. Let can be defined in terms of lambda. Let* in terms of let. Letrec* is also not that hard.
Once you know this and the difference between definition context and bodies (which is simple) there isn't much to add. The top-level works mostly like letrec*. Internal definitions work like letrec* Libraries work like letrec*
Neat! Reminds me of the Lisps around that still have f-exprs, like NewLisp (f-exprs are functions that take their arguments unevaluated, functions are mutable lists of code you can overwrite, etc)
Absolutely! That and Vau Calculus are the inspirations. My vague thought that started it all was that if you take an f-expr, and apply it to a stack machine, you might be able to take a stack-based language from a toy and turn it into something that could look and act professional.
PostScript is one of the very few stack-based languages that is used anywhere, and it's mostly used in anger. Forth has a bit of a following, but the syntax isn't as regular as Lisp, which makes metaprogramming a tad annoying at times. So I was curious if I could use the f-expr to change both of those. Thus far... I think it's a workable concept.
Oh cool, another Vau Calculus enthusiast! There are dozens of us :D
F-exprs for stack machines is very interesting, I'll have to ponder on that - do you have somewhere I can follow your work on your language?
> ...do you have somewhere I can follow your work on your language?
Not really. Not committing to anything publicly has let me feel more free to tear down and start from scratch an embarrassing number of times, and experiment a bit more. Makes it easier not to get over-invested in any one technique.
I have written a little bit, as I've slowly changed my approach. But it's haphazard. Not a whole heap. You can, though, see a few of the previous versions, that do "work":
It's still evolving. You can see where some things have stuck, and others have been tossed by the wayside. The current incarnation isn't public at all, yet.
If I ever get satisfied that I've got the right approach, I'll probably do a big write-up, then.
The fexpr idea is at the very heart of Lisp, which I think makes it truly transposable to other paradigms. Alan Kay credits the fexpr as one of the inspirations for Smalltalk[0] taken from Lisp:
> My next question was, why on earth call it a functional language? Why not just base everything on FEXPRs and force evaluation on the receiving side when needed? I could never get a good answer, but the question was very helpful when it came time to invent Smalltalk, because this started a line of thought that said "take the hardest and most profound thing you need to do, make it great, and then build every easier thing out of it". That was the promise of LISP and the lure of lambda—needed was a better "hardest and most profound" thing. Objects should be it.
You can see the basic idea for the Vau calculus in that quote. Shutt (RIP)[1] later elaborated the theory and proved they were not "trivial" and anathema to compilation [2]. It's great to see new interpretations and uses of the fexpr!
R (yes, the statistics language) has exactly this.
You can literally extract the body of a function as a list of "call" objects (which are themselves just dressed-up lists of symbols), inject/delete/modify individual statements, and then re-cast your new list to a new function object. Or you can construct new functions from scratch this way.
I've written utilities before that will inline specific function calls within the body of another function, or that implement function composition by constructing a new function from scratch.
I don't know why the original devs thought this was necessary or even desirable in a statistics package, but it turns out to be a lot of fun to program with. It has also made possible a wide variety of clever and elegant custom syntaxes, such as a pipe infix operator implemented as a 3rd-party library without any custom language extensions [0]. The pipe infix operator got so popular that it was eventually made part of the language core syntax in version 4.1 [1].
450 comments
[ 1.6 ms ] story [ 326 ms ] threadAda has design-by-contract as part of the language:
https://learn.adacore.com/courses/intro-to-ada/chapters/cont...
Since Ada is used for safety-critical systems programming you could argue that it is very serious about it.
>> And tool integration! One of the coolest things Eiffel sorta did was use contracts to infer tests. If you have contracts, you can use a fuzzer to get integration tests for free.
How about tools that extend design-by-contract to formal verification?
https://learn.adacore.com/courses/intro-to-spark/chapters/01...
SPARK is limited to a subset of Ada, so it is not without limitations, but it can be very useful depending on what you are trying to do.
https://dlang.org/spec/function#contracts
Mostly what Ada lacks is not the features but the comunity effect, we saw that when comparing feature parity with C++ and we see that again when comparing feature parity with Rust.
There are a lot of briliant features and tooling in Ada, but unless there is a community effect (or a gigantic industry sponsor e.g. golang) it is difficult to convince people to switch over.
I can't remember who said it so I will paraphrase, the best language/target to program in is whatever your friends/colleagues are using.
Agreed. If there had been more free (gratis) or low-cost Ada compilers at that time Ada would be far less niche than it is today.
There is increased interest in Ada today thanks to Rust. There is also interplay between Rust and Ada/SPARK features with Rust getting some Ada features and SPARK getting some Rust features.
Ferrous Systems is working with AdaCore on the Ferrocene Language Specification to formally document the Rust subset that Ferrocene will use:
https://ferrous-systems.com/ferrocene/
https://ferrous-systems.com/blog/ferrocene-language-specific...
It is exciting to see Rust mature so it can one day be used in safety-critical work.
Don't look a gift horse in the mouth.
The same can be said about Eiffel.
Didn't they? I wasn't around then, so I'm working with what information I can find online. According to Wikipedia it was fully validated in 1995[1], and I'm pretty sure it was always a free compiler based on GCC. I think the dust had settled on the battlefield of the language wars long before Ada 95. It was/is still common for European universities to teach Ada as part of the computer science curriculum. That still hasn't managed to give Ada the industry presence it deserves. In my opinion it's by far the superior language. It's just losing out for human reasons.
> ...gigantic industry sponsor e.g. golang...
It had the biggest industry sponsor at one stage: The US Department of Defense. Back in the late 70s, and early 80s when the DoD was directly sponsoring Ada's development, they had incredibly deep pockets. I'd say this is why Ada is so comprehensive today. They had the money, and motivation to design the language by committee down to the most minor details.
[1] https://en.wikipedia.org/wiki/GNAT
I've read that the hardware required to use an Ada compiler was far more expensive than its competitors as well. I've heard more than one person say that this was a serious barrier to its adoption in the wider industry.
(Lots of formal verification languages are based on contracts, like SPARK, Frama-C, and Dafny. I'm interested in uses for contracts that aren't just formal verification, though!)
Ada 2012 design-by-contract features aren't just for formal verification, they can be used in a general purpose way similar to Eiffel:
https://learn.adacore.com/courses/intro-to-ada/chapters/cont...
Can you elaborate a bit more about the wanted features for a language with semantic relations?
That capability would be very useful. There are many cases where I have had to define serialization and deserialization functions for numerous objects and had to create so much "boilerplate" unit test code like you describe. It made me wish I was using a Lisp with built-in reader and printer functions.
>> being able to say that two data representations are isomorphic under a given transformation, so I can define a function for one transformation and call it on the other.
This strikes me as very Haskell-ish, but could be quite useful to ensure certain properties or invariants.
It's the furthest thing from a "dynamically typed language" but it can probably also check the "math" requirement here too.
[0] https://www.riskdataos.org/html/HelpCenter/Content/CDL/CDL_S...
The “open” alternative is OASIS. Their Open Exposure Data standard [1] is complete and supported by many industry participants. Unfortunately it’s a database based format, rather than a text based DSL, but still betterthan the alternatives.
[1] https://github.com/OasisLMF/OpenDataStandards/tree/master/Op...
What are the consequences of deciding that no Foo's are allowed to be red? Hmm, that's interesting/unintended...
The main difficulties with statically typed languages seem to be both 1) quickly arriving at a workable prototype, and 2) dealing with the consequences of regrettable decisions made early on. Those two being in tension contributes a lot of drama, and not just in analysis paralysis.
https://github.com/tupl-tufts/rdl
https://flang.dev/tutorial/pre_post_conditions https://github.com/tokiwa-software/fuzion
Disclaimer, I work in the Fuzion team.
That's a pretty weird way to do math, honestly. Shouldn't you figure out what equation you need and then just type it? If you're off by one or whatever, adjust it then, you're still only typing it twice.
https://docs.microsoft.com/en-us/power-platform/power-fx/ove...
[1]https://github.com/fonsp/Pluto.jl
That's like saying "There will never be bugs".
There will be bugs.
And when you come across one and you can change a variable and have another variable affected in some other part of your program and you have no idea how these two variables are connected, you are going to have a nightmare of a time debugging.
> you have no idea how these two variables are connected
Simply traverse the reactive graph to reveal how/if two variables affect each other.
Cycles and dynamically changing bindings are still problems, but they can be tackled.
Delphi and friends[1] is still out there. There's also Lazarus[2] if you don't want to fork off a few grands.
[1] https://www.embarcadero.com/products [2] https://www.lazarus-ide.org/
[0]https://dart.dev/
JavaFX has SceneBuilder[2] which also fit the description but for some reason, it never caught on. Gluon seems to have that work not only on desktop, but also on mobile.
[1] https://docs.flutter.dev/development/tools/devtools/overview
[2] https://gluonhq.com/products/scene-builder/
[1] https://skia.org/
https://imba.io
Well there are many of them like MATLAB, IDL, scilab, R and others. Python is verbose because it's not a calculator language but a general purpose language. I find J unreadable.
The great thing about R is that it scales well with task difficulty. It works well for a couple numbers. Then you can use data frames, and the Tidyverse (https://www.tidyverse.org/) packages to do a lot of data analysis (especially dplyr, which is extremely powerful). Finally, if you want to look at your data, the built-in plotting capabilities are solid, and ggplot2 is both extremely powerful and reasonably easy to use.
> import math prod([math.factorial(x) for x in l]) No! Bad python! In J it’s just / ! l, quite literally an order of magnitude fewer keystrokes.
I don't know anything about `match.factorial()` or `prod()` but I can deduce what it's doing. `/ ! l` is nonsense.
Meta
To be clear, I like PHP. It actually has many attributes that make it almost ideal as a Web development language, most notably:
1. Pretty much everything is request-scoped. This makes it a lot harder to leak resources when everything is torn down;
2. A stateless functional core. This avoids a lot of class loading that has dogged Java (as one example);
3. No ability to start threads. This is actually a positive. Most application code should avoid creating threads like the plague.
But PHP comes with a lot historical cruft. It's type system is also primitive (Hack is a better example of this).
Where I think this could really shine is in data analysis (ie the numpy/scipy realm).
As for the type system, haven't worked Hack but I absolutely love the gradual typing experience that PHP offers. Yeah, we had some things that could not be expressed but they are slowly being added, again we have finally union types!
I would even say PHP might be the only mainstream language that offers a first class gradual typing experience.
In Python your type hints are a lie as they are not enforced at all and different linter will give you different results and there is no standard.
In JS, you have to use a whole other languages that compiles down to it, slowing you down with an extra compile step.
Meanwhile PHP just works. Just use PHPstan for linting but even if you don't you get at least runtime checks.
For other languages, Ruby for example feels a lot more stable. I'd expect that tcl, perl and most lisps are as well. But in fact I am still searching alternatives for the times I don't want to use ruby.
I don't know if it's changed recently but that's something I really appreciated when I wrote PHP: it was unabashedly singular in it's purpose as a web scripting language. The source article talked about VB6 as being a language where the GUI was a first class thing; for PHP the only focus was on making applications around web requests. No useless bloat while trying to be a general purpose shell scripting language; no delusions of grandeur of trying to be an enterprise business logic tier language... it did it's one task, did it well, and left other languages to other tasks. I think we would be better served to have languages will small standard libraries focuses on specific tasks rather than gigantic one-language-to-do-everything but about which nobody could possibly know everything (ahem C# ahem).
I write PHP every day (just took a break from that to be here) and the request scoped content is both a nice thing and a pain in the ass some times. It's great that each request is on it's own - really handy for cleanup like you mentioned. There are some thing I want to be shared though, like a DB connection pool, a caching layer (one layer below my Redis layer), and clients for various other services.
Not the end of the world, but an example is AWS Secrets Manager libraries for Python support secrets caching, but they can't offer that in PHP since we won't be able to share the objects. You can use the file system, but that comes with its own hosts of quirks.
That said, PHP really is a fine language. I'm personally not a fan of the use of truthy/falsy values, but they've really dove head first into solid type support. Lot's of good progress has been made with it.
As for why AWS doesn't use it (if that's your question), I assume they didn't want to deal with the need for a system dependency also. That's something that is kind of a pain to me with PHP as well. Some PECL, some Composer makes mentally tracking and managing deps a bit trickier.
Still, there's a lot of things that would be more useful with such a language. In-memory highly-correct cache invalidation would become trivial code to write. In fact I think the cache would actually update itself live. Out-of-memory cache invalidation might be pretty easy. Certain auditing things would be easy. UIs would probably take some work to get right but would be interesting, at the very least. Game programming would also be interesting; a lot of game patterns would be incorporated into the language at that point.
Probably need to be lazy; I suspect proactively recalculating everything all the time would be a bad idea. Haskell has pushed lazy programming a long way, but "re-thunkifying" a previously calculated cell would be a new frontier.
Come to think of it, while I think the performance could be improved and you'd want to lift up to a full language eventually, I think a good Haskell programmer could bash together a prototype of this in a few days and start playing with it. It's going to need a lot of playing with before someone casts it into concrete as a language specification and corresponding compiler and runtime.
My language turns out to be very fun to write UIs and games in, so I'm glad you honed in on those two examples. Actually one way to view it is that it's a programming language with a game engine as a runtime. But robotics is the primary application I'm targeting.
In fact I had to choose between lazy and eager evaluation, and I've chosen eager because I want latency guarantees. With a lazy evaluation scheme, I was unsatisfied with the windup that would occur in some situations that would totally blow performance. And we don't have to recalculate everything all the time; we only have to recalculate the paths that have an input change. This may lead to recalculating everything, but most of the time we only have to focus on the execution path that is "dirtied" by the updated input.
Anyway if you want to test drive such a language as you're imagining, send me an email and I'll give you a tour/demo. The language isn't really user-friendly yet so there are some pointy edges that make it unusable to new users at this time (which is the main reason I don't want more attention on it until that is resolved.)
https://github.com/kennytilton/cells
and he has slides from a talk
https://github.com/kennytilton/cells/blob/main/Lisp-NYC-2018...
to give context.
Reactivity in Jupyter would probably have to be provided by the kernel, not by the Lab frontend.
https://powerapps.microsoft.com/en-us/blog/power-fx-open-sou...
https://github.com/microsoft/power-fx-host-samples/tree/main...
This is then used to make a web development environment where "react to input changing" is much more natural than with e.g. React.
You also want to look at Dafny for a contract-based language: https://github.com/dafny-lang/dafny
Since it has verification support it also covers the second point about semantic relations.
…have to poke around a bit methinks.
you can create comments above methods that act as tests
It's true that Racket unit tests are an optional module within the standard library, but the command line tools and package system work directly with it. You can place unit tests right alongside the regular code but within a specially-named "test" submodule, so the unit test code doesn't run at runtime by default, except in certain contexts (package installation, "raco test" command, or running the file from within the Racket IDE).
[1] https://dlang.org/spec/unittest.html
[2] https://ziglearn.org/chapter-1/
It would also be nice to have system-level contracts. Like, say, "every item in list X should have an entry in dictionary Y". Or "no button should be located outside the visible viewport". These are the sorts of things that people procedurally unit test, but expressed in a declarative way you can imagine all sorts of tooling.
Lua is based around tables, which, aren’t quite graphs but seem relatively close enough. What is a graph if not a collection of points associated with their edges?
As far as semantic function relations, I could swear Ada has something like this, but it’s been more than a decade since I’ve touched any.
In Shen, the contract rules applied on each function are themselves a Turing complete language::
https://thestrangeloop.com/2014/shen-a-sufficiently-advanced...
https://shenlanguage.org
"static type checking based on sequent calculus"
"one of the most powerful systems for typing in functional programming"
Shein never became a serious language. It's highest point was when @deech made a talk about it and it also promoted their klambda but it had constant problems on every single aspect of the system and the design, from code size expanded as macros to unusable APIs due inconsistent design of libraries and features the author collected from everywhere. I even bought the book and felt deceived after it's unsuitability for anything other than academic examples on how to implement itself.
- Idris (dependent types rather than contracts, but squint and the UX is the same) - Python's decorators enable some of this, but Aspect oriented programming may also be what they are looking for. Boomerang is totally different but also an exploration in this space. - Io, or even JavaScript fit this description - there's a calculator like this I am forgetting the name of at the moment - this looks like Dependent typing to me, see Idris above
E.g. contracts language could mean better understanding of substructural stuff so we can handle "real resources". But coming up with some syntax for this is the last step.
The R and Julia syntax are better than Python for vectorized operations, although NumPy and Pandas mostly make up for it using the magic methods.
In terms of keystroke efficiency, nothing beats the array languages. One might think APL is a little too terse, whereas Pandas is a little on the verbose side.
And it's fast, enjoyable and productive
I bet that changes eventually when we reinvent personal computing again (currently we are reinventing mainframes).
Basically, a formalization of Dagger/Guice into a language.
There are some examples, e.g. the Bevy ECS doing more or less dependency injection for you (and ensuring against things like aliased mutable references), which is pretty neat.
Reminds me of implicit parameters in Scala - https://docs.scala-lang.org/tour/implicit-parameters.html
Edit: I think I like it. It doesn't solve dependency injection like e.g. Guice but the examples given, e.g. providing implementations for traits defined in external crates that take arbitrary state, is compelling.
https://itnext.io/hmock-first-rate-mocks-in-haskell-e59d7c3b...
What other ways do you have in mind?
I am not aware of any Haskell functionality or compiler flag that enables implicit parameter passing, like Java, Kotlin, and Rust allow.
An approach to do it statically is with implicit parameters. Scala has them, for example.
I've been working on an extremely dynamic programming language for a while. Ricing away on the syntax to make it as flexible as possible, whilst maintaining readability.
One of the things that already works is some insane metaprogramming. It doesn't really have functions. It has callable lists of things. Which means you can modify them at runtime (still working on the names for the functions for doing that).
It is stack-based, so it's a little quirky, but as a taste of some very simple meta-programming:
Bare-words are a symbol-type. So they only resolve from the environment when you ask for it with the $ instruction, which is how the above works. A little tedious, but does enable all kinds of dynamism.Once you know this and the difference between definition context and bodies (which is simple) there isn't much to add. The top-level works mostly like letrec*. Internal definitions work like letrec* Libraries work like letrec*
PostScript is one of the very few stack-based languages that is used anywhere, and it's mostly used in anger. Forth has a bit of a following, but the syntax isn't as regular as Lisp, which makes metaprogramming a tad annoying at times. So I was curious if I could use the f-expr to change both of those. Thus far... I think it's a workable concept.
Not really. Not committing to anything publicly has let me feel more free to tear down and start from scratch an embarrassing number of times, and experiment a bit more. Makes it easier not to get over-invested in any one technique.
I have written a little bit, as I've slowly changed my approach. But it's haphazard. Not a whole heap. You can, though, see a few of the previous versions, that do "work":
+ 2 years ago: https://git.sr.ht/~shakna/esom
+ 3 months later: https://git.sr.ht/~shakna/stackly
+ 6 months ago: https://git.sr.ht/~shakna/james_scriptlang
It's still evolving. You can see where some things have stuck, and others have been tossed by the wayside. The current incarnation isn't public at all, yet.
If I ever get satisfied that I've got the right approach, I'll probably do a big write-up, then.
> My next question was, why on earth call it a functional language? Why not just base everything on FEXPRs and force evaluation on the receiving side when needed? I could never get a good answer, but the question was very helpful when it came time to invent Smalltalk, because this started a line of thought that said "take the hardest and most profound thing you need to do, make it great, and then build every easier thing out of it". That was the promise of LISP and the lure of lambda—needed was a better "hardest and most profound" thing. Objects should be it.
You can see the basic idea for the Vau calculus in that quote. Shutt (RIP)[1] later elaborated the theory and proved they were not "trivial" and anathema to compilation [2]. It's great to see new interpretations and uses of the fexpr!
[0] http://worrydream.com/EarlyHistoryOfSmalltalk/
[1] https://en.wikinews.org/wiki/Wikinews_mourns_loss_of_volunte...
[2] https://fexpr.blogspot.com/2011/04/fexpr.html
You can literally extract the body of a function as a list of "call" objects (which are themselves just dressed-up lists of symbols), inject/delete/modify individual statements, and then re-cast your new list to a new function object. Or you can construct new functions from scratch this way.
I've written utilities before that will inline specific function calls within the body of another function, or that implement function composition by constructing a new function from scratch.
I don't know why the original devs thought this was necessary or even desirable in a statistics package, but it turns out to be a lot of fun to program with. It has also made possible a wide variety of clever and elegant custom syntaxes, such as a pipe infix operator implemented as a 3rd-party library without any custom language extensions [0]. The pipe infix operator got so popular that it was eventually made part of the language core syntax in version 4.1 [1].
[0]: https://magrittr.tidyverse.org/
[1]: https://www.r-bloggers.com/2021/05/the-new-r-pipe/
Arguments sent as `[blocks]` are always unevaluated but you can also explictly set a function argument to not be evaluated...
Outputs: And the function body is mutable...