It describes Relational Lisp as being Lisp enhanced with a database-like language for logic programming.
I suspect this may be it:
https://oceanpark.com/ap5.html
There's a C2 entry for it, of course:
99% of developers will not learn a language that doesn’t look familiar to them on principle. They don’t like it and it’s the end of discussion.
When I started out in the 1990s professional programmers generally made a point of learning new languages, to acquire skills and expose themselves to alternate ways of thinking.
I remember a boss in the early 2ks who was teaching himself OCaml in his spare time just because.
Perhaps only God knows.
And I'm not trying to demean advanced type system extensions or van Laarhoven lenses; I'm just reflecting on my personal journey with Haskell. Playing around with the language in this way is similar to playing around with advanced template meta programming in C++. It just takes experience to have the discipline to know the difference and write simple code and be productive.
At the time I don't think Haskell had any of that, and not sure when monads were introduced in Haskell either (wasn't on day 1 I think). Which means that the language was simpler in some aspects.
But what I do think made the job simpler is that they had easy access to other people that knew Haskell. Whereas, today, unless you have a mentor you're going to need to handle any issues you're encountering via delayed responses on community forums... Or AI, most of the time.
Which is to say this is interesting, but it is a microbenchmark and so of questionable relevance to the real world.
I can't really speak to your problem domain, but I feel like we do a lot with what we have. Most of our pain comes from compile times / linking taking longer than we'd prefer, but we invest a lot of energy and money improving that in a way that benefits the whole Haskell ecosystem.
Not sure what abstractions you are wondering about, though.
It helped that our large application was a bunch of smaller pieces that coordinated through PostgreSQL.
We had three architects who spent their time finding near future problems and making sure they didn't happen.
I've had Haskell jobs with smaller and worse codebases. I think bad code can be created in any language.
Haskell, Ada, C++, Awk: An Experiment in Prototyping Productivity (1994) [pdf] - https://news.ycombinator.com/item?id=33936366 - Dec 2022 (49 comments)
Haskell, Ada, C++: An Experiment in Prototyping Productivity (1994) [pdf] - https://news.ycombinator.com/item?id=19570776 - April 2019 (55 comments)
Haskell vs. Ada vs. C++ an Experiment in Software Prototyping Productivity (1994) [pdf] - https://news.ycombinator.com/item?id=14267882 - May 2017 (59 comments)
Haskell vs. Ada vs. C++ vs. Awk vs (1994) [pdf] - https://news.ycombinator.com/item?id=13275288 - Dec 2016 (68 comments)
Haskell, Ada, C++, Awk: An Experiment in Prototyping Productivity (1994) [pdf] - https://news.ycombinator.com/item?id=7050892 - Jan 2014 (24 comments)
Haskell v Ada v C++ v Awk... An Experiment in Software Prototyping Productivity - https://news.ycombinator.com/item?id=7029783 - Jan 2014 (23 comments)
I think the real test would be to do the same experiment, but not with a prototype. It would be to write a finished program, and then maintain it for a decade or three. (But of course, nobody's ever going to run that experiment - it would be too expensive, plus the time is too long for the "publish or perish" world.)
The point is, more matters than just "how fast can I develop". How fast can I develop code that can be maintained for a long time by other people? How hard is it for them to maintain? How does the choice of language affect that? How does how fast it was developed affect that?
In the real world, speed of development is only one variable, and maybe not the most important one. (And, yes, I'm complaining about the data being inadequate, after noting earlier how rare it was to get data at all...)
Sometimes, you have to bow to industry trends. Otherwise, you're one or two resignations away from being dead in the water. A Haskell solution might be svelte and easy to reason about, for a Haskell programmer. But you have to also be prepared to train new hires on all that, and you may not have that lead time.
Some thoughts on the experiment:
- To get a better idea of the impact of the language on authors' thought processes, it'd probably have to include submissions from more authors in each language. With just one (or so) submission per language, I could see there being variation in expertise.
- I'm curious to see what the documentation looks like here, that there's so much written in some of the submissions, and that the paper authors value it so highly. Is it used to explain what the code does, indicating potentially too-complex code, or is it explaining whys?
- In the "Lessons Learned" section, it's mentioned that other reviewers were not as impressed with Haskell. I'm curious if their reactions were included in the evaluation - to me, these reactions would reduce the success for the understandability (and learnability?) criterion. The paper authors seem to have written this off as "If functional languages are to become more widely used, various sociological and psychological barriers must be overcome".
You say that of the submission that was sent back to the authors to complete the actual code instead of just sending pseudocode...
For the "suspicious" people, this seems to imply the code was final:
> It is significant that [people] were all surprised and suspicious when we told them that Haskell prototype P1 (see appendix B) is a complete tested executable program.
For the people critiquing "cleverness", that seems completely valid whether or not it's actual code or pseudocode.
Haskell appeared to do quite well in the NSWC experiment; even better than we had anticipated! The reaction from the other participants, however, in particular those not familiar with the advantages of functional programming, was somewhat surprising, and is worth some discussion. There were two kinds of responses:
In conducting the independent design review at Intermetrics, there was a significance sense of disbelief. We quote from [CHJ93]: "It is significant that Mr. Domanski, Mr. Banowetz and Dr. Brosgol were all surprised and suspicious when we told them that Haskell prototype P1 (see appendix B) is a complete tested executable program. We provided them with a copy of P1 without explaining that it was a program, and based on preconceptions from their past experience, they had studied P1 under the assumption that it was a mixture of requirements specification and top level design. They were convinced it was incomplete because it did not address issues such as data structure design and execution order."
The other kind of response had more to do with the "cleverness" of the solution: it is safe to say that some observers have simply discounted the results because in their minds the use of higher-order functions to capture regions was just a trick that would probably not be useful in other contexts. One observer described the solution as "cute but not extensible" (para-phrasing); this comment slipped its way into an initial draft of the final report, which described the Haskell prototype as being "too cute for its own good" (the phrase was later removed after objection by the first author of this paper).
We mention these responses because they must be anticipated in the future. If functional languages are to become more widely used, various sociological and psychological barriers must be overcome. As a community we should be aware of these barriers and realize that they will not disappear overnight.
I wonder if this is the 'relational lisp' in question
https://www.ap5.com/ap5-man.html
or
These days I’d be surprise if asking Copilot to start the product wouldn’t cut of the equivalent of 6hours of the Haskell development time.
I would say ML might have been a more popular language, but again it's just a bit too weird for people that aren't deeply into academics / PL research. Also the tooling is just bad, even to this day. (Tbf that is true of Python too.)
As for tooling, few people actually want immersive, robust tooling. If that was popular things like Pharo and Allegro wouldn't be niche. Most want crude text editing with pretty colours and not much more.
you get results, who cares when they have no correctness tying them together.
Monocole-wielding elitist opinions like these scream to me very low productivity environments that would spend weeks on reinventing the wheel rather than getting to market fast.
I love and practice Haskell, but anybody thinking that technologies like these are fit for fast moving startups are absolutely delusional.
You can easily setup a monorepo and release quickly mobile/native/web/apis and whatever with excellent editor and ecosystem support in TypeScript, good luck achieving that on alternative languages.
Last but not least, 99% of people like you criticizing JavaScript have never seen what kind of great goodies there are in the ecosystem, even for writing pure functional programming that scales, e.g. with Effect-ts[1]
I also wonder how the literate programming approach taken with the Haskell solution compares to the "test harness" used by the C++ implementation in terms of documentation. It has been shown that people read code differently from prose and tests suites are somewhat comparable to the "executable specification" that literate programming provides, with the former aligning more closely with reading the implementation.
It'd be interesting to conduct a similar study today with more contemporary contestants like Haskell, C++20, Rust, Python (it's about prototyping after all), Java, C#/F#, and Julia. It'd also be intriguing to learn whether the perception of the functional solutions changed over time.
Writing code is already hard enough as-is due to the complexity imposed by business requirements. On top of that, reading and debugging code is significantly harder than writing it. It is extremely easy to end up with an incredibly clever codebase, which in practice is just a completely unmaintainable mountain of technical debt.
Take for example something like the Fast Inverse Square Root algorithm [0]. It is incredibly clever, but if you randomly come across it most developers won't be able to easily understand how it works. The well-known variant uses undefined behavior, gives a suboptimal result, and doesn't have any meaningful comments. In other words: it cannot be maintained. Having one or two of those in places where they are absolutely necessary isn't a too bad, but imagine having to deal with bugs in a codebase where you come across stuff like this every single day.
Writing clever code is easy. It is far harder and way more important part to write simple code.
> if you randomly come across it most developers won't be able to easily understand how it works
So document it.
> uses undefined behavior
So fix the undefined behavior. Use memcpy or bit_cast.
> gives a suboptimal result
That's probably intentionally sacrificing accuracy for speed. Document it.
> doesn't have any meaningful comments
Then add it.
Clever algorithms is never the problem by itself. It's undocumented unencapsulated clever algorithms with unknown bugs.
Totally agree on having people nearby that knew Haskell already.
I agree, but good code bases do need language support. Some languages cannot easially scale to large code sizes (dynamic types, self modifying code, and other such things that Haskell doesn't have most come to mind as why I've given up on some languages for large code bases - but there may be other things I don't know of that make languages not work at large sizes)
I want this.
I really just want to combine Sets and Maps the way I combine numbers and strings.
But I'm afraid that the network effects that favor large languages are only getting stronger over time. It's very hard to convince someone to learn a new language if they're used to finding the answer to almost every question they can think of on Stack Overflow and find free libraries for almost any task on GitHub. Your niche language will have neither of those things. Large languages will continue to consolidate and become impossible to displace, even if a new language pops up that is strictly better in terms of design.
Entrenched programming languages have reached the point where, I believe, they will never be displaced. So there are certain obvious mistakes, such as every object in Java being nullable, that we will have to live with in perpetuity.
For example, I like python for small programs, but I found around 10-50k LOC python no longer is workable as you will make a change not realizing that function is used elsewhere and because that code path isn't covered in tests you didn't know about the breakage until you ship.
Most of the control flow in a Haskell program is encoded in the types. A “sum type” is a type that represents choices and they introduce new branches to your logic. The compiler can be configured to squawk at you if you miss any branches in your code (as long as you’re disciplined to be wary about catch-all pattern matches). This means that even at millions of lines you can get away with refactorings that change thousands of lines across many modules and be confident you haven’t missed anything.
You can do these things in C++ code based as well but I find the analysis tooling there is building models where in Haskell the types are much more direct. You get feedback faster.
One of the things you'll often hear as a critique levelled against Haskell developers is that we tend to overcomplicate things, but as an organization we skew very heavily towards favoring simple Haskell, at least at the interface level that other developers need to use to interact with a system.
So yeah, basically: Web Request -> Handler -> Do some DB queries -> Fire off some async work.
We also have risk analysis, cron jobs, batch processing systems that use the same DB and so forth.
We're starting to feel a little more pain around maybe not having enough abstraction though. Right now pretty much any developer can write SQL queries against any tables in the system, so it makes it harder for other teams to evolve the schema sometimes.
For SQL, we use a library called esqueleto, which lets us write SQL in a typesafe way, and we can export fragments of SQL for other developers to join across tables in a way that's reusable:
select $ from $ \(p1 `InnerJoin` f `InnerJoin` p2) -> do on (p2 ^. PersonId ==. f ^. FollowFollowed) on (p1 ^. PersonId ==. f ^. FollowFollower) return (p1, f, p2)
which generates this SQL:
SELECT P1., Follow., P2.* FROM Person AS P1 INNER JOIN Follow ON P1.id = Follow.follower INNER JOIN Person AS P2 ON P2.id = Follow.followed
^ It's totally possible to make subqueries, join predicates, etc. reusable with esqueleto so that other teams get at data in a blessed way, but the struggle is mostly just that the other developers don't always know where to look for the utility so they end up reinventing it.
In the end, I guess I'd assert that discoverability is the trickier component for developers currently.
Looked at from the above viewpoint, you should be able to appreciate us "old fogies" mindset and why we refuse to eagerly jump on the C++11 (and later) bandwagon just because it exists. We need a justification for every new thing we are forced to re-learn and use. So my suggestion is to take one new feature at a time, discuss and convince the folks of its utility, maybe make the changes in one single module and demonstrate it. The argument of "its new and shiny" will never fly with us.
Even the old fogies need to be aware of what is happening. I haven't yet done much Rust, but the advocates (when I cut through their Rust religion) are saying some things that really speak to my pain points and so I'm planning to learn it (and have been for a couple years - I may retire before I get around to it, but some new and shiny things are not just glitter and so it remains on my list)
The C++ standards committee really needs to disband itself for a decade and leave us programmers in peace :-) Stroustrup wasn't kidding when in the preface to the 4th edition of TC++PL he said "C++11 feels like a new language".
Finally, i agree that we need to be aware and keep abreast of new languages/features which are genuinely good/useful but i would rather be "slow and sure" than "fast and fail".
Says everyone after having spent months getting the hang of it and more months writing blogs about how everyone is wrong to think it’s difficult because when you think about it “ A Monad is just a Monoid in the Category of Endofunctors”
It’s incredible just how blind most programmers are to the time they spend on learning things to come to the point where they think a subject is simple and how they all assume that any junior should just listen to their magical recital of a boiled down explanation and immediately understand it at a fundamental level.
Haskell is hard - monads in themselves not really.
Honestly do you not even see how naive this idea is that the only thing standing between a subject that literally everyone spends a ton of time on getting good understanding of is a renaming or a catch phrase? Even people who read tons of blogs of “actually it simple just…” end up spending time getting to know it. And every one of those people writing those blogs spent a ton of time on it which is why they are writing blogs about their “eureka moment” that will forever make the subject and instantly learned matter.
x = [1, 2, 3].flatMap(el => if (el % 2 == 0) [] else [ el ]) // x = [1, 3]
vs x = []
for (i = 1, i <= 3, i++) { if (i % 2) == 0) x.push(i) } // x = [1, 3]
or some such. There are advantages and disadvantages to either style of programming. I actually don't like pure functional or imperative style because of the respective tradeoffs each make (functional style requires either significant runtime overhead or heavyweight compiler optimizations and imperative gets messy very quickly and is typically difficult to reason about as program size expands). (remove-if #'evenp '(1 2 3))
vs (mapcan
(lambda (n) (if (evenp n) '() (list n)))
'(1 2 3))
in Common Lisp for example.(Yes I know the original is just a contrived example that you shouldn't read too much into, but...)
This kind of data structure is like the next step after learning how the scalars work.
The primary problem with JavaScript is that one, the entire ecosystem is prone to breaking. It is a very brittle system.
Next, TypeScript gives you an allure of safety, but at the edges it breaks down because underneath it, it's all just untyped JavaScript.
And the last and the most important one is because it makes it so easy to wrap functions in each other and there are no patterns at all, there is a lot of indirection in most typescript code bases.
This causes API surface area bloat which becomes hard to maintain in the long term.
And tooling doesn't completely solve for this problem either. I have seen code bases where the LSP struggles because of tons of generic types.
I think the most pragmatic alternative is Golang if you are building APIs.
And for a full stackish framework Phoenix and Elixir is a great choice.
Or you can just go all in on Rust like my companies and you get the best of everything.
And as for fast moving startups, the most important factor will always be the problem (and it’s myriad sub problem) and hiring. Selecting a language is dependent on those.
So there are all those good reasons for it, but it's still a little weird.
Build a tech team around lisp in your startup then task somebody with scraping a website or handling session tokens refreshing and see the results.
At some point people should ask themselves why there's so little killer software written in Lisps or pure functional languages (besides extremely rare exceptions like Emacs or Datomic) when modest and much hated languages like PHP have plenty.
Have done this task at work - taking a Java crawler and modifying it to be able to navigate saml-based auth.
It's the goddamn pervasive mutation that kills you everytime. The original author will make assumptions like "I can just put my cookies here, and read from them later". And it works for a while, until you add more threads, or crawl different domains, or someone attempts some thread-safety by storing cookies in shudder thread-locals.
(This was actually the project that convinced me that I'll never be able to diagnose or fix a race condition with a step-through debugger.)
I was so fed up with this task that I whipped up a Haskell version at home. I then used that as a standard to test the Java work crawler against - by comparing mitmproxy dumps. If the Java version didn't do what the Haskell version did, I hacked on it some more.
Is working on things that would be useful. The early adopters of modules have reported some very intriguing results - it isn't ready for me to try yet, but so far what the standard committee has done looks like it will be a win for me in 5 more years when I finally can adopt it. Reflection and contracts are two other things coming that have some intriguing uses in the real world and I'm hopeful to see how/if they work out.
There are so many things in programming that are hard to grasp at a deep level, but don't look scary to beginners (who misuse them without realizing). Monads look scary so they take all the blame. I'm not really proposing this, but I guess if monads were called something like "flatmappable", the focus would perhaps shift to the more difficult parts of Haskell: at the root, immutability and purity, and on top of that, the deluge of abstractions and ways to compose them. It wouldn't make Haskell any easier... it would just save the poor monad all the bad rap.
Amen ;-)
John Lakos' old Large Scale C++ Software Design (there is a newer edition which i have not read) taught me C++ "physical design".