Hacker News new | past | comments | ask | show | jobs | submit login
Lisphp is a Lisp dialect written in PHP. (github.com)
110 points by jperras on July 5, 2010 | hide | past | favorite | 107 comments

Happy to see PHP be taken more seriously around here. Trust me, we're not all moronic programmers using register globals, magic quotes, and other abominations originally available in PHP.

This is a pretty cool project.

I think PHP suffers from some of the same issues that VB6 suffered from in terms of inexperienced programmers. With VB6, someone who was not as much of a programmer as we all are could write themselves into a massive mess. I have found when writing PHP code if you are careful, you can build things with decent design patterns and consistency. Sure, its not as clean as a lot of languages, but it sure is easier for me to figure out what is going on with PHP code someone else wrote compared to Perl or Ruby code.

This single biggest issue I've found when working with PHP for larger projects is that even if you are a disciplined programmer you get to the point where the includes will eat up your performance and your memory requirements will become larger. See drupal as an example of this.

You can get around some of that by using a php accelerator but it would seem to me that something like that ought to be part of the basic distribution.

That's what autoload is for. It's been around since PHP 5.0. http://php.net/manual/en/language.oop5.autoload.php and allows you to require the necessary files or classes only when you need them. Seems like they solved this problem to me.

I use frameworks (Zend, CodeIgniter) for all PHP projects except protoyping HTML templates (like, when knocking out a quick page with placeholder data and functionality to show a client). The hate poured on PHP for functionality that I have never, ever used (read pretty much any "PHP sucks because..." post and notice that the majority of the complaints are for things that real PHP developers are well aware of and effortlessly deal with) gets tiresome to hear about. Use it following some basic, current, best practices and it is fast, easy and perfectly fine for developing with.

Thank you! I must have missed that bit in the announcements.

Same largely applies to "classic" ASP and VBScript. You can write fairly well architected systems in that environment, but you have to discipline yourself to do it; the language won't really help you.

If you understand PHP and not Perl or Ruby, then I suppose you don't master regular expressions.

I think you're suggesting that PHP has somehow "arrived" because there's a LISP written in PHP. I don't agree.

The LISP here is a neat project but I can't think of a practical niche. Sandboxed hosted scripting is praiseworthy, but most customers would want that in Javascript or PHP itself, not as a Lisp.

PHP already powers three of the top 10 websites. What was left to prove?

> I think you're suggesting that PHP has somehow "arrived" because there's a LISP written in PHP.

Where do you read that ? He's not making any suggestion of the kind, he's simply happy that PHP is not always scoffed on, and this piece of code is a really good example of what you can do when you use PHP properly. That comment could apply to plenty of other PHP projects. HN has a bit of a history of ridiculing PHP and the people who program in it.

> I don't agree.

With who ? Your strawman ?

The LISP here is a neat little stepping stone, just like almost any other 'language X implemented in Y' it is not currently meant as either a high performance solution or a production ready environment, just a neat little hack, easy to understand.

If you're in front of a river and someone builds you a rope bridge across you don't complain about the fact that it won't handle 6 lanes of highway traffic, you're just happy someone took the time and trouble to build it. If you don't want to use the bridge then fine, if you do then it's great.

What's left to prove is that there is a wrongness in the attitude towards certain classes of languages. I think PG is partly to blame for this with his 'blub' classification of programmers and languages. As though those that program in language X are somehow better than those that don't.

To tie LISP and PHP together in this way shows that that argument holds no water at all, and for some that may change their minds, which is good.

I guess PHP parallels C++ in this respect.

I'd argue the situation with PHP was in fact much worse than it ever was with C++, but there are some parallels. The bigger problem with PHP is that there is no consistency and C++ always had some of that.

The naming of library functions alone can drive you bonkers. It's rare that I have to look up the names of functions in languages that I've used for a decade or more, with PHP it still happens with some regularity.

I know I could use an IDE to mitigate that but for some reason IDEs and me don't get along, with the exception of Borlands C Builder, which was really pretty good.

I'm the author of this, I made the web REPL also.


Well, of course, it uses a sandbox environment.

From a quick look, you managed to get one thing right, even though it baffles even respected language implementers: a proper stack trace on error. Most people who write a HLL interpreter in another HLL tend to cheat and implement a half-baked source to source translator.


Question: Why does it have T, NIL, #T and #F? what roles do these symbols play in the language; which ones are canonical truth values, which ones signify list termination, and which ones signify the empty value? This is the perennial Lisp question.]

> Most people who write a HLL interpreter in another HLL tend to cheat and implement a half-baked source to source translator.

Is this a jab at the Lisp tradition of compiled sublanguages?

It's a jab against the general case, below.

A lame, wasteful little script to print out a TODO list ;-)

  (defun map-print (item list)
    (mapcar (lambda (x)
  	    (format t "~a in ~a~%" item x)) list))
  (defun permute (list)
    (mapcar (lambda (x)
  	    (map-print x list)) list))
  (defun languages ()
    (destructuring-bind (response headers body)
        (trivial-http:http-get "http://mahmud.arablug.org/languages.txt")
      (when (= response 200)
        (loop for line = (read-line body nil nil)
  	 while line
  	 collecting line))))
  (permute (languages))

Bad stack traces is certainly one of the major complaints against Clojure.

They seem fine to me.

Do you want to see “bad” stack traces? Try Erlang. There is a language with truly obtuse stack traces.

And the very concept of strack traces does not even make too much sense in e.g. Haskell. (Though it can be made to work, with some cleverness.)

This is analagous to McDonalds serving duck confit (and it actually tasting kind of good).

Minor nitpick, the cdr implementation is (out of need, as far as I can see) terribly slow for larger datasets, it uses array_slice($this->getArrayCopy(), 1) .

Well done jacquesm! It's only been what since you first looked at Lisp? 2 months? Already you know your way around typical primitive implementations.


I've been studying it so I can go and write a little lisp interpreter in C just to make sure I really 'got' it.

For me that's the 'benchmark' of understanding a language, to be able to re-implement it.

edit: by the way, it's been more than two months, but there have been a lot of things done in that time that have nothing to do with programming.

I wrote a few long-winded posts on HN listing Lisp implementation bibliographies. There are a few essential papers that you need to read to get the full picture; Here they are collected in an archive. Nearly all of them assume nothing more than rudimentary familiarity with Lisp, some functional programming (Scheme and tiny bit of ML) and they're geared towards someone with some C, machine-org and assembly knowledge.


I would recommend these to someone who wants to implement a functional or a Lisp language, going with nothing but basic systems programming knowledge.

Recommend prerequisites are the first 2-3 chapters of SICP, some familiarity with classic compiler construction (dragon book would do, skip the lexical analysis and parsing parts though; you wont need much of that for Lisp if you retool and existing lexer. But focus on function calling and stack discipline, lexical scope and variable visibility, and maybe learn how an Algol dialect implements runtime heap-allocation) some exposure to C and assembly, and healthy curiosity.

A suggested order might be: Ghuloum, Wilson, Dybvig, the uncredited "lisp-implementation.pdf" paper, Gudeman, Kelsey, then Shao (Shao might make a whole lot more sense if you read one of Andrew Appel's compiler books and dip a tiny bit into ML.)

For the brave, I can also recommend "Write Yourself a Scheme in 48 Hours" (http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_H...).

I write `brave', because it implements Scheme using Haskell. And if you aren't familiar with either, the learning curve will be steep.

For a challenge: Try a lazy language next.

See http://research.microsoft.com/en-us/um/people/simonpj/papers... for some hints on writing a compiler for those (or just general amusement).

Lisp is enough of a challenge for me at the moment.

For instance, it seems to be like travelling back to the 80's just to have to live with the 'third element in the list containing the data about a person' instead of simply using person->name , I've gotten used to thinking in terms of symbols so much that I find it hard to go back to the kind of trickery that was 'normal' in say microsoft basic in the 80's because it lacked named elements in something akin to a struct.

There is a certain kind of elegance in something so minimal but it also comes with considerable mental overhead and opportunity for mistakes.

third element in the list containing the data about a person

Most people don't do that, except in throwaway code. In CL you can use an alist, a plist, a struct, or CLOS.

I've gotten used to thinking in terms of symbols so much that I find it hard to go back

Given that Lisp's history with symbolic computing goes back to the beginning, something about that doesn't sound right.

Your person->name comment reminds me of something, by the way. Portions of our app run in both CL and JS (via the Parenscript library). This is relatively easy, although there are a few disagreements between the two that you have to watch out for (such as how they handle null). Anyway, there are lots of CL features we've "ported" to JS via macros, but there is also one JS feature I found myself envying so much that I finally wrote a partial implementation of it in CL. That is JS's willingness to allow you to stick any property on any object just by setting it. It's a rather Lispy feature since it's maximally dynamic and maximally flexible. I find it handy for incremental development. When you have some data about a thing that you want to associate with it, but only some of the time, it's a real pain to have to make a separate data structure to hold it and then make sure it gets passed alongside the original thing to all the appropriate places. Our CL implementation of this is pretty crude but it allows the important thing, which is that you can write (@ something :some-data) and (setf (@ something :some-data) "blah") pretty much wherever you want. So we can be said to have Greenspunned a bit of JS in Lisp!

You're still in the pedagogy sandbox; every real Lisp worth using has structures and objects:

  (defstruct person
    name age occupation)  => PERSON

  (make-person :name "Kris Kringle" 
               :age 71 
               :occupation "Santa") => #S(PERSON-1)

  (person-name *) ==> "Kris Kringle"

> You're still in the pedagogy sandbox;

Don't worry, I realize that :)

> very real Lisp worth using has structures and objects:

That's a tricky and somewhat circular definition.

If it is worth using it is probably real and vv, but most lisp tutorials that teach 'pure' lisp do not have structures and / or objects, they rely on lists alone as their major data structure.

That also defines all lisps that do not have these items as somehow not real and there seem to be a lot of lisp people that somehow consider this 'impure'. Or is my reading material that dated ?

If your reading material Lisp doesn't have some form of defstruct then it certainly is dated, or they are avoiding it on purpose. Every non-toy Lisp has it.

Ok, thanks. That helps. I started 'from the ground up', I figured that the best way to study a language is by looking at the way it was in the beginning since that's the most pure form of it. If 'lists' are as great as they are said to be you'd expect that things like structs and classes are optimizations, not essentials.

Don't do that for Lisp. You will be stuck with the dynamic scoping that Lisp used to be plagued with in the beginning.

Go far a modern Lisp like Scheme. (You can ignore the syntax you don't want to deal with, yet.)

Yeah, you can view it as an optimization: you can build defstruct & co on top of lists with macros. However, using ca(d)*r by hand is not advisable in any case...

Usually it goes the other way around: you implement lists on top of defstruct.

Restricting yourself to Pure Lisp is like insisting on only dating virgins.

I have a very snappy comeback to that one but I highly doubt it will pass the censor ;)

How about: "If she wasn't good enough for the other people, she won't be good enough for me."

Instead we all just use associative arrays.

But there is a danger in overusing structs and classes, too. You end up with these hugely complex ontologies for every little program, and frequently when you try to reuse code you end up with conflicts.

I implemented cdr just to respect a Lisp tradition. Lisphp's Lisphp_List type isn't a linked list but a subtype of ArrayObject, which supports random access. So instead of doing:

  (car (cdr (cdr (cdr list))))
you should just do:

  (at list 3)

Yes, that's what I thought, and I don't see a real way around it.

The bigger problem is in code that consumes the lists item by item, every time an extra copy of the remainder of the list will be made, there it is much harder to work around this.

Very neat code by the way, thanks a ton for making this.

I'll be playing around with it for a bit, I've written a chunk of code for a small site a while ago in a 'functional' PHP style (as in, no assignments except at the highest level), I'll see if I can convert that project to your lisp dialect and get it to work.

It could be mitigated in some cases as long as the garbage collector is working fine. For example, in a tail-recursive function that uses a first/rest pattern to go through an array a new copy would normally be allocated with each recurse. If LispHP can do some form of tail recursion elimination, it will theoretically take up constant memory and hopefully the GC takes care of the practical part.

Even if memory usage weren't a problem you still have O(n) time for cdr. This may not be a problem if you implement the higher level functions (map, filter, etc.) efficiently.

Yes, higher order functions (especially foldr (resp. reduce)) are the way to go.

Since Lisphp is a new dialect, you don't need to worry about backward-compatibility. You should get rid of car/cdr entirely. There is no inherent reason for using linked lists for anything - even s-expressions. For example, no one has ever complained about the lack of car/cdr in Parenscript (although to be fair, Parenscript is not homoiconic, just a translator from CL to JS). Having a good backquote-comma templating syntax is more valuable.

You should even get rid of a custom type for Lisphp sequences and use PHP arrays directly. Ditto all other datatypes. Then if you use PHP's calling convention you should be able to call PHP code directly without needing USE and FROM, and more importantly PHP code can do the same to Lisphp with minimal pain. That way writing Lisphp libraries for others to use becomes viable.

I think it's actually quite neat that it tries to stay close to the lisp code that you see in examples and books about lisp.

What's the point of sacrificing something like platform integration for car/cdr? Anyone who is going to want to use Lisphp for real work is not going to have a problem writing their code to use arrays. Anyone who thinks that car/cdr is more valuable than platform integration is not going to use Lisphp for real work. And everyone else will come by and say "Lisphp has those weird car/cdr and it can't even work with PHP libraries without glue code, Lisp is a toy."

Call them what you want. First/car & rest/cdr are the classic tool for writing code that iterates across datasets.

Recursion, map, and fold can be used for iteration. car/cdr is just one way to access elements of a sequence.

Think about what you just said and why it's not a valid rebuttal. I know you know better.

Hint: via recursion, how do you advance along a sequence in a collection-agnostic way?

So now iterators mean car/cdr is "classic" so it should be present in all Lisp dialects from here to eternity?

Again, you can call them "first" and "rest" if you like.

It is the ability to decompose an arbitrary sequential data structure efficiently that is important. It's not important because of any historical bias; it's important because it is incredibly useful.

If you can't support first/rest in an efficient manner, it is a bad sign.

So all those other programming languages that come with arrays and not linked lists are completely useless? Mildly useless? Only kind of useless? Or is it the case that it really doesn't matter?

Most of them DO provide a way to do this. For example, Ruby is array based but provides a way to do this through enum. The lispish way is first/rest. Clojure, a lisp with very novel data structures, does this just fine even with associative arrays and vectors. It introduces a seq abstraction to support it.

It's important when you need to write your own iteration primitives. Map, fold, etc are all well and good, but sometimes they won't do because the iteration may require some unusual steps.

In your particular dialect of lisp, what does the code for writing map or fold look like? The one that doesn't copy memory egregiously, that is. And how bad must the performance of javascript linked lists be to scare you away from them so damn hard, anyways?

Or perhaps, what you're supporting is a "lispish" language that isn't really something we should directly compare to lisp, in the vein of Nu?

I don't have a dialect of Lisp. I work on a translator from a subset of Common Lisp to JavaScript (http://common-lisp.net/project/parenscript/). In particular, this subset does not have car/cdr, or any other list manipulation functions. No one has ever complained about this.

This is map:

(defun map (fn arr) "Call FN on each element in ARR and return the returned values in a new array." ;; In newer versions of ECMAScript, this may call Array.map, too (let ((idx 0) (result (array))) (dolist (el arr) (setf (aref result idx) (fn el)) (setf idx (1+ idx))) (return result)))

This is reduce:

(defun reduce (func list &optional init) ;; the use of init here is actually a bit broken wrt null (let* ((acc)) (do* ((i (if init -1 0) (1+ i)) (acc (if init init (elt list 0)) (func acc (elt list i)))) ((>= i (1- (length list))))) (return acc)))

What would a linked list data structure provide? Incompatibility with all other JavaScript code and a mandatory runtime library?

Thanks for introducing me to Nu btw. What makes you say that it is not a Lisp?

Lisphp's map/filter/reduce functions are implemented in the same manner that Parentscript does.

This is map: http://github.com/dahlia/lisphp/blob/81849070ac7ff13948786b5...

This is filter: http://github.com/dahlia/lisphp/blob/81849070ac7ff13948786b5...

This is fold: http://github.com/dahlia/lisphp/blob/81849070ac7ff13948786b5...

Lisphp respect the iterator protocol of PHP instead of Lisp tradition in the practical reason.

There is no inherent reason for using linked lists for anything

Uh, constant-time insertions?

Did you read the parent post?

I immediately went to see how he implemented lists too with this kind of concern in mind. One way around the list issue is to write/use a C extension that provides a true linked list data type. I checked and there is such a thing in PECL, though who knows about quality.

Of course, because of the whole shared hosting thing you'd have to fall back to a pure PHP implementation if the extension isn't loaded.

FWIW, there's an implementation of a doubly linked list in the SPL.


LispHP seems to use ArrayObject, though I'm not really sure how its performance is (googling indicates iteration is slow).

Is that written in C? Implementing a linked list in PHP isn't difficult, but I'd imagine memory use would be prohibitively expensive. If that is not written as a C extension, and perhaps even if it is, presumably you'd get a big speedup if you implemented a list with common lisp operations in mind as an extension for a project like this. It could fall back to a PHP implementation if the extension is not/can not be loaded.

It's kind of moot because if speed were a huge concern, this might not be used in the first place, but just it would be interesting to see how far such a thing could be taken.

All SPL classes/methods are written in PHP. They're essentially PEAR classes that just come preinstalled.

Incorrect. The SPL is entirely implemented in C.

Huh interesting.


Ah, that's why my memory was fuzzy. I downloaded PHP and as of 5.3, the SPL does come with a double linked list, and a userland and C implementation are both in the tarball. Why is there a PHP version also?

Maybe as a fallback in case the extension isn't loaded ?

An alternative syntax for that is to cast $this as an array:

    array_slice((array)$this, 1);
I'm not sure if this will be faster though. Is the source of the slowness getArrayCopy or array_slice itself?

Is there a way to implement cdr more efficiently? (I don't know PHP.)

In more traditional lists, the main data structure over which car and cdr munched was a linked list. car and cdr were just accessors for the two fields (usually used as a data container and a 'next' pointer).

I mean, is there any way to get that efficiency from PHP data structures? I guess you could implement a linked list data structure from scratch, but that would likely limit inter-operability with the rest of PHP.

cdr could return an array "object" where it references the entire array, and a pointer where it should start indexing. Instead of using linked-lists as the fundamental datatype, it could use array subsets.

If I recall correctly, Clojure does something like this on most of its basic types. Since it wants users to avoid in-place modifications, it provides data structures that have modification functions that return new instances of the data structure with the changes made. These also do not regenerate the whole structure; they only use memory to point to the one modified/added element.

Please correct me if this is false.

Something like this could be very feasible, especially when combined with the array interfaces so that the fact it's an object with a moving pointer isn't even noticed.

I think I need to change the name of my repository now. I am now the author of "phlisp" http://github.com/shaunxcode/phlisp to avoid confusion. hah. It is cool to see a different approach to the same problem! I am going more for source -> source, quasiquote macros, TCO, some arc-esque syntactic sugar etc. and back to php4 compat. Definitely a kick in the ass to release it now as I have stalled since april due to "work".

This is maybe not so useless but definitely awesome!

At least this would solve the whole "which server should I use for lisp" problem...

That was exactly my thought, the lisp deployment problem can be checked off the list. This also significantly lowers the barrier to entry for people that want to try lisp.

He seems to have done (much) more than a half baked job of it judging by the rest of the page.

There is lisp and there is lisp. The libraries and the speed do matter. While this is an interesting project it's not comparable to SBCL, Racket or Clojure.

It's a working Lisp-1 with proper support for lambda conversion/reduction rules. It's a long way from being a usable Lisp, but it's theoretically feasible to take it there.

It says a lot about about his work that I am very excited to hack on it for the rest of the day, and I am not easily impressed by toy Lisps :-)

Tip of the hat to the author for doing the right thing, imo.

And under a very permissive license to boot.

Also it's actual code, as opposed to most 'hacker news 'news''.

"The libraries and the speed do matter."

Does PHP lack for good libraries? That was the trick Clojure used with Java to get bootstrapped.

Hmm... maybe in conjunction with HipHop for PHP? http://github.com/facebook/hiphop-php

I guess if you were allowed to run compiled binaries you could just as easily use another Lisp, though -- except maybe if you needed to integrate with existing PHP applications. That might be a good use case.

> Does PHP lack for libraries?


> Does PHP lack for good libraries?

Yes. PHP's libraries are one of the ugliest and non lispy in existence :(

Since they were not made with 'lisp' or anything 'lispy' in mind that should come as no surprise.

PHP is about as declarative as you get, assignments are the norm, not the exception and side-effects are how just about everything is done. It's not exactly elegant, but it fills a niche and apparently fills it quite well.

Though python is catching up, and even if it isn't quite as easy to deploy a large scale python web application as it is to deploy PHP the difference is getting smaller all the time.

I'm curious how fast it actually is for 'real life' examples.

The fact that it's interpreted makes it a little unrealistic to directly deploy a whole web application with it. However, the sandboxing and embedding features look really cool.

It's kind of funny that people see PHP related to anything other than serving websites and they cringe.

As of 5.3 (and I won't say prior to that because even I don't believe it), PHP has become a nice utility language for just more than serving out wordpress.

Closures and the reference sweep GC level the playing field.

I just finished reading "Javascript the Good Parts" recently. It's probably also the case for PHP that if you pretend that certain parts of the language just don't exist, you end up with a pretty good language. (I have never programmed anything in PHP.)

Building a Lisp on top of PHP strikes me as a vehicle for helping developers to adhere to the "good parts." I'm not sure whether that was the intent here, but it could turn out to be a useful side effect.

A note on the language itself:

It appears that you can choose any set of matching braces that you want anywhere in your program. I think this is the convention Scheme uses. Common Lisp reserves braces other than () for reader macros.

I personally, however, favor Clojure's approach of defining distinct behavior for each brace type. There are only three pairs of matched characters on the keyboard. It seems to me that this makes them too valuable to not leverage them as a core part of the language. Immediately knowing the meaning of a specific brace type makes it much easier to read someone else's code.

Yeah, this code actually really illustrates the difference between writing crappy code in PHP and writing something elegantly using everything the has come in in the past few versions.

I have seen quiet a few PHP bashing comments recently that while true of older versions are much less true of 5.3, I mean there is still the same old nitpicks about the parameter orders and the amount of built in functions in the standard namespace but it is defiantly heading in the right direction.

What features specifically have made the difference? I've tried a few times to buckle down and learn PHP and I never seem to get farther than the mixed value/reference semantics, which I remember highly unfondly from my perl days.

PHP has made me more $ than all the other 'good' languages that I've ever worked with combined. And that was before 5.3.

An embedded lisp, even a slow and imperfect one is a nice stepping stone to have though.

It takes away two thirds of the trouble you have to go through when starting with a new language, environment and libraries.

Has anyone tried running code with this yet? This results in errors:

  $env = Lisphp_Environment::full();
  $program = new Lisphp_Program("(echo 1)");

Try this:

  $env['echo'] = new Lisphp_Runtime_PHPFunction(create_function('', '
      $args = func_get_args();
      foreach ($args as $arg) echo $arg;

  $env = Lisphp_Environment::full();

Thanks! From the 'try lisphp' page, I assumed that echo was built in.

A related project is clpython. I think lisphp can be a funny way for people using php to learn some dialect of Lisp. But in the end, there is glue with C code for example FFI to get speed.

I hope he finds workarounds for some of PHP's more serious defects, like "pass by value or maybe reference" semantics, no user-defined types as indices (even most primitive types get silently mangled), and == being just blatantly wrong. Part of what makes a language pleasant to use is that it doesn't keep surprising you--stuff you'd expect to work, does.

Similar to Lisp2Perl


Which implements Lisp on Perl.

Counter-intuitively, it's often easier to implement a more powerful, more elegant language in a less powerful one than the other way around. Often, good language design is correlated with elegant syntax, which contributes to this.

Maybe. Though it is relatively easy to implement an ancient BASIC in Haskell. The other way around is harder.

I made the mailing list (Google Groups) which is the place for discussion about Lisphp:



Join us if you are interested in this project!

Let me guess - \(get_global_var "HTTP_SERVER", $MyServer, $ENV\)

Smartguys should write a lisp dialect for erlang VM. ^_^

ugly language meets pretty language

have a few drinks

nine months later, baby arrives and uploaded to GitHub! :)


Please, don't ever try to be funny on hacker news. People are too smart for this kind of shit.

So, I knew that I would be downed for saying that (of course). However, I had fun looking how the score oscillated from -2 to +2 all the day. But I guess, at the end, too-smart-people won.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact