Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
SBCL: The Ultimate Assembly Code Breadboard (pvk.ca)
159 points by kryptiskt on March 16, 2014 | hide | past | favorite | 75 comments


I am still trying to get a handle on Lisp and Scheme, but blog posts and projects like this, and the Common Lisp OS posted here previously [0] make me really impressed and confused about why people, if you dislike the Lisp style and philosophy, cannot admire its bizarre powers. I mean, I see most languages from that paradigm (why hate when you can be entertained, either by the good or bad), but this is very cool and way over my head.

[0] https://news.ycombinator.com/item?id=146670


> cannot admire its bizarre powers

Pure jealousy =]. People like to rag on lisp, but their language dujour just got what lisp's been packing for at least 20 years. Between the interactive programming model, compiling to machine code, real threading, macros, and many other features, it's the perfect secret weapon.


As much as I find lisp interesting, I've always hated the kind of smug comment like yours that every discussion on Lisp inevitably brings. Lisp is certainly far from perfect, and there are a ton of good technical reasons why people use other languages.


Consider it a response to the "lol you use lisp?!? that's really old right??" bullshit you heave to hear whenever you tell someone you use it. Nobody said lisp is perfect for everything. Just that it's better than most people are willing to give it credit for.

For instance, I'd use C for embedded systems, tight control of memory, or building libraries I wanted to be used everywhere. I'd use Erlang for distributed programming. I'd use Haskel if I needed pure functional programming. I'd use Java if I had a team of 500 engineers working on an enterprise app.

For any other general-purpose programming, I'd probably pick lisp.


> I'd use Java if I had a team of 500 engineers working on an enterprise app.

If possible, fire 490 of them, and switch to a saner language. Quintuple the salary of the remaining ten, and pocket the difference.


Sorry, but the director of product vision, who's my boss (as dictated by a complicated blame-shifting matrix of dynamic management) heard 12 years ago that Java is a good language for the kind of things he thinks we're probably building. He even gave us an incomplete UML spec to get started with!


It is my understanding that threading is a sore topic in most common lisps having either poor or experimental support. I'd love to be proved wrong, however.


Most Common Lisp implementations have had good threading support for years now, at least on the implementation's primary platforms.

The threading APIs, however, are all different -- not necessarily in any deep way, but in incidental ways like function names and signatures. There's a package Bordeaux-Threads that provides a portable API layer on top of the various implementation-specific APIs.


All these problems are fixed. Most implementations provide stable threading on most platforms.


> People like to rag on lisp, but their language dujour just got what lisp's been packing for at least 20 years.

Strong static typing? ;o)


Well as I have pointed out before, I am still learning but there are few interesting propositions here.

core.typed - Type Clojure, but still not complete

Shen - despite its questionable licensing scheme, it is a derivative of Qi. Qi and Shen are very powerful types systems (not just Haskell, but think even more complex like Idris, Coq, Agda, etc). The original implementations, if you want irony, are Common Lisp. The caveat is Shen has been ported to other runtimes, namely, Python and Ruby. But SBCL is the preferred runtime layer for Shen's crazy type system.

Typed Racket - It is a Scheme, and people will throw things at me, but I think it safe to say static types for this subset prove Lisp family languages have the potential for robust static typing systems you hint at.

TL;DR: Strong static typing is not common (pun intended), but it is possible. I think what we want to see is Shen with a good license: a very robust type system with a Lisp, separate or library. Then, Lisp will be the one ring-language to rule them all.

Perhaps we can call this library or Lisp variant Precious, and its hipster vote can go through the charts.


Common Lisp already features strong static typing (the example below uses SBCL):

  * (defun foo (x) x)

  FOO
  * (declaim (ftype (function (fixnum)) foo))

  * (defun bar (y) (declare (string y)) (foo y))
  ; in: DEFUN BAR
  ;     (FOO Y)
  ; 
  ; caught WARNING:
  ;   Derived type of Y is
  ;     (VALUES STRING &OPTIONAL),
  ;   conflicting with its asserted type
  ;     FIXNUM.
  ;   See also:
  ;     The SBCL Manual, Node "Handling of Types"
  ; 
  ; compilation unit finished
  ;   caught 1 WARNING condition

  BAR
One could also declare the function signature first (perhaps in skeleton code generated by modelling tools). Then, if someone implements it incorrectly, the compiler will throw an error (again, SBCL):

  * (declaim (ftype (function (fixnum)) qux))

  * (defun qux (z) (car z))
  ; in: DEFUN QUX
  ;     (CAR Z)
  ; 
  ; caught WARNING:
  ;   Derived type of Z is
  ;     (VALUES FIXNUM &OPTIONAL),
  ;   conflicting with its asserted type
  ;     LIST.
  ;   See also:
  ;     The SBCL Manual, Node "Handling of Types"
  ; 
  ; compilation unit finished
  ;   caught 1 WARNING condition

  QUX
So, what else do you think Common Lisp lacks compare to more "modern" languages? ;)


That's nice. What type system is that? Does it support eg parametricity?


That's the standard type system in Common Lisp.

As for whether Common Lisp supports parametricity, I don't know. It might be one of those things that isn't difficult to implement on your own, like design-by-contract or AMOP.


SBCL is an amazing project. I use it for some personal projects, but where the SBCL community really shined, in my experience, was on an AI for medical records project I worked on several years ago. We had some issues with SBCL that were fixed very quickly. A nice experience.


The only thing I would point out is that "free" FXCH isn't free. It has quite a lot of baggage inside the microprocessor and was something that designers made a conscious decision to support (see Pentium vs. AMD K5).

There are good reasons why hardware supports registers instead of a stack. However, given where x86 was at the time, the hardware designers of the time took the penalties because the gains to the software folks justified it.

Those choices no longer hold.


People love to go on and on about the power of macros, but for me, what separates Lisp from the crowd is that it's a compiled language with a REPL.

Are they any other usable compiled languages with a REPL?


Haskell, OCaml, Erlang, Scala, C#, C, C++ (!) and many others. This really doesn't set Lisp apart any more.


If by Haskell's REPL you mean GHCi, I think it probably qualifies, but it lacks one of the nicer features of a REPL, which is that you can type the same code interactively that you can load from a file. In fact that's how SLIME works with Lisp, by literally copying forms from a Lisp file into the interactive session. If you copy/paste Haskell code out of your .hs files into GHCi you get errors, because the syntax is slightly different, which I find a bit confusing & inconvenient.


Meh. You want to do imperative stuff in the repl, and you can't do that at the top-level in Haskell. The way ghci works is solid. When you are in the ghci repl, you are coding inside the do-notation for the IO monad.

Really, IMHO, jumping back and forth between the editor and the REPL with constant reloading is a better approach than pasting things into the REPL. I remember when I was playing with Common Lisp, I would always have trouble keeping the code on disk and the code loaded into the REPL synced.


>I remember when I was playing with Common Lisp, I would always have trouble keeping the code on disk and the code loaded into the REPL synced.

Then you're doing it wrong. Most people doesn't type directly into the REPL, they open up a separate buffer in Emacs and play in that. The REPL is just for small tests. There is no such thing as 'keeping the code synced'.


I don't know how you develop Lisp, but one style which seems encouraged by environments like SLIME is this: You write your file in emacs, and each time you finish a function definition you hit the "send definition to repl" keystroke, and then experiment a bit in the repl to see that you got it right.

If you do things this way, the state of the running lisp interpreter depends on the entire history of the coding session. Each time you add a new definition, you mutate the interpreter's memory. There is no guarantee that the current state matches what you would have if you recompiled the entire system from scratch.

On the other hand, the usual style in Haskell development is that you write a function definition and then hit the "reload" key combination, and this makes the state of the repl exactly the match the contents of the file. It throws away the results of any commands you ran in the repl in the meantime.

(This seems like an interesting cultural difference, something like "Haskell/ML/Java/Scheme programmers think of a program as a text, Common Lisp/Smalltalk programmers think of a program as an OS process").


One does that in Lisp, too. But often we want to avoid that. Lisp programs are often written in such a way that interactive modification is painless. There are a few very large Lisp programs which would take too long to compile/load each time. Thus we learn to deal with changing running programs. A Lisp system is often like a big collection of objects.


That's how I was doing it. It's been too long to remember the details, but I remember that there are different phases when code is loaded, and it can get very tricky when you're doing heavy meta-programming stuff. Everything works when load your new definitions from a buffer into the repl, but when you try to load it again from a file, all the phase issues come into play.


Yes, there is some potential for confusion here. Usually the problems can be solved by appropriate use of EVAL-WHEN.


The main thing I use a REPL for is playing around with defining/redefining functions and then using them in various ways, and the different syntax plus the weird :{ :} business made that tedious in GHCi. So I ditched it and just moved to the old-school C-style "edit a file, save, compile, run" cycle.


I've settled into the following workflow:

    ghci Module.hs
    *mess around*
    :e *this opens Module.hs in vim, and reloads afterwords.*
    *mess around some more*
It's the best programming work-flow I've found in any language.


With emacs and haskell-mode, I just open a Haskell buffer, and C-c C-l (control-c control-l). This will reload that buffer into another buffer that runs GHCi, creating it if it doesn't exist and using the existing one if it does.


I have used the REPL in all those languages except for Erlang. Trust me. Nothing compares to Common Lisp's Slime. Not all REPLs are equal.


Erlang has Distel, which is a pretty powerful SLIME like mode: https://github.com/massemanet/distel (and written by Luke Gorrie, the same guy who wrote SLIME).

I haven't used either Distel or SLIME extensively enough to say if it's comparable, but it looks fairly similar.


Sadly, true. I would cheerfully murder somebody for something as good as Slime for OCaml.


The CLIs in these languages are mostly not widely used, provide only few features and are poorly integrated in the language.

Plus, most don't implement Lisp's READ EVAL PRINT LOOP, but a simpler command line interface.


Well none of these languages have anything like Lisp's `read`, but Haskell's interactive loop is fairly usable and extensible. You can add vi-like keybindings and interact with other programs like hoogle, which lets you look up things based on types.


Does it provide integrated interactive error handling, break loops, debugging of compiled code, ...


It doesn't have a similar error handling system to lisp or scheme no, but it does have an interactive debugger, and the code does get compiled when you're in ghci (including whatever modules you have loaded).


Yes, it is merely an implementation issue as I mentioned in another thread.

What I think still sets Lisp and Smalltalk apart, is their environments at Xerox PARC.

We are still far from having back this type of live editing experience in more mainstream languages.


Oh please. Besides the fact, that CL has a ton of other features which set it apart any of mentioned langs, none of their REPLs are comparable in power. We have interactive debuggers built in and a language designed for it.

Take this simple example:

  > (+ 1 "foo")
  
  The value "foo" is not of the expected type NUMBER.
     [Condition of type TYPE-ERROR]
  
  Restarts:
   0: [USE-VALUE] Use a new value of type NUMBER instead of "foo".
   ...

   > 0<RET>41<RET>

   => 42
  >


What is this C++ REPL you speak of?


Try cling (http://root.cern.ch/drupal/content/cling) for just one example based on LLVM.




Most implementations of Javascript give you a REPL (assuming that JIT counts) in the sense that there's no hard distinction between code loaded from a file and code typed at the console prompt.


FORTH comes to mind.


Cool. No idea FORTH had a REPL. Do you use FORTH or have cool project examples?


OpenFirmware, such as the various implementations on http://www.openbios.org/ always comes with a Forth prompt.

I was part of the team that wrote the original Open Source implementation (under GPL terms) named OpenBIOS. The project now also hosts all kinds of other implementations that were later published under BSD terms by their owners (and had 10+ years of market experience under their belt at that time).

When FORTH started out, one of its differentiators was its live environment: you could test one step (part of an algorithm or similar) at the prompt, then define a "word" (= function) that implements it using the statements you tried, repeat until you finished your program using the words you defined earlier.


The only time you're happy to reboot a machine is when it has OpenFirmware Forth so you can play a bit with it.


Forth not only has a REPL, but an incremental compiler, too. Have a look at http://www.forth.com/starting-forth/index.html, you'll be amazed what Forth has to offer! Interactive microcontroller debugging is every bit as fun as it sounds :)


Do you have any favorite compact Forth implementations for AVRs? Each time I find myself writing a configuration-and-introspection parser for a microcontroller project I think to myself, "I should just expose a REPL here", and FORTH is the obvious choice, but I've never had the time to dig through the many AVR FORTH implementations out there for the right one.


AmForth (http://amforth.sourceforge.net/) is my current favorite, but like with Linux distros, my tastes are somewhat floating. So I'm writing my own little weekend Forth just for playing around ;)


FORTH had (and has) a REPL.


Well, I wouldn't say Forth is specially "usable," (I'm far from an expert, so this may be it) but it applies: compiled with a REPL.


Does it matter if it is a bare-bone systems language with no GC or automated memory management?

Erlang and Elixir, a new language for the Erlang VM with a different syntax, are compiled languages with REPLs. I also think about Scala and others for the JVM, but I have a feeling you do not think of those as examples because of the VM or the lack of native code compilation. I could be wrong.


Any language can have a REPL, it is a plain implementation issue.


It's not. You will detect that in many cases it is a language issue, too. If you look at Lisp, you can see that the language definitions are optimized for interactive use and have a lot of facilities defined at the language level: incremental definitions, evaluation, effects of loading code, a robust interactive error system, ...


How come it is not?

- Create an interpreter library for language X

- Offer a GUI/CLI application using the said library

Not all repls need to work at function level like LISP does.



So you use existing bugs in one given implementation to defend your point of view?


So you redefine missing features due to language problems as 'bug' to defend your point of view?


LuaJIT.


Julia


Thank you! I did not know about SBCL, or Paul Khuong, before. This is the most interesting thing I've read this week. This kind of article is why I come to Hacker News.


Site is currently down for me, presumably deluged by traffic. A Google cache exists, but at least on Chrome it still needs to load something from pvk.ca before it displays any portion of the webpage. Therefore, here is the page source with most of the markup removed; you can get from it some idea of what the page is about.

http://pastebin.com/JL7aRcpn


It looks like he (accidentally?) included a gigantic scanned image in the post. However most of the comments on the last pvk.ca post submitted here were also about how it's down (https://news.ycombinator.com/item?id=7286655), so the blog config/hosting might just be borked.

Google Cache seems to always try to load the images and hang for a while if the site is unreachable (not sure why, since if the site were reachable I wouldn't be on Google Cache in the first place). You can click "text-only version" in the top-right to stop that.


Thanks for the heads up on the scan! That's what I get for publishing just before rushing to dinner.

The blog is just a bunch of static files, so there shouldn't be any issue. This situation is really annoying; I'll figure something out soon.


There's a readable copy on http://planet.lisp.org/


It loads for me currently, after a bit waiting though, here is a mirror: http://archive.is/8WQV5


djb once wrote a FORTH-like interpreter.

Anyone get that old code to run today?

I have always thought think LISP/Scheme's "best" use is to write code generators (e.g., that output C or asm). I know there's at least one LISP/Scheme project that outputs C, so I know I'm not alone in thinking this can be useful.

FORTH has always seemed better suited to driving hardware than any LISP/Scheme.

This is some sort of bias perhaps. These are both very flexible languages.

What if historically programmers tried to use FORTH for "AI" and LISP as a "portable assembler"?


Bigloo Scheme (among others) generates C code. I've used it on a few projects to process, reformat, and visualize some meteorology data and was pleased with the result. I know some other people that used it for other scientific application development (including 3D graphics) and it performed beautifully. One of my friends used Chicken Scheme to write some interesting web scraping tools. It also has the ability to compile to C.


> What if historically programmers tried to use FORTH for "AI" and LISP as a "portable assembler"?

Then I guess the Burroughs B5000 and its ilk might have become a popular machine for AI research. We would have had Stack Machines instead of LISP Machines.


I'll just throw that in here, as I always do, in case people want a nice historical overview (yet detailed) of stack machines : http://www.eecg.toronto.edu/~laforest/Second-Generation_Stac...


Lisp Machines were mostly stack machines.


I have in fact written a Forth dialect, but I'm guessing you mean some other djb. :)


Well done. What language did you use to write it?


C. I know, booorring; I just hate dealing with the x86 and haven't had occasion to work with ARM, etc.

https://github.com/darius/tusl

P.S. If Dan Bernstein did write a Forth, I'd like to see it.


There is no such thing as an "exciting" computer language.

But there's also no shortage of big, slow, verbose languages designed for dummies.

Worse, these actually form the majority of the world's most popular computer languages.

When juxtaposed against that state of affairs, the small, terse, fast, flexible languages which do not insult one's intelligence could seem "exciting".

FORTH has that effect on me.

Check IOCC. And let me know if you do get it to compile.




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

Search: