Live data from Hacker News

Modern, functional Common Lisp: myths and best practices

ambrevar.xyz

111–120 of 161 posts

Re: Modern, functional Common Lisp: myths and best practices

#111
post #89
post #83

Earlier quoted context omitted.

Of course many implementations do compile to machine code but there are also a few that don't (clisp, ecl, abcl). It is really complicated to talk about languages that have multiple implementations. That is why I went with "interpreter". That was pretty lazy of me :). But you didn't say anything about the original question. Have you seen Common Lisp code deployed as an executable or as source code?

Interpreted in Lisp usually means 'executing s-expressions by a Lisp interpreter'. ECL and CLISP also use compilers. ECL usually compiles to C, which then is compiled to native code via a C compiler. CLISP has its own virtual machine, which is the target of its compiler. Don't know what ABCL does, would have to look it up - but in the end it usually would execute JVM code, which gets compiled by its JIT compiler. Que…

> ScoreCloud is a LispWorks application as an executable and can be bought on Apple's Mac app store.

Wow, super impressive. You didn't post this on reddit yet :)

Re: Modern, functional Common Lisp: myths and best practices

#112
post #96

Assuming that the subtext here is wanting to address potential reasons for Lisp's chronic underpopularity, I think that all conversations like this miss the mark. Lisp was never failing to attract many users because people hadn't experienced sufficient evangelism about all its advanced features. It fails to attract many people because it's not a fun language to get started in. A year or so back I picked up a copy of…

I think that it is even simpler than that. There are languages for people who want to get things done, and languages for people who want the perfect language for getting things done. The problem is that definitions of perfection differ between people, so people who are searching for perfect languages wind up in a small niche of people who agree, separated from people next door whose idea of perfection is just slightl…

I can only speak from the perspective of using Clojure, I toyed around with Racket and have no experience with CL.

That said, I disagree with some of the points you make:

> availability of useful libraries

Clojure is embedded in both JS runtimes and the JVM. Both feature massive ecosystems.

In terms of getting things done:

Languages like Python, JS and so on are fantastic at plumbing. But when it comes to modelling data-structures, manipulation and algorithms then they are far less expressive and productive than Clojure from my experience.

I personally think the main issue really is the learning curve. There are quite a number of things that 'suck' about Clojure initially:

1. In a Lisp you are essentially manipulating an AST rather than writing statement-based text. Initially this is cumbersome and taxing both while reading and writing programs.

2. In a functional Lisp like Clojure (and other FP languages) there is a wide variety of commonly used functions which are used to manipulate and compose data-structures and other functions. Especially when reading code this can be daunting.

3. Setting up a environment correctly for REPL driven development as a beginner and tuning it as an intermediate user is quite an undertaking in comparison to many other languages.

4. Clojure specifically being a hosted language forces you to understand the hosted ecosystem as well plus the wiring between the host and Clojure.

The payoff for all those four points is worth it:

1. Search for paredit visualizations like this one: http://danmidwood.com/content/2014/11/21/animated-paredit.ht.... Manipulating code this way requires mechanical prowess and exercise but scales up really nicely, especially alongside better understanding of Lisp code in general.

2. The incredible variety of functional building blocks scales really well with your experience and understanding. You are programming with a series of descriptive expressions, rather than lower level statements. Code becomes more declarative, dense and expressive. Abstraction is much more fluent.

3. A nicely set-up environment enables very fast development cycles and allows for understanding pieces of code of any scope in isolation, because you can evaluate, change and test any expression instantly.

4. This is in my experience a necessary evil to get higher adoption and a the massive library ecosystems of JS and Java.

I assume there are a lot of capable developers who simply cannot get over 1 possibly while being pushed back by 2-4. And I have to admit: If I wasn't already infected with Lisp at a younger age, I probably wouldn't have bothered with Clojure or any Lisp. But now it is my favorite for manipulating and modelling data AKA doing 'business-logic' and transformations between APIs.

Re: Modern, functional Common Lisp: myths and best practices

#113
post #66

Earlier quoted context omitted.

It's a very important implementation detail. Explicit loops tend to imply mutation, which is contrary to idiomatic functional programming. Recursive calls don't require mutation but do require TCO to achieve equivalent space complexity. Constant-factor optimizations are one thing but failing to perform TCO turns constant-space algorithms into linear-space algorithms (or linear ones into quadratic, etc.). It's less a…

Common Lisp was designed without requiring TCO because: * various platforms don't support TCO. It was designed such that it can be implemented by a simple non-TCO interpreter, transpiled to a non-TCO C compiler, compiled to a non-TCO Lisp Machine CPU, or to a non-TCO virtual machine (like the JVM). Many languages don't support TCO on the JVM and may only implement explicit tail recursion or have a compiler detecting…

I agree with you on the first point. There are good technical reasons why TCO can't be implemented on some platforms. However, that just punts the issue one level down the stack: These platforms should have built in support for guaranteed TCO.

As for language features like dynamic scoping, I would say that a function call which needs to be followed by some cleanup activity is not in tail position, so TCO would not apply. The cleanup code could be in tail position, however, if implemented as a function call. In Common Lisp most forms of dynamic scoping or unwinding are explicit anyway, so this shouldn't come as a surprise as it might in languages like C++ and Rust where destructors are called implicitly when objects go out of scope.

Re: Modern, functional Common Lisp: myths and best practices

#114
post #96

Earlier quoted context omitted.

I think that it is even simpler than that. There are languages for people who want to get things done, and languages for people who want the perfect language for getting things done. The problem is that definitions of perfection differ between people, so people who are searching for perfect languages wind up in a small niche of people who agree, separated from people next door whose idea of perfection is just slightl…

I don’t like this argument and paints a picture that Lispers are overly concerned by some academic aspect of programming. Many people, myself included, use Lisp because it lets us get something done more quickly, more efficiently, more easily, or all three. It’s true. Importing flask in Python and starting a server takes about 1/2 as much code as in Lisp. Some extraordinarily routine stuff Python has down to a one-li…

The following phrase is telling.

"But after a certain very short prototyping period, I end up fighting Python’s terrible deployment, terrible efficiency, and slapdash language implementation when trying to build something robust."

How many programmers would see Python's language implementation as "slapdash" or care? The fact that you both see it that way and care speaks volumes towards you fitting my characterization of the kind of person who uses a minority language.

The other points likewise. Python's inefficiency is well-known and acceptable for most use cases. Where you need better, it lets you escape down to more efficient languages in libraries. Some good examples of this include pandas and various machine learning libraries.

And as for deployment, it is more of an organizational challenge than technical one. The strategy of taking half your servers offline, deploying, then swapping and repeating worked fine 20 years ago. It works fine today. (You might have to do some ceremony with kubernetes. But the strategy remains conceptually similar.) This is only a significant pain point when there are other problems that interfere.

The fact that these things bother you, and bother you enough to rewrite wheels that already exist, speaks volumes.

The fact that people who agree with you on this then can't agree on whether strong typing is worthwhile (like Haskell), or continuations are good (like Scheme) or whether performance is critical (like Common Lisp) or whether access to the JVM toolset matters (like Clojure) causes you to not create a critical mass around a better toolset. And so the situation remains. You all agree that the popular languages are so terrible that you avoid them. But then can't agree on which unpopular language everyone should use instead, or why.

Re: Modern, functional Common Lisp: myths and best practices

#115

Earlier quoted context omitted.

Just mentioning it for historical reasons (someone correct if this is wrong), Rich Hickey was enlightened by CL after doing lots of Java/C#/C++, after doing much CL he created Clojure. To answer your question, the JVM has wide acceptance in the industry and a big ecosystem of libraries, from a business and practical point of view, it seems the most adequate choice for a Lisp?

Yeah, that's why the company I work for lets me use Clojure occasionally; we have a metric ton of libraries written in Java and being able to interop with them more-or-less seamlessly is a huge selling point for Clojure.

For Common Lisp one would use ABCL and probably its JSS library for easier Java calls.

https://abcl.org/

Re: Modern, functional Common Lisp: myths and best practices

#116
post #112
post #96

Earlier quoted context omitted.

I think that it is even simpler than that. There are languages for people who want to get things done, and languages for people who want the perfect language for getting things done. The problem is that definitions of perfection differ between people, so people who are searching for perfect languages wind up in a small niche of people who agree, separated from people next door whose idea of perfection is just slightl…

I can only speak from the perspective of using Clojure, I toyed around with Racket and have no experience with CL. That said, I disagree with some of the points you make: > availability of useful libraries Clojure is embedded in both JS runtimes and the JVM. Both feature massive ecosystems. In terms of getting things done: Languages like Python, JS and so on are fantastic at plumbing. But when it comes to modelling d…

Yes, Clojure is a special case. But still let's address your points.

Languages like Python, JS and so on are fantastic at plumbing. But when it comes to modelling data-structures, manipulation and algorithms then they are far less expressive and productive than Clojure from my experience.

My experience differs. It is hard to find a data structure that cannot be modeled with Python's native data structures with only a constant performance penalty. In fact I believe that I have only seen one, and I don't remember what it is.

I agree with the points that suck about Clojure. To them I must add, "It is hard to hire people who already know the ecosystem." For real businesses this is a non-trivial issue.

As for the purported payoff, I'm glad that you enjoy your editor. For all practical purposes the mechanics of editing code are not a pain point. Besides, I can do the same with vi in any language with braces. (Selecting a block in Python is slightly harder but again has not proven to be a significant challenge.) Designing code as a series of descriptive expressions has more to do with learning to program well than any particular programming language. The third point is not a significant pain point. And as for the fourth point, simply using the hosting language gives you the same library ecosystems without having to understand the wiring and translate all of the examples you find online.

The result is that if you want JS (I only occasionally do), you can just write in JS (or TypeScript). If you want Java (I don't), you can just write in Java.

However Clojure gives you the perfect tool that is customized to exactly how you have to work. Bully for you. My experience with evaluating such environments and tools is that for me to master it is a large investment of time and energy, at the end of which I might or might not (probably not) have much payoff.

(This doesn't stop me from learning said tools. There are good ideas there that I can adopt. But I have not found them particularly useful for me in practice.)

Re: Modern, functional Common Lisp: myths and best practices

#117
post #77
post #68

Earlier quoted context omitted.

The above post didn't say unpopular, they said 'underpopular'. While that's a bit of a neologism, it just implies that it's less popular than it should be. Also: "Lisp is the most fun you can have programming, yeah from the start," while subjective, is by most accounts, wrong. Especially when referring to Common Lisp; there could hardly be a more convoluted Lisp than Common Lisp.

I learned much of my initial Lisp programming decades ago with Macintosh Common Lisp. I found it to be incredible fun to work with a tight interactive environment on my Mac.

So did I--or, rather, I started with Coral Common Lisp, which is what it was called before Apple bought Coral Software.

I found it hugely fun--so much so that it took over my brain. I've been primarily a Lisp programmer ever since.

Coral Common Lisp offered an extremely easy way in to Lisp programming that was also tons of fun to work with, and an industrial-strength development environment for the full panoply of Mac applications. At AAAI in Detroit in 1989 I mentioned to someone that I was working in Coral Common Lisp and a passer by quipped, "Oh, the good Lisp."

It ran just fine on a 1 MB Mac Plus. You could start it up with a double-click. It launched with a Listener window and a blank Lisp source file (unless you launched it by double-clicking an existing Lisp file with some code in it.) You could save an image, copy the image file to a different machine, launch it, and you would see the same windows in the same state.

Creating a fully-functional Mac window looked like:

    (make-instance 'window)
You could populate the window with working widgets with similarly simple interactions. If you built something you wanted to keep using, you could turn it into a Mac application by doing

    (save-application "Foobar")
If you preferred constructing UI by dragging together widgets by direct manipulation, you could do that, too. Once your UI was assembled, you could tell the Lisp to save the Lisp source code needed to reconstruct it.

The built-in editor, Fred, was a species of Emacs, with the Lisp-oriented features that you would expect, but you'd never have to know that if you didn't want to, because it was also a fully-featured Tesler-style modeless editor at the same time.

The stepper, inspector, apropos, and other development niceties all had Mac graphic interfaces that made them easy to discover and tinker with. They were all written in Lisp, so you could use the Lisp reflection tools to crack them open and poke around inside to see how they worked.

Coral Common Lisp made it easy to get started using Lisp to build apps--about as easy at it can possibly be. At the same time, though, it imposed no arbitrary limits on what you could do with it. When I first met Dave Vronay he was working on the GUI for SK8, and gushed about how easy it was to write window-definition resources (WDEFs) in assembly language using CCL's LAP subsystem. He was an experienced arcade-game hacker, well versed in assembler, but Coral Common Lisp was his first fully-interactive assembler, able to define and integrate new assembly-language code while the program under development was running.

The CCL of today, Clozure Common Lisp, is still a great Lisp, but it isn't as easy or as inviting for newbies. It doesn't have the same graphical environment or the tight integration with the underlying system.

In part that's because when the Clozure Common Lisp project was created (under the name "OpenMCL"), its creators had the rights to the compiler and the Lisp runtime, but not to the Macintosh graphical environment. In part it's because modern macOS is quite a bit more complex than Macintosh System 9 and its predecessors, and development systems have a lot of additional hoops to jump through nowadays to do the kinds of things that MCL was able to do.

If a modern MCL existed, I expect a lot more people would find Lisp a lot easier and more fun to get started with. I think it's perfectly possible to create such a Lisp, but it would be a heck of a lot of work.

Re: Modern, functional Common Lisp: myths and best practices

#118
post #12
post #3

I don't consider a language supportive of functional programming unless it supports tail-call optimisation, which is performed by some but not all implementations of Common Lisp. The article recommends SBCL, which does support TCO. An old (2011) survey of TCO support is at https://0branch.com/notes/tco-cl.html

That sounds more like an implementation detail, in most cases. By this metric Haskell wouldn't be a functional language.

Haskell implements call-by-need reduction, in which all calls are tail calls.

Re: Modern, functional Common Lisp: myths and best practices

#119
post #66

Earlier quoted context omitted.

Common Lisp was designed without requiring TCO because: * various platforms don't support TCO. It was designed such that it can be implemented by a simple non-TCO interpreter, transpiled to a non-TCO C compiler, compiled to a non-TCO Lisp Machine CPU, or to a non-TCO virtual machine (like the JVM). Many languages don't support TCO on the JVM and may only implement explicit tail recursion or have a compiler detecting…

I agree with you on the first point. There are good technical reasons why TCO can't be implemented on some platforms. However, that just punts the issue one level down the stack: These platforms should have built in support for guaranteed TCO. As for language features like dynamic scoping, I would say that a function call which needs to be followed by some cleanup activity is not in tail position, so TCO would not ap…

They are often explicit, but they are widely used and often generated behind the scenes by macros or declarations.

Re: Modern, functional Common Lisp: myths and best practices

#120
post #68

Earlier quoted context omitted.

First off, Lisp is chronically POPULAR not unpopular. (It is critically unpopular.) Secondly, Lisp is the most fun you can have programming, yeah from the start. You apparently had either a very poor teacher, or taught yourself Lisp ... same thing.

The above post didn't say unpopular, they said 'underpopular'. While that's a bit of a neologism, it just implies that it's less popular than it should be. Also: "Lisp is the most fun you can have programming, yeah from the start," while subjective, is by most accounts, wrong. Especially when referring to Common Lisp; there could hardly be a more convoluted Lisp than Common Lisp.

Ah. Okay. I misread underpopular as unpopular. My apologies.
Post reply on HN