Skip to content

What learning lisp taught me about other languages

For the last few years I’ve been learning and using Lisp more, and here is a disorganized, poorly-worded dump of how Lisp changed my opinion about other languages.

Static Types (as implemented by C# and Java) are oppressive.

I spend most of my time in C#, and it always feels like I’m toiling in the Type mines under the iron fist of compiler. I frequently run into problems where I can’t abstract common code because the .NET framework doesn’t use C#’s abstraction mechanisms enough (more detail). In Lisp it feels like types are a tool I can employ when I need it, but it’s rare that I need COERCE something from one type to another. Too much of my C# is devoted to casting to / from types, a hefty tax I pay to the compiler.

Syntax doesn’t have to be so hard.

I was recently working on a ruby script, and had to stop and think: “what does ruby want for ifs again? curly braces? indentation?” In Lisp it’s easy: it wants matching parentheses. You can add syntax if you find it useful (CL-INTERPOL is one of my common additions), but there’s nothing I need to remember, no need for cheat sheets. The simplicity also highlight the strangeness of things like python’s “pass” statement. There’s no human meaning to a pass statement, it’s just to handhold the parser, and I don’t think that should be one of my responsibilities.

Development tools don’t have to be such a pain in the ass.

In C#, I frequently end up waiting for Visual Studio, and that’s on a modern dual-core workstation with 2GB of RAM. In Lisp, I end up waiting once when I start slime/emacs, and once when I initially load a big system, and then everything else is pretty instant. That’s on my Asus EEEpc, a 900MHz Celeron with 512MB RAM. I could use cores to eliminate most of that start-up time. At work we have one fast server we all use to run our lisps, and if we didn’t have to run programs like Visual Studio, we could use low-power, low-cost workstations and be perfectly happy. I know some people think Emacs is a heavyweight program, but devenv.exe sets a new standard.

Variables don’t have to be so hard.

LET statements and lexical scoping in lisp are pretty basic, and meet all my needs. The scoping rules in other languages seem really overcomplicated by comparison. In C#, sometimes curly braces open a new scope, but sometimes not. Sometimes you have to declare variables away from their usage in order for them to be available in all the right scopes you want. In python / ruby, I’m frequently confused whether I’m declaring a new variable or using one from a higher scope. Most of that is from lack of ruby/python practice, but even when first learning Lisp, the rule was so simple that I never had trouble with it.

27 Comments

  1. Grant Rettke wrote:

    I do Java and .NET and Ruby and Perl at work, and Scheme on nights and weekends. I don’t feel like I am slave to the type system at all. It is a feature I choose to utilize.

    Why do types make you feel like a slave?

    Sunday, March 30, 2008 at 5:24 pm | Permalink
  2. Tamas Papp wrote:

    Well said, I agree with everything. The other language I am using sometimes (besides C) is R, which is a nice language (dynamic typing, but C-like infix syntax), but scoping is a PITA, I have to think about it in delicate situations, whereas in Lisp it is effortless.

    Sunday, March 30, 2008 at 8:19 pm | Permalink
  3. dnm wrote:

    @Grant: I just ran into the compiler restrictions .Net imposes on friday. ds is a HashTable with each element containing another HashTable whose elements contain Regex elements. So instead of writing

    Match m = ds[key]["id"].Match(text);

    which would work at runtime, the compiler forces me to cast everything:

    Match m = ((Regex) ((Hashtable) ds[key])["id"]).Match(text);

    because it won’t wait until runtime to do the type-checking. An unnecessary burden on me that should have been checked at runtime, not compile time. Alot of parens, but then, this obviously isn’t Lisp.

    Sunday, March 30, 2008 at 10:33 pm | Permalink
  4. lani wrote:

    same here. In Java.
    here I fetch from the hashtable ‘h’ the value for ‘3’ and convert it to char via conversion to String.
    (( (String)h.get( Integer.toString(result) )).toCharArray())[0];

    or maybe this is just bad code :(

    Monday, March 31, 2008 at 1:27 am | Permalink
  5. leppie wrote:

    @dnm: Either refactor that to a helper method or use generics aka Dictionary.

    Monday, March 31, 2008 at 2:44 am | Permalink
  6. Jay wrote:

    dnm,

    Shouldn’t you be using Dictionary with generics instead?

    Monday, March 31, 2008 at 2:55 am | Permalink
  7. david wrote:

    @dnm, I agree with leppie, generic version of the Dictionary class should solve your problem.

    If you declared ds as Dictionary<String,Dictionary>, ds[key]["id"] would just work.

    Monday, March 31, 2008 at 3:02 am | Permalink
  8. Johnny Bravo wrote:

    This is a classic rant from someone who barely knows how to properly write code in C#. Maybe if he had spend proper time on learning for example generics instead of drifting through various languages, he would not have made the completely silly and beginner error shown in the HashTable example.

    Learning a programing language (plus runtime) takes time.

    Monday, March 31, 2008 at 3:43 am | Permalink
  9. derek wrote:

    Guys, although the case by dnm is trivial to be replaced by Dictionary, there are much more complicated cases in which simply by replacing generics won’t work.

    Monday, March 31, 2008 at 3:46 am | Permalink
  10. Steve Cooper wrote:

    I agree on two points, but like others wonder about the typing issues. I don’t know that one is inherently better than the other. Static typing is great when you’re doing big changes to large systems; remove a commonly-used property, say, and everything impacted is shown to you as the error list. Static typing shows you the impact of your changes.

    You can’t get that in dynamic typing. OTOH, dynamic typing allows for cleaner-looking code and duck-typing, which are also really useful.

    I don’t know that there’s a ‘best’ here, but I suspect that static typing works better at large scales (because it tells you quickly where yor changes impact the whole system) and dynamic languages are better at smaller scales, because they allow you to work faster on small problems.

    C#3 helps a lot here with it’s static type inferrence. Statements like

    var parameters = new [] {
    key=”key1″, value= new [] { “val1″, “val2″ },
    key=”key2″, value= new [] { “val3″, “val4″ }};

    // == “val2″
    var item1 = parameters["key1"][1];

    are strongly typed but don’t require any type declarations, and get static typing closer to a dynamic style.

    Monday, March 31, 2008 at 4:21 am | Permalink
  11. Alexey wrote:

    The only thing I’m missing in Java typing mechanism is type inference (like in Scala or OCaml). Most of time ST help not interfere. You have to study in more detail it before judging in public.

    Monday, March 31, 2008 at 4:37 am | Permalink
  12. kl wrote:

    You’re exaggerating difference between ease of syntax. Lisp requires you to remember nesting rules for things (do I put a symbol here or a list with a symbol?). Thankfully there’s awesome macro system that let’s you change everything to be your way.

    To commenters: I don’t like generics. The syntax is ugly, especially when you nest containers.

    Monday, March 31, 2008 at 4:44 am | Permalink
  13. Alexey wrote:

    I don’t see any problem to remember which syntax to use: {} or begin-end or : with indent or something else to mark the bounds of code blocks. The problem which lisp really solves is to make syntax extensible. IMHO its the main advantage of it over non-lisp languages. And I find pointless creating dialects like this: http://www.newlisp.org/ where there is no macros.

    Monday, March 31, 2008 at 4:44 am | Permalink
  14. Weave Jester wrote:

    But how many parenthesis? Some dialects of lisp have flatter syntax than others:

    Scheme: (cond ((a) (b)) ((c) (d)) (else (e)))

    Arc: (if (a) (b) (c) (d) (e))

    So Lisp-like languages are not exactly 100% straightforward, either. Ruby does have more rules, but they are fairly consistent: all control structures end with the keyword “end”.

    Regarding Python’s pass statement, it’s not used very often, because there are very few times that you need a blank function or class, or will ignore an exception. Those are the only times in which you need “pass”, so it’s not that big a deal.

    The problem I have with Lisp is that whilst it’s lack of syntax paves the way for handy macros, it also makes it tougher for me to read than a language with more complex syntax like Python or Haskell.

    Monday, March 31, 2008 at 5:09 am | Permalink
  15. Larry Clapp wrote:

    Johnny Bravo: “This is a classic rant …”

    This is also a classic pattern:

    Blogger: I don’t like , and here’s why, and here’s an example:
    Commenters: is bogus, you should do . Therefore your point is also bogus.

    and it’s bogus too.

    Monday, March 31, 2008 at 6:39 am | Permalink
  16. pozorvlak wrote:

    Hi,

    Here via Reddit, and enjoyed the article. Re the static typing thing, I suspect that a lot of your pain comes from trying to use static languages as if they were dynamic (the HashTable thing in the comments is an obvious example). This is something I struggle with too: I talk about it more in this blog post.

    Monday, March 31, 2008 at 6:47 am | Permalink
  17. she wrote:

    “I was recently working on a ruby script, and had to stop and think: “what does ruby want for ifs again? curly braces? indentation?””

    it is simple

    if condition
    do stuff
    end

    you can use {} and using {} is very popular for chaining methods and terse one liners. Personally I end up using do/end more than {} although i often use {} as well

    Monday, March 31, 2008 at 8:16 am | Permalink
  18. she wrote:

    btw i think python and ruby code in essence look very similiar

    from my point of view python and ruby are close allies – both are younger than perl, have a cleaner syntax and represent a large user-base that have similar needs (python is a bit faster but i think noone using a language such as perl python ruby should care about small SPEED differences. for speed i recommend everyone to couple that language with a faster lang like C or C++ anyway)
    they somewhat both fill the same ecosystem, and i predict that both ruby and python will grow (besides, every language with strong www aspect will grow because the www still grows)

    Monday, March 31, 2008 at 8:18 am | Permalink
  19. troelskn wrote:

    > The problem I have with Lisp is that whilst it’s lack of syntax paves the way for handy
    > macros, it also makes it tougher for me to read than a language with more complex
    > syntax like Python or Haskell.

    Have you had a look at IO? It has much of the same minimalistic qualities that Scheme has, but with a syntax based on words, rather than parantheses.

    Monday, March 31, 2008 at 8:19 am | Permalink
  20. Steve wrote:

    @Weave Jester:

    Using one example from Arc and one example from Scheme to show how “Lisp-like” languages are inconsistent is dishonest. It’s the same as using booleans in C vs. booleans in Java to show how “Algol-like” languages are inconsistent.

    Monday, March 31, 2008 at 8:56 am | Permalink
  21. Tom wrote:

    If the author finds himself dealing with too many casts, it may be a sign of poor design (either his code, or the MS libraries). Often it helps to think before typing when using high level OO languages ;-)

    Monday, March 31, 2008 at 9:00 am | Permalink
  22. Code Auditor wrote:

    After auditting millions of lines and patches to various software projects and languages I can safely say that 40% of all lines in C/C++/Java programs is dedicated to static typing.

    You have to maintain that code, it is non-trivial.

    Monday, March 31, 2008 at 3:12 pm | Permalink
  23. Steev wrote:

    It seems like some of you are suggesting he make a type for his example object. This is bogus and a waste of time.

    Monday, March 31, 2008 at 6:19 pm | Permalink
  24. Weave Jester wrote:

    @Steve
    But that’s exactly what Ryan appears to be arguing. He said:

    “what does ruby want for ifs again? curly braces? indentation?”

    Since Ruby doesn’t have an indentation-based syntax, presumably he’s mixing Ruby up with Python. In other words, he complaining that different Algol-like languages have different syntax, and that this makes them difficult to remember.

    I merely compared Arc to Scheme to show that Lisp-like languages can have dissimilar syntax as well.

    Monday, March 31, 2008 at 7:44 pm | Permalink
  25. Steve Knight wrote:

    >> … it always feels like I’m toiling in the Type mines under the iron fist of compiler.

    Brilliant!

    Wednesday, April 16, 2008 at 5:10 am | Permalink
  26. T wrote:

    Shouldn’t you use Map in Java instead of Dictionary?

    Monday, May 5, 2008 at 6:17 pm | Permalink
  27. DBS thinks that if we got the bad jews out of the way, the other jews would happily live along side the rest of us, yet all the evidence on his site says otherwise! Read Maurice Samuals You Gentiles to see for yourself.

    Monday, April 2, 2012 at 4:21 am | Permalink

3 Trackbacks/Pingbacks

  1. My daily readings 03/31/2008 « Strange Kite on Monday, March 31, 2008 at 7:33 am

    [...] Ryan’s Tech Blog » What learning lisp taught me about other languages [...]

  2. test 03/31/2008 « Strange Kite on Monday, March 31, 2008 at 1:48 pm

    [...] Ryan’s Tech Blog » What learning lisp taught me about other languages [...]

  3. rascunho » Blog Archive » links for 2008-03-31 on Monday, March 31, 2008 at 4:53 pm

    [...] Ryan’s Tech Blog » What learning lisp taught me about other languages (tags: ryepup.unwashedmeme.com 2008 mes2 dia30 at_home Lisp programming) [...]