Skip to content

common lisp rosetta for the devs

o-jasper edited this page Sep 19, 2012 · 5 revisions

Common Lisp differences with Julia

A list of all CL language symbols. Note that 'some dude' wrote this stuff. Note that common lisp has too much in the spec and no standard libraries. Probably would have been better to separate the two.

Note that some of the features can be done with macros effectively. Still having thing possible, and having many people maintain versions of it separately is also annoying, it might still be handy to have a standard library.

defvar, defparameter

Not really the same as Julia toplevel variables, in CL these are special variables;

(defvar *special* 1)
(print *special*)    ; -> 1
(defun print-special () (print *special*))
(let ((*special* 2))
  (print-special)) ; -> 2 local variable went right through to the function body.
(print-special) ; -> 1

Can be useful in having basically a 'context' that you do not have to keep passing and accessing a 'context object'. Could to better than the asterix convention to remind that the vars are special.

Boolean stuff

There is a bool type in CL but anythint that is not nil is true. This is useful; for instance (or a b c d) returns the first non-nil one. Guessing this is shallow though, could easily make a macro in Julia returning the first non-nothing or non-false one.

Defining functions

defun is typically most used, there you can declare types, but there is no overloading. defmethod functions are overloaded. Common lisp forces you to make up your mind what the parameters of methods are in a defgeneric form. Maybe they felt things are too 'wild' if there are lot of functions in different ways.

setf and setf-functions

A function changing some particular thing can be made 'official', making it clearer what it does instantly.

(defun (setf something) (set-to ..args..)
   ...body setting it.. 
   ...probably should return set-to...)
(setf (something ..args..) set-to) ;Is how yo use it.

Of course it can be used alongside a non-setting version. Afaik, Julia doesn't have it, setting stuff is done with =, which isn't a function. (that can be overloaded) If this were to change one has to take into account that = is used to define stuff in Julia aswel.

An idea is to implement something like this on 'accessors' and 'readers'; a.b could run a function instead of accessing, but one that only depends on the object, and not changing any state.(just returning) (possibly excepting transparent memoizing) a.b = to would run a function that assigns. (Well, much like assign now, but with members instead.) Btw I do not like member functions used otherwise, i think Julia is wise to stay away from them.

defmacro

Defines macros. Different with julia is that argument lists don't have to be flat for instance

(defmacro some-macro (a (b c) d) `(print ,a ,b ,c ,d))
(some-macro 1 (2 3) 4)  ; -> (1 2 3 4)
(some-macro 1 2 3)      ; -> macroexpansion-time error '2' should have been a list.

Defining macros seems a little trickier in some respects in Julia, and 'easier' in other respects. Making macros shouldn't be taken too lightly, usually you shouldn't.(where it is useful and mostly caveitless is usually either binding variables or defining stuff)

Probably more to say about the differences of representation of code in CL(just nested lists) and Julia.(Probably mostly Expr,QuoteNode objects)

destructuring-bind takes a list and similarly takes apart.(not seen it one Julia so far, can be done, though the 'rue analogy' would be called with-destructured-expr or such)

compiler macros, define-compiler-macro

Adds a macro that will be called on a function call of that name. In Common Lisp these are identical to macros but for two big differences; they can return nil, and then the implementation will ignore that the 'macro' ever ran and just keep the function. Or it can return something else, and the code might be replaced if that something else is faster.(Probably you assume it is always faster and make

This sounds like something that may be nice to have. Caveit is that it is a full macro and could do anything, they have to be made pretty carefully. But it can probably exist alongside basically any other optimization stuff. But compiler macros just cover optimizing one little thing. If a general method can catch something-to-be-optimized, it should!

macrolet, symbol-macrolet

Local macro, the latter for just symbols. Not seen in Julia sofar, usual caveits of macros apply. I think not having it weakens macros a bit relative to those of CL.

case, typecase, cond

case basically a C switch type macro, you give a value and each clause has a value or list of values of which if any match, that clause is chosen. (It doesn't need to break; every case though, strange how C doesn't have a case because not breaking seems so rare.)

typecase is the same as case but instead of values, the user provides types. Can do much more than CLs defmethod. Of course defmethod and in Julia, typed functions can fill much of the role, selecting versions of an overloaded function instead of clauses.

cond fills the if .. .. elseif .. ., elseif ... else .. end-like stuff.

with-open-file, with-input-from-string Opens streams and closes them at the end of the macro body. Very useful. I have one, planning to replace it with plain with so you can do @with open(....) begin .. end and a with_end(io::IOStream) = close(io), basically the overloaded function can catch anything.

Otherwise, all of these easily covered by Julia's macros. (i already did some)

multiple-value-bind and values

values is somewhat like (a,b) notation but is hugely different in that you basically need to use the multiple-value-bind to get more than the first value back. values as CL does it can be handy when you want to return something that users might need but often just want to discard. But doesn't seem all that valuable.

loop, iterate

iterate is a library, for the record. Thought they should be mentioned. They're ... not horrible but i dont really like them either. for syntax plays some of the same roles in Julia.

Note that there are more libraries in CL that make various sorts of binding easier. (had a 'phase' wasting time making stuff like that use none now)

block, return-from

You can make named blocks (block name ...body..) and basically get out of any named block with (return-from name return-value) (local)functions, macros and methods are also blocks. (prog does full gotos, but people dont like gotos anyway.)

unwind-protect

This macro allows you to ensure an expression is executed even if a return or return-from is called:

(block ska
  (unwind-protect
      (progn (print :a)
             (return-from ska :return-something))
    (print :b))) ;This will be called despite returning above.

For instance it is used when anything is cleaned up for you by a macro or function(taking a function as argument) I think this one is pretty important.

All the 'with' stuff

For instance lispbuilder-sdl has with-sdl if they're about creation and then destruction after you're done, destructors might be able to perform the same role. Most libraries like that have some with.. macro.

with-slots, with-accessors

Uses symbol-macrolet to attach accessing struct/class members to symbols. the struct.member notation is pretty short,(certainly shorter than (slot-value obj 'slot)) but getting them this may still be interesting to have in Julia.

with-open-file, with-input-from-string

Opens streams and closes them at the end of the macro body. Very useful. (destructors could do it?)

Docstrings

Julia throws a lot of line numbers in. So autodoccers should be able to find them and use them for autodocumentation. (I do not know how get a 'hook' on macroexpansion yet something like macroexpand-hook, but i found that weak, so i made this(shameless plug).

disassemble

I dont use it basically ever tbh, but it is awesome. It shows you the assembler bytecode or whatever such the CL implementation produces 'upon seeing that value'.

Annoying is to see how short it is but yet you cannot make a few-kilobyte binary with the compiler. -_-

Libraries

Alexandria

Alexandria is an oft-used macro for various function/macros that are a bit lacking in the CL spec.

rcurry, curry, compose

Various ways to 'fill in a variable' infront of in the back of a function or to compose functions. Can be slightly tricky to read; i just use one layer of these and proceed to lambda after that, but can make defining functions more convenient.

An more flexible but 'uglier-code-producing' way to the same is to access lambda quickly and without making a parameter list, by using numbered parameters.

if-let, when-let

Often you're only interested in a variable if it is non-nil, that is what these are for. In Julia things are a bit different; Julia requires a Boolean type. Still something like

if (x = 3)==3
  x
end

Seems to work; basically allowing binding of new variables anywhere.

with-gensyms also, it is covered by a,b = gensyms(2)

Automatic memoization. Never ended up using it, but sounds neat.

There was a 'superset-of-Haskell' lib somewhere..

Allows you to write code to write html inside a lispified html form that writes other html you want to write.

Sure i forget about things