
Lisp and Haskell (2017) - sridca
https://markkarpov.com/post/lisp-and-haskell.html
======
thibran
My problem with Haskell is that it is not pragmatic and the reason I stopped
using it. The philosophy of separating pure and impure code – and to enforce
it – does more harm than good. Often its difficult to understand what your
"forth level of abstraction code" above the usual abstraction levels really
does. Haskell is fun to learn. Haskell has a lot of interesting concepts to
explore and it will make you a better programmer. Haskell code can be really
dense. And Haskell code can be totally unreadable, using concepts which take
weeks to learn – which is... not so great.

Lisp (Common Lisp) is what you get when you reduce the rules and syntax to a
minimum and try at the same time to be a maximal flexible programming
language. This simplicity and applied pragmatism is what makes it so great.
Like the authors in the Lisp books warned, after learning Lisp, coding won't
be the same. Now I see everywhere code that could easily be written in Lisp,
but is not so great in language XYZ. All those SDL's and stringified API's and
code-generators they need to do real things and data formats like JSON... all
obsolete, if they would have chosen Lisp (or a language with brackets and Lisp
like evaluation rules).

The other thing that is often overlooked regarding Lisp is, that in a Lisp
like language there is no need to wait for a new version of the programming
language to be released to get new syntax – you can simply add it yourself (or
have a library doing it for you). E.g. a lot of the JavaScript mess could be
fixed by code transformation, if they would have chosen a Lisp like syntax in
the first place.

I don't see Haskell as uber language. If I want super correct code, speed and
I'm okay with using a more complex language, then there is Rust – which is
probably more correct than Haskell and faster, too.

Haskell, Lisp and C++ would really benefit form a std library 2.0 and a
default package manager... one can dream.

~~~
bad_user
The ability to come up with new syntax in LISP is in my opinion vastly
overrated.

Yes, LISP code can be easily parsed for as long as you stick to lists. However
the ability to do anything complicated with LISP code is simply not there
because LISP code loses type info, which is extremely important for anything
you’d want to do with code, like all kinds of non-superficial transformations
and refactorings.

So here are some facts given as examples:

(1) LISP code does not make the job of IDE authors easier; just ask the
authors themselves

(2) in terms of macros, anything more complicated than lazy evaluation of
thunks and kiddie examples is out of reach; for example .NET’s LINQ cannot be
expressed in LISP, not unless you add a static type system, but at that point
you’re no longer talking about LISP ;-)

In my opinion LISP has been vastly romanticized, yet it doesn’t live up to
expectations. It falls short in every department, from functional programming,
to available tooling, to the great enlightenment people are supposed to
experience once they learn it.

Whereas Haskell really does live up to expectations, being one of those very
few ecosystems that does make developers better, at this point being the
lingua franca of FP.

~~~
na85
>for example .NET’s LINQ cannot be expressed in LISP, not unless you add a
static type system

Doesn't that imply that Lisp, sans-static typing, is not Turing-Complete?
Surely that is not the case.

~~~
nine_k
A Turing machine is Turing-complete.

Good luck expressing LINQ syntax, or Lisp syntax, or even Basic-80 syntax on
it.

Syntax is important, it's a tool of thought.

~~~
na85
I understood the post to which I replied to be arguing that LINQ could not be
expressed in Lisp, not that Lisp doesn't have the correct syntax.

Surely you can write LINQ in any Turing-complete language.

~~~
crabmusket
I guess the question you are begging is: how far can Lisp-LINQ go from the
syntax, semantics, and ergonomics of C#'s LINQ before you stop calling it
LINQ?

Turing completeness doesn't describe expressivity of source code, as far as I
understand it - it describes the ability to produce a desired outcome. So yes,
you can perform the same operations LINQ allows, in Lisp or any other Turing-
complete language. But can you do it while giving the programmer the same (or
a similar enough) experience? From the .NET docs:

> Language-integrated query allows query expressions to benefit from the rich
> metadata, compile-time syntax checking, static typing and IntelliSense that
> was previously available only to imperative code. Language-integrated query
> also allows a single general purpose declarative query facility to be
> applied to all in-memory information, not just information from external
> sources.

Now, to be fair, I'm not sure you can express all that in Haskell either. And
it could be reasonable to argue that a few of those benefits (e.g.
intellisense) just _haven't_ been done for Lisp, not necessarily that they
_can't_ be done.

------
aidenn0
So I put his example into a lisp file and compiled it under SLIME/sbcl, which
is how I typically develop:

    
    
        1 compiler notes:
    
        scratch.lisp:10:7:                                                                                                                  
          warning:                                                                                                                           
            The function (LAMBDA (X) :IN ADD-TEXT-PADDING) called by MAP-INTO returns NULL but CHARACTER is expected                           
            See also:                                                                                                                          
              SBCL Manual, Handling of Types [:node]                                                                                             
            --> TRULY-THE SB-KERNEL:%MAP                                                                                                       
            ==>                                                                                                                                
              (MAP-INTO (MAKE-SEQUENCE SB-C::RESULT-TYPE (MIN (LENGTH #:G51))) SB-C::FUN                                                         
                        #:G51)                                                                                                                             
                                                                                                                                             
    
        Compilation failed.
    

It's also patently false that quicklisp has no tests; it has no unit tests,
but it is tested as a whole thoroughly, both the code _and_ the set of systems
it provides.

~~~
jose_zap
FWIW the first comment in that blog post shows that at the time of writing the
post, the compiler could not detect that error.

~~~
taeric
My suspicion is the author just didn't know how to use sbcl for this. I
quickly tried this and did not receive any such message.

------
daly
There is no magic in Haskell's strong typing. Axiom, a computer algebra system
with extremely strong typing, is written in common lisp. Axiom's language is
essentially a domain specific language on top of lisp.

The key difference is "impedence matching". An impedence mismatch occurs when
you hook a soda straw to a firehose. There is a lot of friction and lossage.
In programming terms, there is an impedence mismatch between your problem and
the computer. You use a programming language to bring them together.

In assembler, you have to move your problem all the way to the machine. In
Haskell, you have to move your problem all the way to the type system. In lisp
you can write at the machine level (e.g. (car x) which is just a pointer
fetch) or your can write at the problem leve (e.g. (integrate (sin x)))... or
you can do both in the same statement (car (integrate (sin x))).

In other words, lisp allows you to craft a solution that is close to the
machine and close to the problem at the same time, in the same language.

~~~
platz
You have the choice to move as much or as little to the type system in Haskell
as you deem beneficial. You do not have to "type it to the max", as is
commonly offered as a stereotype.

------
rusabd
I have worked on fairly large Common Lisp project (around 500k lines of
actively managed code). That was the most pleasant experience in my
professional life. The refactoring was easy, introduction of new features was
simple and clear. I attribute it partially to language itself and partially to
the team culture. We had a lot of tests BTW. After this I had quite bad
experience with a Clojure project in a different company, so I am not dogmatic
about lisp supremacy anymore. Still, Common Lisp definitely can be extremely
successful in production given proper environment.

~~~
sahil-kang
I’ve been programming lisp for a few years now (Scheme then CL) and write
Clojure daily for my current job. I initially described Clojure as: the worst
dialect of lisp I’ve used, but the only one I’ve been paid to write in. It’s
grown a lot on me over the past few months, but I still end up missing CL
every now and then at work (I think I mainly like CL’s Slime more than
Clojure’s Cider).

What were some unsettling points about the Clojure project you worked on? One
thing I’ve noticed is that since Clojure is a relatively new language, a lot
of newer lispers will start off with it, and I imagine that companies are more
likely to try Clojure than CL as a first lisp. Consequently, the Clojure code
you run into on the job may be of lower quality.

~~~
rusabd
Laziness is quite dangerous given existence of side effects in Clojure - this
would the most common error for new developers. I missed LOOP even it has a
bad reputation - nevertheless it covers almost all useful cases in practice.
We were misusing AOT compilation, the behavior of that code was surprising
sometimes.

But the main failure in my opinion was absence of good development patterns
which leverage strengths of the language and mitigate weaknesses. Dynamic
languages such as lisp absolutely need REPL as primary development mode,
conventions has to be strictly enforced and consistency is much more important
than in languages with rich static typing. Clojure can be and is successful in
many projects. But if your team is trying write Java with parenthesis
everything is hard.

------
waterhouse
For what it's worth, on a fairly recent SBCL, here's what the
compiler/interpreter prints when you enter the author's function:

    
    
      ~> sbcl
      This is SBCL 1.4.7, an implementation of ANSI Common Lisp.
      More information about SBCL is available at <http://www.sbcl.org/>.
      
      SBCL is free software, provided as is, with absolutely no warranty.
      It is mostly in the public domain; some portions are provided under
      BSD-style licenses.  See the CREDITS and COPYING files in the
      distribution for more information.
      * (defun add-text-padding (str &key padding newline)
        "Add padding to text STR. Every line except for the first one, will be
      prefixed with PADDING spaces. If NEWLINE is non-NIL, newline character will
      be prepended to the text making it start on the next line with padding
      applied to every single line."
        (let ((str (if newline
                       (concatenate 'string (string #\Newline) str)
                       str)))
          (with-output-to-string (s)
            (map 'string
                 (lambda (x)
                   (princ x s)
                   (when (char= x #\Newline)
                     (dotimes (i padding)
                       (princ #\Space s))))
                 str))))
      ; in: DEFUN ADD-TEXT-PADDING
      ;     (MAP 'STRING
      ;          (LAMBDA (X)
      ;            (PRINC X S)
      ;            (WHEN (CHAR= X #\Newline) (DOTIMES (I PADDING) (PRINC #\  S))))
      ;          STR)
      ; --> TRULY-THE SB-KERNEL:%MAP
      ; ==>
      ;   (MAP-INTO (MAKE-SEQUENCE SB-C::RESULT-TYPE (MIN (LENGTH #:G51))) SB-C::FUN
      ;             #:G51)
      ;
      ; caught WARNING:
      ;   The function (LAMBDA (X) :IN ADD-TEXT-PADDING) called by MAP-INTO returns NULL but CHARACTER is expected
      ;   See also:
      ;     The SBCL Manual, Node "Handling of Types"
      ;
      ; compilation unit finished
      ;   caught 1 WARNING condition
      
      ADD-TEXT-PADDING
      * 
    

Thus, it identifies the exact problem the author describes at the end.

~~~
fiddlerwoaroof
Yeah, just because the language doesn’t force you to specify types, doesn’t
mean that the compiler can’t infer them for you.

What was most eye-opening for me about CL was just how helpful the compiler of
a dynamically typed programming language can be.

~~~
fjsolwmv
Checking dynamic types us not the work of a compiler. Checking types at
compile time is static type checking. If a type is wholly dynamic (determined
at runtime via something like casting to a type specified in user input), the
compiler can't check it.

(Also, type inference is orthogonal to static vs dynamic typing.)

~~~
phoe-krk
> Checking dynamic types us not the work of a compiler. Checking types at
> compile time is static type checking.

By your logic, Common Lisp is both dynamically and statically typed, because
compile-time type checks are done by the compiler and warnings/errors are
signaled whenever proper.

More, if you declare (OPTIMIZE SPEED), the compiler is going to print a list
of all places where it was unable to infer types on its own and where it
expects help from the programmer in form of type declarations for individual
variables.

------
moomin
Those interested in both should check out Hackett: [https://github.com/lexi-
lambda/hackett](https://github.com/lexi-lambda/hackett)

Whilst not “finished”, what she’s done here is incredible. It’s an
implementation of Haskell semantics as Racket macros.

~~~
BalinKing
Totally agreed... I wish I had her talent ;-) Hackett actually inspired me to
start a project that goes in the opposite direction, i.e. from LISP to Haskell
rather than Haskell to LISP. I'm a PL amateur, so it's been really interesting
to implement a macro system on top of an existing language, without it being
difficult to use or seeming like a hack.

------
nabla9
Common Lisp as hackish vs protective is nice way to describe it.

Another way to describe it exploratory vs implementatory.

In some ways Common Lisp is like Mathematica for programming. It's a language
for a computer architect to develop and explore high level concept. It's not a
accident that early Javascript prototype was done in common lisp or that
metaobject protocols, aspect-oriented programming, etc. were first implemented
and experimented with Common Lisp.

>Dalinian: Lisp. Java. Which one sounds sexier?

>RevAaron: Definitely Lisp. Lisp conjures up images of hippy coders, drugs,
sex, and rock & roll. Late nights at Berkeley, coding in Lisp fueled by LSD.
Java evokes a vision of a stereotypical nerd, with no life or social skills.

------
dragandj
One of the points the author touches is documentation. I just couldn't get why
then he gave advantage to Haskell, since the documentation of Haskell
libraries famously consists of a few definitions of types. So, you know that
there is an abstractly named monadic function, you know it takes a foo and
returns monad foo. What more guidance would you ever need?

But when it comes to CL, he has expectations:

>I’ve opened an issue on GitHub of one quite popular library, asking the
maintainer to write documentation, but after 6 months it’s still not written
(strange, right?).

~~~
forkerenok
I think that was indeed a bit biased.

That said, and not defending the author, I would add, that as an advanced
beginner in Haskell, I find types to be sufficient documentation.

Very often if I look for something in Hoogle, I would be just skimming through
type signatures looking for what I want. If I look for something specific I
would just put the type I want into the search field.

On the contrary, f.x. with Java I tend to either google the operation I want
to do using queries like "How to do blah" or read javadoc in advance to
mentally index what a given library has. And there you really need to read
into the text trying to figure out why a method is overloaded multiple times
with some obscure extra boolean flags.

edit: grammar

~~~
bad_user
I’m a Scala developer, using many libraries inspired by Haskell equivalents.

I have to disagree, types are not sufficient as documentation and this is what
kept me from making the jump to Haskell actually.

But types are better than nothing, or better than outdated documentation. I
can’t imagine anything worse than code written in a dynamic language and
without any documentation.

This is the same discussion as with types vs tests. Types give you certain
proofs for free, freeing you from writing certain types of tests. And they
make property based testing easier. But the ideal is to have types + tests,
types + good documentation, etc.

------
mark_l_watson
I enjoyed reading this article, fun read, even though I don’t agree with much
of it.

My problem with Haskell: I use a subset of Haskell and when using
Emacs/Intero/etc. I feel like I am productive and I enjoy myself. But, reading
and understanding/modifying other people’s code is like running through mud.
BTW, I wrote a Haskel book that just uses the small subset of the language
that I use.

I very much enjoy working on my own projects in Haskel and using Emacs and a
repl feels a lot like my 30 years experience using Lisp languages.

I also use Common Lisp, but now mostly just for paid projects, not as often
for side projects. (I wrote a Common Lisp book for Springer Verlag in the
1980s, which is probably why I still get occasional CL consulting work).

------
coldtea
> _Speaking of tests, recently I discovered that Zach Beane AKA Xach, an über-
> level Common Lisp hacker doesn’t usually write tests. FYI, he is the author
> of Quicklisp, that is something like (but not quite) Cabal or Stack.
> Quicklisp is de-facto the only widely used library manager in Common Lisp
> world, and so it’s written in Common Lisp and doesn’t have any tests. It’s a
> wonder for me how it’s not breaking!_

Why is it "a wonder"? We could, and did, write robust code, for decades before
testing and TDD became a thing.

~~~
lvh
But Quicklisp exists today. Are you suggesting testing is unnecessary or
ineffective? If it is effective, why wouldn't Quicklisp use it?

~~~
coldtea
> _But Quicklisp exists today._

So? Tons of the software of the 70s and 80s exists today too. In COBOL form,
it powers some of the more critical banking, government, etc, systems. In C
form, it powers just about everything else.

> _Are you suggesting testing is unnecessary or ineffective?_

Of course it's ineffective. It can only prove the present of errors, not their
absence.

> _If it is effective, why wouldn 't Quicklisp use it?_

Because the need for testing also depends on how often you refactor and change
your code, and how many bugs you're likely to put in it in the first place
(based on the complexity of the domain, your skills, etc). I'd say both of
those things are no issues for Quicklisp.

So, if having no tests works for Quicklist, and the program doesn't have many
bugs people complain about, then that's it.

If a codebase works, is used, and doesn't seem to have bugs people complain
about, we should also consult with reality when deciding if it's worth our
time to write tests for it, not just some a priori ideology that they're
necessary.

------
hellofunk
The older I get, the more I realize that the ultimate factor in programming
productivity is how readable the language is, or how readable the typical
idioms and usage of that language are. There are a lot of really awesome
languages, but they are tremendously difficult to read your own code, let
alone someone else’s. You can find languages that are very expressive while
you’re writing the code, but that code will be read much more often than the
time you spent writing it, both by yourself and likely by other people.

For this reason alone, I can really see why python has been so enormously
runaway successful.

------
nemo1618
The distinction between "hacking" and "protective" languages is a good one. If
you want to be balanced as a programmer, you need to deeply understand at
least one language in both categories. Protective languages are wonderful for
large-scale projects (true "software engineering"), but they tend to lack the
whimsy that drew so many of us to programming in the first place.

~~~
undecidabot
A similar distinction was made by Yegge before [1], which he (unfortunately)
labeled as "liberal" and "conservative". It was quite controversial [2].

Another similar idea is Fowler's software development attitude [3]. He uses
the terms "enabling" and "directing" instead.

[1]
[https://plus.google.com/110981030061712822816/posts/KaSKeg4v...](https://plus.google.com/110981030061712822816/posts/KaSKeg4vQtz)

[2]
[https://news.ycombinator.com/item?id=4365255](https://news.ycombinator.com/item?id=4365255)

[3]
[https://www.martinfowler.com/bliki/SoftwareDevelopmentAttitu...](https://www.martinfowler.com/bliki/SoftwareDevelopmentAttitude.html)

~~~
platz
It's hard to describe, but the discourse on hn in 2012 just felt _different_
in some weird way.

~~~
spraak
The tone of the posts seems about the same as now to me, but the top level
comments do seem to be longer and with more analysis than most top level
comments lately.

------
didibus
I'm not sure I understand his point. He wrote the function, and in under a
second, he was able to easily run it to test it and got a type error.

The only difference in a static world would be the error would have showed up
a few seconds earlier, before he needed to run it in the repl.

I mean, the error was definitly vague from CL, but is that really because it
lacks static typing that the error is vague?

~~~
atombender
But he did get a _runtime_ failure. Imagine this wasn't about adding a
function, but changing an existing function that is being called from 23
different locations in a 100kloc codebase, and that it contains some tricky
condition logic so that a bug might only be triggered in 5% of the calls.
Without exhaustive unit tests, a type error potentially would not show up
until all those call sites had been executed with the specific arguments
required to trigger the bug. His point was that in a statically typed
language, this would all be detected at compile time.

~~~
bjoli
This error would be detected at compile time without it (as another commenter
shows), and if you are not using the more dynamic features of lisp you really
should declare the types.

------
symtos
> Quicklisp is de-facto the only widely used library manager in Common Lisp
> world, and so it’s written in Common Lisp and doesn’t have any tests. It’s a
> wonder for me how it’s not breaking!

Quicklisp also downloads and executes code over plain HTTP with no integrity
checks whatsoever.

~~~
sigjuice
Yes, that is the default. But you could connect through an https proxy or
check PGP signatures (see [http://blog.quicklisp.org/2017/09/something-to-try-
out-quick...](http://blog.quicklisp.org/2017/09/something-to-try-out-
quicklisp-with.html)).

------
kazinator
Log of my last 3 minutes of activity:

    
    
      This is the TXR Lisp interactive listener of TXR 197.
      Quit with :quit or Ctrl-D on empty line. Ctrl-X ? for cheatsheet.
      1> (defun add-text-padding (str nspaces : newline-p)
           (let ((padding `\n@(mkstring nspaces)`))
             (when newline-p
               (set str `\n@str`))
             (regsub #/\n/ padding str)))
      add-text-padding
      2> (add-text-padding "hello\nworld")
      ** (expr-2:1) add-text-padding: too few arguments
      3> (add-text-padding "hello\nworld" 3)
      "hello\n   world"
      4> (add-text-padding "hello\nworld" 3 t)
      "\n   hello\n   world"
    

Note how (mkstring n) makes a string of n spaces by default; if you want a
different character, specify it as a second argument.

I've never heard of trivial-update, but TXR has something like it built-in
that I independently invented called _upd_ :

    
    
      1> (defvar a 0)
      a
      2> (upd a (+ 2) (* 3))
      6
      3> a
      6
    

_upd_ implicitly uses the syntax of the partial application operator _op_ to
create a pipeline of partial applications. To get such a pipeline by itself as
a function, _opip_ can be used; _upd_ is simply the result of wanting a place-
mutating operator that takes a place's value through an _opip_ pipe and then
writes the result back in.

TXR Lisp also has a well-featured command line option parsing library built-
in:

[http://nongnu.org/txr/txr-manpage.html#N-02C70C38](http://nongnu.org/txr/txr-
manpage.html#N-02C70C38)

------
daly
One other key difference between lisp and haskell... Common Lisp has a
standard definition. My code from the last century still compiles and runs as
does code from all of the books.

On the other hand, haskell seems to be a struggle. I recently downloaded the
book "Write yourself a scheme in 48 hours" written in 2007. Page 18 tries to
"import Monad", which fails. I tried surfing for that string and only see
"Control.Monad". I tried installing the lastest Haskell and the workbench.
Still no luck. So, 18 pages into a book written 11 years ago and the trivial
examples don't work.

~~~
the_why_of_y
As of today, there are 2 versions of the Haskell specification; "Monad" is
Haskell 98, but in Haskell 2010 it's "Control.Monad".

If you want to get legacy code to run with a current version of GHC, try "ghc
-XHaskell98".

~~~
daly
That also fails.

~~~
nyberg
Certainly it does but given that Control.Monad is sufficient I do not see it
as a larger issue. Haskell 2010 is a different language with libraries
refactored. If you wish to use an older text (pre Haskell 2010) then it would
probably be best to try either Hugs98 or one of the other haskell
implementations as GHC is concerned with the current standard.

~~~
daly
The problem is the phrase "the current standard". Is Haskell going to become
the new C++? C++11, C++14, C++17, etc? Is it going to live up to the phrase "I
love standards. There are so many to choose from."? Is it going to become a
python 2.7 vs python 3 vs python 4 game?

I understand the benefit of using the same name as you can trade on the prior
mindset. But if old code won't compile then it isn't the same language.

If you're going to change a language with a standard (e.g. Haskell98), change
the name. Call it Peyton or something. Otherwise there will be a Haskell20
that is refactored for dependent types that won't compile under Haskell10.
Then when people claim to "know haskell" the question becomes ... which
language?

The "current standard" game leads into "library hell".

~~~
the_why_of_y
I think in theory you are correct: as soon as you have 2 different "versions"
of a language, you can, with sufficient creativity, construct a program that
will break with the newer version.

How likely this is going to bite you in practice, though, is a different
question, and one where the specifics of the language are important.

C++ is a language without a module system; instead, the preprocessor resolves
#include directives by textual substitution, which is of course extremely
fragile wrt. compatibility, particularly considering idiomatic "header-only
libraries" of the boost variety.

Haskell doesn't use #include but has a module system, which avoids a lot of
the C++ standard incompatibility issues, because you can compile each module
independently with its required language standard.

Also note that there is a pragma you can put into modules to specify the
version directly inline, e.g., "{-# LANGUAGE Haskell2010 #-}" or "{-# LANGUAGE
Haskell98 #-}" .

------
_ph_
I have to say, this is a very bad blog post. There are things to critisize in
Common Lisp. And of course there is a valid discussion about static vs.
dynamic typing. But the author gets a lot wrong and that kills the validity of
his criticism. What is left, is a very negative article, which might
discourage people to check out Common Lisp, which would be a pity. Even if you
do not end out as a permanent Lisp programmer, learning Lisp can teach you a
lot about programming.

Before I comment about some aspects of the blog, about my background: I am a
professional Lisp programmer, in the recent years I used Common Lisp less
(working more with Scheme-style Lisps as they are provided by my work
environment), but I have implemented (and sometimes still have to maintain)
reasonably sized productive applications in Common Lisp.

The blog starts with the remark that coming back to his code after months took
an hour - I consider this quite a reasonable time. You might be lucky and look
at a function which can be modified without the consideration for its
environment, but often you have to spend quite some time before you can do
changes - this has very little to do with the language.

Then the example he basis his blog post on - I think it has several issues,
some already pointed out by other posters. Padding should be a required
unsigned integer, not a keyword param - if you omit it (it then becomes NIL),
the function will error. While current SBCL compilers even warn about the map
function is called, older ones don't give a good warning and not a great error
message at run time. But the main issue here is: map is the wrong function to
use in this context. Map, as he used it, builds up a string from the return
results of the functions it calls in the iteration, but this string is not
used by the algorithm, because it writes to the string output stream s
instead. Directly looping over the characters in s with "loop" would have been
the better way to do it here. Interestingly, I don't think I have ever used
map in my Lisp programming career so far.

The rest of the post focusses on two things, that Common Lisp doesn't have
enough libraries, and static vs. dynamic typing. Funny though, that he praises
Python, where the availability of libraries certainly is great, without
acknowledging, that Python is way more dynamically typed than Common Lisp. The
side comment "Macros are missing, but you can live without macros after all."
is hand-wavingly dismissing one of the strongest features of Common Lisp. They
should be used with care, but macros are what puts Common Lisp ahead of most
other languages - you can, inside your project, make careful adjustments and
extensions to the language itself.

So, yes, the amount of libraries has some point, but attacking the one guy,
who did most to give all Common Lispers easy access to lots of libraries, for
"not writing tests" is not strengthening the argument. And while the Common
Lisp community indeed could use more active contributers and more libraries,
blog posts like this rather deter people. It would have been more productive
to call for contributers. And from my own practical experience: yes libraries
are very valueable and sometimes essential to start a project. But once you
become a maintainer of production software, they can be also quite a
liability, as you depend on a piece of software you don't maintain.

This leaves the critique of the lack of static typing in Common Lisp. First of
all, yes, Common Lisp is not a statically typed language. If that is a
blocker, use a static typed language, but then don't praise Python. There are
many reasons which speak for static typing - and that is also a reason I have
added Go to the programming languages I use. A proper static type checker can
be quite a help developing. Interestingly in this context, Go uses a very
limited type-inferencing, so that usually the type declaration of the function
parameters is enough so that you don't have to explicitly type local
variables. Which brings us back to what Common Lisp offers, especially SBCL:
optional static typing. You can declare the type of any function parameter and
the return results of functions. You can declare the type of any local
variable. Depending on your "optimize" settings (speed/safety), SBCL will
insert the necessary type checks and use the type information in its type
inferencing engine, and create type errors, wherever it can detect them. With
fully-typed code, SBCL can generate code which matches and occasionally even
exceeds the output of gcc. So, Common Lisp has a lot to offer, which the
author had not tapped into.

~~~
YouAreGreat
> what Common Lisp offers, especially SBCL: optional static typing

Common Lisp offers optionally unsound static typing.

(SBCL doesn't change that, that's why it inserts runtime checks.)

~~~
_ph_
I am not sure what you mean by "unsound static typing", care to explain?

SBCL does offer optional static typing as it goes beyond Common Lisp and can
perform static type checking across compilation units.

If you have a lisp file like:

    
    
      (defun foo (x) (declare (fixnum x)) (* x 2))
      (defun bar () (foo 3.5))
    

Where bar has a static type error by calling foo with a float value, compiling
the file with SBCL will give you the expected static type warning:

    
    
      ; file: foo.lisp
      ; in: DEFUN BAR
      ;     (FOO 3.5)
      ;
      ; note: deleting unreachable code
      ;
      ; caught WARNING:
      ;   Asserted type FIXNUM conflicts with derived type
      ;   (VALUES (SINGLE-FLOAT 3.5 3.5) &OPTIONAL).
      ;   See also:
      ;     The SBCL Manual, Node "Handling of Types"

~~~
YouAreGreat
A sound type system would have guaranteed that no program execution ever calls
your function _foo_ with the wrong type of argument. SBCL doesn't offer any
such guarantee.

If some part of your program extracts values from a heterogeneous list and
calls _foo_ on one of them, SBCL will not be able to statically guarantee that
it is a FIXNUM at runtime. It can't reject the program either, that would
require seriously subsetting the language.

~~~
_ph_
SBCL will issue a compile warning, that it cannot guarantee that you call the
functions only with FIXNUMS.

~~~
YouAreGreat
Whoa. Will SBCL emit false-positive warnings all the time when you dare to use
lists in a Lisp, or does it at least try _hard_ to infer specialized list
types, going above what's expressible using the standard LIST type?

~~~
_ph_
No it doesn't emit false positive warnings when using lists. That is FUD. But
if a function explicitly requires fixnums, and you pass something of type t,
it might warn (depending on the optimize settings).

~~~
YouAreGreat
You didn't answer the question if SBCL infers specialized list types. I'll
assume the answer is "no".

In that case, _everything_ extracted from a list is _T_ and you'd have to get
that warning you mentioned above.

Such warnings would _very likely_ be false-positive, unless Common Lisp
programs are buggy as hell most of the time—assuming _that_ would be the real
FUD, no? Therefore, the non-FUD conclusion is to expect a high rate of false-
positive warnings in typical list-using programs.

TBH, I suspect those false-positive warnings you mentioned aren't bona fide
warnings, but some kind optimizer stream-of-consciousness log stream.

~~~
_ph_
List elements are of type t, and of course acceptable to pass to all functions
which take t as an argument, so no false-positive warnings.

~~~
YouAreGreat
Are you saying that the happy path of SBCL static typing—given you _also_ want
to use lists, and _also_ not be inundated with spurious warnings—is to declare
everything to be of type T?

~~~
_ph_
No, I am not. Perhaps you read up a bit about Common Lisp and SBCL before we
continue the discussion.

------
willio58
I was introduced to Lisp in my CS320 Programming Languages class and fell in
love.

The sad thing is I've never really used it in any "productive" way. Never for
a freelance gig, and absolutely never in my job as a web developer. That being
said, I have learned to appreciate languages that I don't necessarily use to
directly make money.

I've learned a lot of programming concepts from Lisp that will be with me
forever. Thinking about problems in a functional way has been beneficial to a
lot of my work.

~~~
pmarreck
You've probably heard of [http://lfe.io/](http://lfe.io/), I imagine

------
avodonosov
> The fact is difficult to argue with, nil is definitely not a character. But
> why the heck do I get this? Can you tell?

Look at the stack trace first, what funtion signals that.

------
dumb22233
I think that haskell is fine when your data structures are simple, for example
tuples of length < 10\. When you need more general structures, like lists of
lists of element of any type, then Lisp is a better fit. For example, I don't
know any computer algebra system (cas) designed in Haskell, but have the free
CAS Maxima programmed in Lisp.

~~~
Peaker
When do you have lists of lists of elements of any type?

What can you even do given such a value?

~~~
dumb22233
You could use XML data structures in which some of the elements are programs
that you can execute (program is data), other are graphic, other are links to
more XML data and so on. And your program is able to process that information.

~~~
Peaker
Then what you have is not "anything". You have an expression problem:

Either each element has some "handler" that does the right thing for that data
type.

Or you have a set of cases that each data can be, and you handle them all.

Neither is just "any type". And static types are very suitable to describe
either.

~~~
hderms
The handler being a typeclass in Haskell vs the enumerated cases being pattern
matching?

~~~
Peaker
A type-class or just a callback type, vs pattern-matching, yeah.

------
ghosthamlet
Are there anyone can compare Haskell vs Ocaml vs Sml vs Scala vs Nim vs Shen
vs Rust?

~~~
3rdAccount
All pretty different languages.

Haskell and OCaml are both functional programming languages (well OCaml has OO
support as well) that have a REPL, interpreter, and native code compiler. They
have decently sized communities considering that they are mainly academic
languages although Haskell is starting to see industry use at some big name
companies like Facebook. OCaml is maintained by the French national lab INRIA
and is extremely popular at the fintech firm Jane Street. OCaml inspired much
of the syntax of F# (.NET functional programming language). Haskell takes
functional programming to the extreme and has a lot of academic jargon
(Monads) that can turn off beginners. Scala is like OCaml in that it has
support for both OO & FP, but it runs on the JVM and you can use Java
libraries. I think both Haskell and Scala have excellent concurrency support.
I forget which one uses STM. OCaml has been supposed to be getting multi-core
functionality for a long time I think. These three languages are all pretty
standard business languages that you could build a business out of. The fact
that Scala can use the JVM is super super nice as a lot of Haskell and OCaml
libraries are not something I would trust in production. FPComplete is a
consultancy for helping firms use Haskell commercially.

Nim is a language with Python like syntax that transpiles (or compiles) its
source code to C or JavaScript and then uses those compilers to get really
fast code and an executable to distribute. It is still pre 1.0, but it has the
option to use or not use garbage collection. A pretty cool language. It can be
used for fairly high level projects where Python might normally be used, all
the way to OS and video game work with some effort. Rust is mainly meant to
replace C++ (closer to D than Nim, but they do overlap some). Rust uses the
LLVM compiler and is being used at Mozilla. It currently has had a lot of hype
for awhile. It is supposed to be a really safe language. The syntax is a mix
of imperative, OO, and FP in my opinion. It is quite fast. Lastly Shen is a
research language from Mark Tarver (wrote the bipolar lisp programmer essay).
Last time I looked it focused on theorem proving and ran on top of another
lisp (can't remember, but maybe Racket or Common Lisp). It used to have a
pretty odd license, but I think it's been fixed for awhile. It has built in
support for prolog functionality like some other lisps which is cool. He also
has taken time to write some libraries which let you use Tk for GUI, which is
also neat. I wish I had time to check it out.

Lisps are kind of like the ultimate dynamic languages, while Haskell is like
the ultimate static language. Lisp has more flexibility, but the Haskell
compiler will catch a lot of bugs.

~~~
ghosthamlet
Thanks for your detailed explain. I forgot the wonderful D language.

Can you give Some compare on their type system detail? Thanks.

~~~
pjmlp
D is more like an improved C++ with GC.

The type system is quite similar to C++'s one, just that the language has
better support for meta-programming, modules, packages and explicit notion of
unsafe code (system in D speak).

Code generation at compile time is done via compile time code execution,
templates or plain replacement.

It has a GC, but like all GC enabled systems programming languages, has
mechanisms to control its execution, forbid its use in performance critical
sections (@nogc attribute) or just manually allocate memory via stack, globals
or plain OS calls.

------
ghosthamlet
What about the Static Lisp : Shen language?

------
panic
_> Lisp and Haskell are arguably the most peculiar languages out there, at
least they are from my experience._

Clearly the author has yet to encounter HOtMEfSPRIbNG!

------
king_nothing
Greedily evaluating both Haskell and OCaml for systems development work rn.
Haskell seems a lot cleaner, perhaps too purely clean, and OCaml seems to have
some rough edges where it can’t/doesn’t infer types without awkward syntax.
Lazy haskell has STM, monads, parallelism, concurrency and a huge community.
Greedy OCaml can do impurity and pseudo-procedural code easier but lacks much
of what Haskell has. Thoughts?

~~~
platz
What does "systems development" work quite entail?

