

X server in 5000 lines of Common Lisp - luu
https://github.com/pyb/zen

======
ritchiea
Cool though it's going to be egg on OP's face when someone else implements
this in 12 lines of Javascript

~~~
kursiv
and subsequently in 0 lines of js.

~~~
girvo
The OP was already in 0 lines of js.

~~~
frou_dh
AKA no.js

My hot tip for the future of programming.

------
apetresc
One of the prerequisites for using this is, apparently, an already-running X
server.

~~~
bane
So by implementing none of the hard bits, LISP wins again!

~~~
dman
Lisp featured support for Garbage collection, exceptions done right, macros,
optional typing, native compilation. In fact I would argue that many in the
lisp community were so obsessed with the hard bits that they overlooked
building simple things that see everyday use in the real world.

~~~
lispm
Quite some bits were implemented in Lisp. Both simple and hard. Some of the
bits are lost.

For example the TI Explorer Lisp Machine came with an X11 server written in
Lisp. On my Symbolics Lisp Machine I used the usual MIT X11 server written in
C - this was possible because the Symbolics Lisp machine had a C compiler.

~~~
gcr
I was under the impression that those machines evaluate Lisp forms at the
hardware. How could a C compiler work? Does it translate your code into Lisp
s-expressions, or is there like a bytecode layer in between?

~~~
xenophonf
What do you think a compiler does? The target instruction set can be any
Turing-complete language. Compiling C to LISP is no different than compiling
LISP to C (e.g., GNU Common Lisp), compiling ML to Java (which is so easy
students can do it in 300/400-level CS), and so on.

~~~
stormcrowsx
You make it sound of easy. How hard is it to go from a GC language to a non-GC
language? What methods do you use to fill in GC?

~~~
SimHacker
How would a language's support for garbage collection make it any harder to
compile a non-garbage-collected language into it? Why would you think that?

~~~
stormcrowsx
There's no code to hint at how garbage collection should be done, since its
automatic. You have to essentially include a library that would emulate the
source language gc that it came from so its not a direct translation from one
language to another. At least that's what I was thinking

------
pyb
I actually left this project alone after I got the very basics running... It's
very very slow and rather buggy. I guess a profiling run could help a lot
though.

------
10098
Wait, you need an X server to run it? What's the point then?

~~~
_delirium
It looks like it just uses an existing X server as a way to create an OpenGL
context, not as an X server per se. Perhaps it would be possible to port it to
some non-X method of producing an OpenGL context, such as EGL.

~~~
OGC
So its a really hard way to run an X server on top of an X server?

------
sytelus
To me 5000 lines of C code is a LOT of code. Linus's first release of Linux
was _only_ 10,000 lines of code. Given much higher expressiveness of Lisp, I
think 5000 lines of List code is not all that to boast about. I can't pull a
reference here but according to one of the studies, the productivity of some
of the well known programmers was measured at 10K LOC/year (yes, it's very
loose measure of "productivity"). So writing 5000 lines of _good_ code may
require ~6 months, i.e., not a trivial amount of work. Although I agree it is
cool to have X server in Lisp.

~~~
varjag
5000 lines isn't really a lot for a real-life project in any language.
Besides, a lot of the code here seems to be in the data.

E.g. [https://github.com/pyb/zen/blob/master/data/request-
specs.li...](https://github.com/pyb/zen/blob/master/data/request-specs.lisp)
describes X spec request syntax, accounts alone for nearly 1000 lines, and
it's not really possible to trim down.

------
elwell
Isn't Lisp really hard to understand? Or is it just because I'm unfamiliar
with the syntax?

~~~
S4M
I guess it's you who is just unfamiliar with the syntax. Do you find (+ 2 2)
harder to understand that 2+2?

If the code is well indented, IMHO it's quite understandable. For example,
what do you think of this Lisp code snippet (it's not the best possible code,
just some stuff I have at hand to show without you needing to know the
context):

    
    
        ;;takes two list l1 and l2 of same length, and return (l1[0] l2[0]
        ;;l1[1] l2[1] ...)
        (defun mix (l1 l2)
          (if (null l1) nil
              (append (mix (cdr l1) (cdr l2)) (list (car l1) (car l2)))))
    

EDIT: formatted the code as per gcr suggestion.

~~~
gcr
In HN, use four spaces to indent code blocks, like this:

    
    
        ;;takes two list l1 and l2 of same length, and return (l1[0] l2[0]
        ;;l1[1] l2[1] ...)
        (defun mix (l1 l2)
          (if (null l1)
              nil
              (append (mix (cdr l1)
                           (cdr l2))
                      (list (car l1)
                            (car l2)))))
    

(Also, for the record, you should say (append (list ...) (mix ...)) or else
you'll reverse the result list, which I don't think is your intent)

~~~
e12e
This shows off one of lisp's (INHO) unfortunate anachronisms, namely that your
"other car is first".

CDR and CAR refer to machine instructions on the IBM-704 that had a (by
today's standards) a somewhat quaint handling of "words" in memory, roughly
allowing CAR to refer to a data element at the head of a linked list, and CDR
to refer to the pointer to the rest of the list (or the empty list) (As well
as a CONS instruction to build a word in memory from for parts, including the
two 15 bit values that CAR/CDR refer to)[1].

Lisp then extended this by allowing programmers to combine c(a|d)+r, to get
the equivalent of second, third, first-of-second-sublist etc. I'm not
convinced the succinctness is worth it -- apparently experienced lisp-coders
disagree. The TL;DR is that if you don't know lisp, this code is the same as
the above, and is a bit more intuitive:

    
    
        ;;takes two list l1 and l2 of same length, and return (l1[0] l2[0]
        ;;l1[1] l2[1] ...)
        (defun mix (list1 list2)
          (if (null list1)
              nil
              (append (mix (rest list1)
                           (rest list2))
                      (list (first list1)
                            (first list2)))))
    

Other languages might use head and tail, rather than first and rest. I'll
agree that list1 and list2 might not offer much more readability than l1 and
l2 -- at least not with the comments given.

[1] Most of this is paraphrased from wikipedia, and what I remember from
various times I've tried to get started with lisp, without ever really getting
entirely comfortable with the language.
[http://en.wikipedia.org/wiki/CAR_and_CDR](http://en.wikipedia.org/wiki/CAR_and_CDR)

[edit: I didn't notice the comment about reversing the list, but I think it is
rather obvious that (append rest list(first)) does indeed reverse the
order...? I actually wondered if append had a strange order of parameters
while I "rewrote" the code :-) ]

~~~
lispm
Basically nobody should write such code. As a style guide: use CAR and CDR on
cons data structures. Use FIRST, REST, SECOND, ... on lists.

Actually on day two of Lisp, fifty years ago, these patterns have been
abstracted. Recursion of lists has been replaced by higher-order functions in
many cases.

    
    
        (defun mix (list1 list2)
          (mapcan #'list list1 list2))
    

Above maps the function LIST over the two lists and concatenates the results.

~~~
e12e
Hm, am I right in thinking that numbers are defined to be atoms in lisp. And
also that there are limits to how one can use macros, in that you'd not be
able to a) rename FIRST, as in (FIRST list) to 1 (or '1): (1 list) or ('1
list); and b) that you couldn't modify the syntax to "decompose"/parse a slice
syntax like: (1:2 (list-of-lists)) (here 1:2 would mean for example first
element of second sublist/cons) ?

Now, you could of course write an interpreter for such a language in lisp, but
am I correct in assuming that there's no reasonable way to get a
(standard'ish) lisp to compile an expression like (1:2 ...) to the equivalent
caadr(?)-like expression?

I saw there's a library for clojure that adds "slice notation" \-- but does so
by adding a (slice x y list)-macro which of course is fine -- but it would
seem that (x:y list) would give similar benefits to the cadr/cddr etc --
allowing selected subsets to be highly optimized, for example? (As well as
offering concise syntax)

~~~
lispm
Functions like CADDR are not much used in modern Lisp.

I would not write `slice` as a `macro` or special syntax. I would use the
`SUBSEQ` function, which already exists.

    
    
        CL-USER 7 > (subseq '(a b c d e f) 2 4)
        (C D)
    

The simplest way to introduce a special syntax for it is to use different
parentheses:

    
    
        {1 '(1 2 3 4)}  -> 2
    

But the default Lisp style is this

    
    
        (functionname arg0 ... argn)
    

where `functionname` is a real symbol. It may be longer on the character
level, which may be a problem for some users - not for me.

~~~
e12e
I suppose what I was really wondering, but cleverly hid in formulating my
comment, was "what are read[er] macros?".

That'd be how you could redefine "{" and "}" to work something like this?

At least I came across the following that seems to indicate that:

[http://letoverlambda.com/index.cl/guest/chap4.html](http://letoverlambda.com/index.cl/guest/chap4.html)

[http://jlongster.com/2012/02/18/its-not-about-macros-its-
abo...](http://jlongster.com/2012/02/18/its-not-about-macros-its-about-
read.html)

~~~
lispm
Reader macros allow you to write parser functions for characters. So you could
write a reader macro for { such that it collects all enclosed content until a
} character. The it could transform that content and return the transformed
result. The result is always Lisp data, not a string. How you transform the
enclosed text is up to you.

Common Lisp itself tries to minimize its usage of characters. It is expected
that users or libraries want to make use of characters like {, }, [, ], and
many others.

------
aidenn0
Apparently this is 3 years old.

------
daemonize
Good luck groking that mess.

~~~
koenigdavidmj
Banging on file descriptors looks pretty much the same no matter what language
you do it in. I expect that a Python equivalent would be pretty much the same
length.

