close

#CommonLisp #Lisp

(set-macro-character #\[
  (lambda (stream char)
    (declare (ignore char))
    (read-delimited-list #\] stream t)))

(set-macro-character #\]
  (lambda (stream char)
    (declare (ignore stream char))
    (error "unmatched bracket on input")))
CL-USER> [defun frobnicate [foo bar]
           [list [+ foo bar] [- foo bar]
                 [* foo bar] [/ foo bar]]]
FROBNICATE
CL-USER> [frobnicate 42 20]
(62 22 840 21/10)

#CommonLisp #Lisp #Alexandria

Someone asked a question on #lisp on Freenode: since WHEN-LET has an analogous WHEN-LET*, why IF-LET has no analogous IF-LET*?

Here is my attempt at answering it.


Suppose we have a following code block:

(if-let ((x 42) 
         (y nil)
         (z :foo))
  (list :true x y z)
  (list :false x y z))

All of X, Y, and Z are bound to the respective values: 42, NIL, and :FOO. At least one of the values is false. Therefore, it should be obvious that the result of evaluating this code would be the list (:FALSE 42 NIL :FOO).

Now, let us slightly modify this code:

(if-let* ((x 42) 
          (y nil)
          (z :foo))
  (list :true x y z)
  (list :false x y z))

We no longer use IF-LET but instead we use the hypothetical sequential version, IF-LET*. We evaluate X and Y. By definition, we are supposed to not evaluate the following variables at this moment, since Y evaluated to false, and we instead execute the false branch.

The big question is: what is the value of Z in the false branch?

It is certainly not :FOO because we have not managed to evaluate that binding yet. In fact, that lexical variable should be unbound since there is no logical value for it, but there is no such thing as an unbound lexical variable in Lisp. In particular, this might mean that we encounter any number of logically unbound variables in the false branch. We could work around it by setting the not-yet-bound lexical variables to some predefined value (which would greatly complicate the code and introduce a lot of mess) or we would make the false branch unable to use any of the lexical bindings, which makes the sequential IF-LET* greatly inconsistent with the parallel IF-LET.


In other words: having IF-LET* would be inconsistent. None of the variables are available in the lexical scope of the false branch, and that should be part of IF-LET*'s contract.

  • favorite

    1 Like

    Claes Wallin (韋嘉誠)

  • person chevron_right

    Response to One Year as Common Lisp Developer - The Bad

    Michał "phoe" Herda – Saturday, 9 February - 07:43

In the Reddit comments of this thread, I have made a comment that turned out to be surprisingly long and off-topic - it didn't answer that particular question related to the linked article, it replied to the article as a whole. Hence I've decided to turn it into a whole separate blogpost.


This article has some points that make no sense to me.

Things in CL, even the nicer ones, can come back to bite you big time. And if you get cocky, expect a lot of pain.

That argument is meaningless. Substitute CL with any other programming language and the meaning stays the same.

As mentioned in my previous post, all the Lisp I wrote was using the Franz AllegroCL compiler

Most CL compilers come with default optimization settings that are safe, and if you abuse those, be ready for the pain.

"I used only a single implementation" clashes with "most implementations can screw you up with invalid proclamations".

You can have the most optimized and safe code available, and then you include a new dependency into your project that perhaps does some fancy optimization and then suddenly your app catches fire like no other.

"A new dependency that does some fancy optimization", to me, means that that particular dependency has unsafe code. This should nonetheless be visible in the stacktrace of any errors that happen, and if it doesn't, then the modularity of your code that calls that dependency needs to be fixed.

and if you get too cocky, you’ll pay the price

Just a stylistic remark, but the author could have used this and the related phrases about five times less, and the text would be a little bit less pretentious.


On the other hand, the arguments about using lists everywhere, dirty Lisp images, lack of a Lisp "community", small number of programmers, and people flocking to other languages, sound quite valid for me.

Using lists that aren't alists or plists is very easy for quickly hacking things together, but the lack of structure comes to bite the programmer later.

Dirty Lisp images are an issue and they have given me countless times where the code I've pushed didn't build in the first place. It forced me to learn to frequently reload the image during development, especially before pushing, and to ensure that the code loads from a clean Lisp image.

Lispers are not only scarce, they are fragmented, just like their libraries. There exist many JSON libraries, each of them being used by other programs; the same thing happens with other libraries (UIOP vs CL-FAD vs PATHNAME-UTILS, to name one).

Lots of other languages have taken features from Lisps to the point where they're "Lisps without the S-expressions". Paul Graham's original points are a sign of that: nowadays, only point 8 is unavailable in Rubies/Perls/Pythons, and point 9 is theoretically available, even though it's hard to make use of it during compile-time, since access a particular program's syntax during compilation time is nowhere as trivial as it is in Lisp.

To quote Ron Garret from his pretty great article:

I noticed that all around me people were writing code using C++ and Java and Python and even (gasp!) Perl, and they were actually getting it to work without tearing their hair out. In fact, in a number of cases I saw people whip things up in Perl in a couple of hours that would have taken me days or weeks to do in Lisp.

The conclusion was inescapable: the problem wasn't Perl or C++ or Java, it was me. I just wasn't a very good programmer any more. Lisp's power had made me complacent, and the world had passed me by. Looking back, I actually don't think I was ever a very good programmer. I just happened to have the good fortune to recognize a good thing when I saw it, and used the resulting leverage to build a successful career. But I credit much of my success to the people who designed Common Lisp.

  • person chevron_right

    VALIDATE-SUPERCLASS explained

    Michał "phoe" Herda – Tuesday, 25 December, 2018 - 16:42 edit

#CommonLisp #Lisp #MOP

(This blogpost is also known as "WTF is VALIDATE-SUPERCLASS and why do I have to define methods on it to get a metaclass to work?".)


Let us assume that we have a set of superclasses. In this case, let us define two of them, but the number does not matter: can be zero, can be one, can be five.

Additionally, let us define a new subclass of STANDARD-CLASS, that we will call our metaclass.

(defclass superclass-1 () ())
(defclass superclass-2 () ())
(defclass metaclass (standard-class) ())

Let us attempt to create a subclass of these two superclasses, while also using our newly created metaclass.

(defclass subclass (superclass-1 superclass-2) ()
  (:metaclass metaclass))

This signals an error.

The class #<STANDARD-CLASS SUPERCLASS-1> was specified as a superclass of 
the class #<METACLASS SUBCLASS {100377A0D3}>, but the metaclasses
#<STANDARD-CLASS STANDARD-CLASS> and #<STANDARD-CLASS METACLASS> are
incompatible.

There are four distinct classes mentioned in the error message. First of all, there is one of our superclasses, SUPERCLASS-1, then, there is the class object named SUBCLASS that we are trying to define. Then, there are two more class objects, STANDARD-CLASS and METACLASS.

The magical combination of parentheses that causes the above DEFCLASS form to evaluate successfully is this mysteriously-looking method:

(defmethod validate-superclass
    ((class metaclass) (superclass standard-class))
  t)

Let us analyze why this particular method fixes our problem and allows us to define classes with the metaclass of our choosing.

The MOP description of VALIDATE-SUPERCLASS says that this function is called with two arguments: class, and superclass, to determine whether the class superclass is suitable for use as a superclass of class.

In other words, if Common Lisp Object System is supposed to permit a situation when superclass becomes a superclass of class, this function needs to return true. If such a situation is forbidden, this function must return false.

The MOP says that the default system-defined method on VALIDATE-SUPERCLASS returns true only in three cases:

  1. if the superclass argument is the class named T,
  2. if the class of the class argument is the same as the class of the superclass argument, or
  3. if the class of one of the arguments is STANDARD-CLASS and the class of the other is FUNCALLABLE-STANDARD-CLASS.

The first case is trivial; every class is a subclass of the class T, therefore it would be pointless to return false on

The third case is there to assure that standard objects and funcallable standard objects can interoperate. A subclass of a funcallable class does not have to be funcallable, and the other way around, too: a subclass of a non-funcallable class may be funcallable.

The second case is what allows the basics of CLOS class definitions to work. Whenever we define a new class with DEFCLASS without any superclasses, we define a new instance of STANDARD-CLASS that has STANDARD-OBJECT as its superclass. STANDARD-OBJECT is an instance of STANDARD-CLASS; the newly defined class is an instance of STANDARD-CLASS; therefore, VALIDATE-SUPECLASS returns true. Whenever we define a new class with any number of superclasses that itself are also instances of STANDARD-CLASS, the same thing happens: each of the superclasses is an instance of STANDARD-CLASS, just like the class we are defining right now, which allows VALIDATE-SUPERCLASS to return true in each single case.

The issue happens when we specify the :METACLASS option to DEFCLASS. :METACLASS states that the new class should be an instance of the provided class. If we pass the option (:METACLASS FOO-CLASS), then the newly instantiated class metaobject will be an instance of FOO-CLASS.

But, remember that we also need to specify superclasses for that class! Unless we explicitly specify a superclass, then it is going to default to STANDARD-OBJECT, in which case, VALIDATE-SUPERCLASS will be called on a pair of arguments: the class we are attempting to create, an instance of FOO-CLASS, and the class STANDARD-OBJECT, an instance of STANDARD-CLASS, which is stated to be its superclass.

Neither of the three cases the default method fit this combination, therefore the method returns NIL. The validation fails, and an error is signaled from the DEFCLASS macro.

This is why we explicitly need to state that we, the programmers, want class STANDARD-OBJECT to be a valid superclass for instances of FOO-CLASS. This is the purpose of the VALIDATE-SUPERCLASS method defined on FOO-CLASS as the class we want to instantiate and STANDARD-OBJECT as the superclass we want to assign to the class.

In other words, the way the aforementioned MOP usage works this way is because VALIDATE-SUPERCLASS serves a greater function than mere playing with metaclasses - it is the central MOP mechanism for declaring which combinations of classes and subclasses are allowed in the Lisp image. Methods on that function which may be specialized in any way, including methods that use EQL-specializers and perform arbitrary computation.

For an example of the above, let us state that the class named FOO may be subclassed only by classes whose name contain the letter E.

(defmethod sb-mop:validate-superclass
    ((class class) (superclass (eql (find-class 'foo))))
  (find-if (lambda (x) (member x '(#\E #\e)))
           (symbol-name (class-name class))))

The above snippet denies (defclass bar (foo) ()), but permits (defclass fred (foo) ()).

It is also possible to imagine a class hierarchy where a programmer decides, for any reason, that a class named BAR should not be allowed to be subclassed at all. The following snippet is enough to achieve that:

(defmethod sb-mop:validate-superclass
    ((class class) (superclass (eql (find-class 'bar))))
  nil)

(To add to the confusion, the error message we get from SBCL in such a case is not very helpful, as it states, the metaclasses #1=#<STANDARD-CLASS COMMON-LISP:STANDARD-CLASS> and #1# are incompatible. I have raised a bugticket on that edge case.)


Summing up, VALIDATE-SUPERCLASS is a general mechanism for describing which kinds of superclass-subclass relations we want to see in the Lisp image. Practice has shown that this function is particularly often used in context of permitting instances of non-STANDARD-CLASSes to inherit from instances of STANDARD-CLASSes - in other words, allowing metaclasses to be instantiated. The way it is done is via defining methods on this function, specialized on the metaclass we want to instantiate, and the superclass that we want to assign to the instances of that metaclass - either a particular superclass of our choosing, or STANDARD-OBJECT as the default. These methods must return true whenever we want to permit a particular combination to happen.

  • person chevron_right

    Repost: The year social networks were no longer social

    Michał "phoe" Herda – Monday, 24 December, 2018 - 11:59

I have never had a Facebook or a Twitter account for exactly that reason - I know that they are there because they seek my time and attention.

I have instead turned to the Fediverse and have greatly enjoyed it there. Social contacts, nothing more - no ads, no spam, no attention stealers, no people that I do not want to see on my timeline.


  • reply

    Original post deleted

  • 2 Comments

  • person

    25 December, 2018 cylonapplepie

    knowing what happened in polish part of the internet with portals, instant messengers, im not surprised what happens now to the "biggest" ones. tho, we can argue that the reason is different but outcome is same - peope are flying away looking for better alternatives and they will do the same in the future when current alternatives will become annoying.

  • person

    25 December, 2018 Michał "phoe" Herda

    @cylonapplepie Hey, I'm Polish. Gadu-Gadu and NK.pl used to be popular, and both are now decimated by the omnipresent Facebook.

  • person chevron_right

    Stallman's greatest achievement: being boring to death

    Michał "phoe" Herda – Sunday, 23 December, 2018 - 12:06

(This is a repost of an old blog post of mine from Teknik.)

#free-software #stallman

Today's random thought: Richard M. Stallman's greatest achievement is that he'd become absolutely and utterly boring.

Think about it for a second. He's a man who has been speaking of the same thing for thirty years - that's longer than I have been alive. Look at his conference talks - it's the same old stuff over and over. Look at his mails on the mailing groups - it's all about freedom, again, again and again. Look at the essays he'd published - it's what free software is, what is free software, what free software isn't and what isn't free software. Literally, reading several different works of his allows you to predict the course of his whole remaining literary output. It is absolutely the same repetitive stuff over and over that has been said tens of thousands of times already, in the same tone of voice as he always speaks in.

That obstinance and single-mindedness had shaped the way contemporary people think of free software. He's a persona recognizable for exactly the fact that he says the same thing everywhere, no matter when or where or who he is talking to at the moment. His views stem from a very simple principle of freedoms that software needs to give to its users, and everything else from his beliefs is a consequence of that principle. He is consistent, he doesn't disapprove of things that do not violate these principles and doesn't approve of things that do not obey them.

The fact that he had been faithful to that point of view for thirty five years and counting are a proof of what humans are capable of. Inventing a principle as free as software freedom and standing firmly to such beliefs for decades is a great showcase of the capabilities of a human mind and will, both when it comes to organizing his own life around the principles he invented, and when it comes to predicting countless consequences for people who do not follow the same of them (see /r/StallmanWasRight for a small showcase).

While I consider RMS to be outright silly in some aspects, he is a very inspiring philosopher who actually lives true to his words to the point of it being painful to look at. The thing is, he'd already attained the liberty he's speaking of.

  • favorite

    1 Like

    Timothée Jaussoin

  • 1 Comments

  • 23 December, 2018 Timothée Jaussoin

    I agree with you :) I brought my girlfriend at an event to let her see Stallman one time in her life (still a nice experience to have). But I also told her "You've seen one conference of R.M.S. no need to watch him another time, it will be the same" :p

(This is a repost of an old blog post of mine from Teknik.)

#common-lisp #clozure #ccl #emacs

In Emacs, evaluate:

(put 'if 'common-lisp-indent-function 1)

It will give you this indentation style:

(if foo
  bar
  baz)

(This is a repost of an old blog post of mine from Teknik.)

#common-lisp #usocket #networking

CL-USER> (ql:quickload :usocket)
To load "usocket":
  Load 1 ASDF system:
    usocket
; Loading "usocket"
[package usocket]..........
(:USOCKET)
CL-USER> (use-package :usocket)
T
CL-USER> (defvar *server* (socket-listen "127.0.0.1" 65432))
*SERVER*
CL-USER> (defvar *client-1* (socket-connect "127.0.0.1" 65432))
*CLIENT-1*
CL-USER> (defvar *client-2* (socket-accept *server*))
*CLIENT-2*
CL-USER> (print "hello world!" (socket-stream *client-1*))
"hello world!"
CL-USER> (finish-output (socket-stream *client-1*))
NIL
CL-USER> (read-line (socket-stream *client-2*))
""
NIL
CL-USER> (socket-close *client-1*)
T
CL-USER> (socket-close *client-2*)
T
CL-USER> (socket-close *server*)
T