Lessons learned after working one year as a Common Lisp Developer (2018)
cdagostino.io
cdagostino.io
The JSON library makes sense, there are several ones and they're all making different decisions wrt. representing the JSON types. I wonder if anyone made a comparison of them with https://news.ycombinator.com/item?id=20724672, that would be useful.
Lastly, I'd always add "be wary of pathnames", even though I was (am) a big fan of the concept in general, it's just sometimes a bit fiddly with regards to physical filenames that can appear on various systems (that's e.g. why SBCL's `native-namestring` can be necessary, http://www.sbcl.org/manual/#Pathnames).
I've found in general, while cute that they exist, the ca*d*r family of functions are certainly a code-smell in any lisp/scheme. They're typically a sign you really want a rather different data structure than a list, and if not they can almost always be replaced with `drop`/`take`.
The differences are pretty big. Some use associated lists, some use hash tables, some convert to CLOS, etc
That and format. Which is remarkable. Certainly dense, but in a "this code is clearly responsible for making a string of that list, serves no other purpose." Something lacking in basically every other language, where a depressing amount of coffee exists simply to make a string of a list of objects. (Amusing, as Java finally got streams, which can look like LOOP, but with more periods. And probably more parens...)
[0] http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node74.html
I actually really like it, oddly enough.
I've also been solving them in Ada. It's not as convenient as just read, but if you instantiate an IO package for a type you can do the same kind of thing. So I made an enumeration and an IO package:
type Command is (Forward, Up, Down);
package Direction_IO is new Enumeration_IO(Direction);
use Direction_IO;
Then reading each line was just: C: Command;
D: Integer;
Get(File, C);
Get(File, D);
Other than the setup (3 extra lines), it's as easy as the Lisp version to use.Granted... The solution for some is a four line loop...
(defun get-file (filename)
(with-open-file (stream filename)
(loop for line = (read-line stream nil)
while line
collect (parse-integer line)))) (with-input-from-string (stream "1,2,3")
(let ((*readtable* (copy-readtable)))
(set-syntax-from-char #\, #\ )
(loop for num = (read stream nil nil)
while num
collect num)))That sounds like a blast, I wish I'd thought of that!
as for cl, if you enjoy learning it in this way you can convert norvig's paip book from md to org format using pandoc
org-babel is also nice even if you aren't doing interactive programming, though. I used it to dissect a complex (150k SLOC or so) C++ program that had piss poor "documentation" (auto-generated UML diagrams and similar garbage). By converting each file to a giant source block and then extracting portions I was able to make sense of it much faster than just reading the raw code. I also left the team with my full notes which amounted to a couple volumes worth of material (code + prose) in the end.
(Also, functionality is more of a continuum than a binary category. CL certainly is more functional than Python, but less functional than Scheme or SML. It all depends on where you draw the dividing line, though I’d argue that classifying CL as ‘non-functional’ makes for more meaningful categories.)
Common Lisp at least has lexical scope and first class functions.
Hmm, perhaps I’m misremembering — I know very little about the history of Lisp. I was thinking specifically about the very beginning of the language, where it could be used to define ‘eval’ and nothing much else.
http://bitsavers.org/pdf/mit/rle_lisp/LISP_I_Programmers_Man...
This is very nice with constants. A function can make a hard dependency on standard out for example. If I change that value locally then call the function, it will operate as expected while everything else using that variable name will continue using their own expected values too. Lexical scoping would instead require passing that value in every time it is used.
Some schemes (e.g. Racket) provide immutable data structures.
So I guess Scheme is slightly more functional than CL but with the exception of call/cc the differences are so minor that it doesn't matter.
It's not ergonomic to compose functions in the way that Haskell is due to Lisp's forms essentially making every function able to accept a variable number of arguments. You end up with something like arnesi which provides these functions but it gets slow and verbose to write all those `funcall`'s everywhere. Or you have macros.
Whereas in Haskell, partial application and composition are first-class in the design of the language.
I really enjoyed programming in CL but I would say that it's one of those languages that let's you try functional programming but it's not the sweet spot for it.
Example: the call stack isn't first class. You can only interact with it through the condition system, which is very limited.
Example: CLOS has a meta object protocol, which is great! But there's no equivalent to the MOP for, say, the language rules themselves (first element in a list in the reader is treated as a function namespace reference, for example).
For example in LispWorks it's possible to look at a snapshot of a stack in a Debugger, later.
That said, I'm all for more first class things. I really just meant that common lisp was ahead of the game on number of first class things.
I've been hiring and wildly advertising Lisp jobs for around 10 years now in all sorts of domains. Good candidates always come by, but you'd think that sentiments like yours ("hard to find a lot of jobs") would mean that supply is significantly greater than demand. There is enough supply (if your employer doesn't have lots of stipulations like local-only, citizen-only, etc.; unfortunately mine does), but it's still counter-intuitive to me that supply isn't overwhelming.
I find most people who are attracted to the jobs are
1. Long-time lispers who are pretty happy with their life as it is, and would only take the job if it was truly a killer deal (e.g., work a couple hours a week at a FAANG staff-level full-time salary);
2. College students who are fascinated with programming languages and have dabbled in Lisp; and
There are others, but those dominate in numbers.
I've never, not once, got an applicant that said, "I don't know Lisp but I'd love a job with an interesting language."
Not so remarkably, Haskell programmers, Clojure programmers, and others of those ilk rarely apply. I don't know why, but Lisp has a distinctly different philosophy and lacks a solid community like those languages have.
I've sort of come to the conclusion that advertising it as a Lisp job is perhaps not even so beneficial these days. My biggest success honestly has been finding people who are obviously good at programming, showing them Lisp, then seeing them take off in less than a week and fly to new heights.
I probably wouldn’t ever apply to a role that’s in a niche language I’ve never used. Most niche roles I’ve seen though want staff/principal level domain experts though.
Last time I looked for a job in a niche language (Erlang in my case) most of what I could find was actually looking for Clojure or Scala developers or something, but decided to keyword dump every functional language they’d ever heard of.
I don’t know Lisp and I’d love a job with an interesting language - but it would never occur to me to apply to a job requiring experience with a language that I don’t have experience in.
But then I see a code snippet, something like this manual datetime string parser. Apparently the programmer needed to roll their own, and I wonder: how on earth am I going to convince my corpo overlords that this language belongs anywhere near a production system?
(let ((year (parse-integer string :start 0 :end 4))
(month (parse-integer string :start 6 :end 7))
(day (parse-integer string :start 9 :end 10)))
(- (+ (\* year 31556926) ;; no. of seconds in a year
(\* month 2629743) ;; no. of seconds in a month
(\* day 86400)) ;; no. of seconds in a day
(\* 1900 31556926)))) ;; lisp timestamps start at 1900(This goes for many other languages too. C, JavaScript, Java, Haskell, R, Perl -- they all have their "holes" in the standard library that need to be filled in.)
This is part of the rationale behind Clojure, being able to lean on established platforms (Java and later JS): https://clojure.org/about/rationale
With that said, though, that parsing function doesn't seem all that complicated?
But Common Lisp is not the language to use in production (at least in a larger commercial context!) if your primary need is CRUD or glue. It does fine at those things, but Lisp is better wielded at problems that require "power in the large", like building a new compiler, solver, simulator, or "engine" of some sort, for instance. For those, the programming language matters a lot more than the libraries, in my opinion.
I've had no issue using Common Lisp in production and employing teams to write and maintain it.
Well, I don't see any difference with any other language you could use, from SQL, to C ,CSS or JSON.
The fact is that the higher your corpo overlord, the less is he going to understand your scrawl.
Instead of them understanding your language, it is you who need to speak theirs. You will need to understand MBA talk in order to communicate in simple terms(elevator pitch) why you using this weird language is important for the company.
Then they will leave you alone if what you promised is true, or just fire you if it is not.
Your senior software engineering colleague who is a huge "Python is the second best at everything" fan will be more of a hindrance than your boss, typically. It's not wholly invalid, but it can easily become a social/political issue rather than a technical one. If someone else is proposing a popular approach (like building a backend in Python), and you're proposing an oddball one (like using Lisp), be prepared for their solution to start off with 50 points of consensus and yours zero.
By the time I got it, most of it was calls to Win32 functions, and almost nothing was using any functionality Autohotkey was any good at. I didn't even dare talk about it on the Autohotkey fora at the time, because I was sure the developers themselves would consider me an insane idiot and/or hit me with an axe. I migrated it to another language the day I understood it well enough.
One of the great truths of ICT is: Anything can survive production if someone decides to bless it with insane amounts of time and money.
Not only they are the survivors, they also come with additional libraries for all kind of stuff not covered by the standard.
This is a big reason for the Rhombus project in Racket[1] one of it’s main goals is to be a Lisp like language which combines the extensibility of a parens based language, with infix notation and a different surface syntax
To compare, this is what a psuedo-popular-lang might look like:
let year = parse-integer(string, start=0, end=4)
let month = parse-integer(string, start=7, end=7)
let day = parse-integer(string, start=9, end=10)
return ((year * 31556926 + ;; no. of seconds in a year
month * 2629743 + ;; no. of seconds in a month
day * 86400) - ;; no. of seconds in a day
1900 * 31556926) ;; lisp timestamps start at 1900
Honestly that looks pretty run-of-the-mill to me.Having said that, rolling your own date logic is insanity, as the problem is difficult[1] yet solved[2].
[1] https://gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b... [2] https://common-lisp.net/project/local-time/manual.html
I believe the author worked at grammarly, using CL. Would be nice if he wrote an updated version!
https://www.gnu.org/software/guile/manual/html_node/index.ht...