4

I spent couple of months learning Scala and got overwhelmed by number of different constructs it had, After looking at partial functions, partially-applied functions, pattern matching, actor syntax, I gave a thought to learning Clojure which doesn't have too much in terms of syntax but looking at the how Java inter-operability is handled, it looks very difficult to get use to.

for example: things like doto, new and .

Every time I start typing a function, there are times I write (1 + 2) instead of (+ 1 2).

It looks like I will have to completely forget how I write regular programs to get use to this syntax.

Also finding it difficult to deal with side-effects that do and let form have. Aren't they similar to what functions with void type are ?

Things like registering a callback,things normal I/O, how can such things be side-effects free.

Till now all I really liked about Functional Programming is that, immutability makes concurrency easier when dealing with shared data, functions as first class objects and having higher-order functions, apply,map and reduce with functions and lists .

I wanted to know, are there practical benefits I will get once I am use to this syntax ? I f I continue to learn Scala and avoid using var, and always use recursion instead of loops. I can do all the above things and still write understandable code.

6
  • 7
    The difference between infix (1 + 2) and prefix (+ 1 2) notation has absolutely nothing to do with functional vs. imperative.
    – ziggystar
    Commented Apr 4, 2013 at 11:01
  • ya.. typed that in a hurry. edited my post. Commented Apr 4, 2013 at 11:03
  • Lisp is a very simple language. It is by far easier to write parsers for LISP than for e.g. Java or Scala. So the prefix notation was chosen because of its simplicity despite its inconvenience. Getting used to this syntax won't buy you much.
    – ziggystar
    Commented Apr 4, 2013 at 11:07
  • Getting used to prefix (+ 1 2) notation means you can define macros. Paul Graham's On Lisp explains the benefits of macros in great detail. Commented Apr 4, 2013 at 13:17
  • 1
    No. Homoiconicity is orthogonal to concrete syntax. It's about abstract syntax, and how code is represented and evaluated. There are Lisps which are not homoiconic (I've written one myself), and non-Lisps which are homoiconic.
    – user39685
    Commented Apr 4, 2013 at 23:28

4 Answers 4

14

Lisp syntax is just that: syntax. It has nothing to do with semantics. In other words, the syntax (ideally) shouldn't affect how you write programs; if you feel like you have to "completely forget how you write regular programs", it sounds like you're having difficulty with the semantics, not with the syntax.

Practical benefits of Lisp syntax include:

  • consistency. Function/macro/special-form followed by its arguments. No operator precedence issues. Grouping is usually explicit.

  • easy to parse and unparse. This makes it easier to write source code tools, such as static analyzers and code browsers.

  • extensibility

    • user-defined macros. Users can extend their Lisp system's syntax.
    • system-defined syntax. New special forms can be added to a Lisp system without having to worry about how their syntax interferes with existing forms.

You also threw in a couple of questions about side effects and functional programming. Those aren't related to Lisp syntax and should really be asked in a separate question.

8
  • How do people dry run such code? I mean expression inside expression inside expression. by the time you solve inner expression you forget what the other one was ? and if remember as well it's difficult to identify which close paren si for which open paren? Should every new '(' be in a new level of indentation ? Any tips ? Commented Apr 4, 2013 at 12:34
  • 1
    @Amogh In my limited experience with Clojure, yes, you want to use a newline and indent for nested functions. This is a Y-Combinator example I adapted from a tutorial in Scheme to Clojure that shows how I'd lay it out: pasteall.org/41052/lisp
    – KChaloux
    Commented Apr 4, 2013 at 12:57
  • 1
    @AmoghTalpallikar the only way to get good at it is to do it and look at how other top coders do it. There's no magic trick. Parentheses matching isn't a problem if you have any kind of code-aware text editor at all.
    – user39685
    Commented Apr 4, 2013 at 13:03
  • 1
    wrote a function tat returns a vector containing fibonacci numbers with n elements. feel much better now :) Commented Apr 4, 2013 at 21:12
  • 1
    @AmoghTalpallikar. Your eyes use the indentation to tell what is what, rather than jumping back and forth matching parens.
    – mike30
    Commented Apr 22, 2013 at 16:10
9

Syntax

The syntax threw me off too at first, but after working with it and reading about it I realized some things:

1. Lisp doesn't have operators, it only has functions, so stop thinking of +-*/ as operators and that will help with things.

Don't think:

(1 + 2 + 3 + 4)

or, even though it's correct:

(+  1 2 3 4)

but:

(sum  1 2 3 4)

and it will feel more natural.

Likewise with -, think more along the lines of:

(subtract-from  x  y1 y2 y3)

and that should help.

2. Method calls aren't that different!

Functions are the first thing in every Lisp form (the stuff between the ()); since methods are functions, they come first! It's not really that different from Java's syntax though -- all you're doing is moving the starting paren to the front and swapping the positions of the object and method:

Java:

objectInstance.someMethod(arg1, arg2, arg3);

Clojure:

(.someMethod objectInstance arg1 arg2 arg3)

(You also lose the commas and semicolon!)

The (.someMethod objectInstance ...) is a special form that gets expanded out into a form using .: (. objectInstance someMethod ...). The latter feels weird because we're not used to thinking of the . as actually doing something when we're calling a method/property in Java; the former is the idiomatic way of doing things in Clojure anyway (reference).

New is similar:

new namespace.Object(); // Java

(new namespace.Object)  ; Clojure

"Difficult Things"

The "difficult things" you mentioned are actually there to make your life easier!!

You said in a comment that it was annoying to keep track of things when nesting expressions within expressions; the do and let forms are built specifically to help ease that difficulty.

Let's start with do

do lets you call things imperatively, one after another, without nesting:

(do (.someMethod objectInstance arg1 arg2)
    (.someOtherMethod objectInstance "foo" "bar" "baz")
    (.aMethod anotherObjectInstance 0))

The behavior of do is such that each of these method calls will be run in the order in which they're given and the return result of the last expression (in this case the return result of (.aMethod anotherObjectInstance 0)) is what is returned from the do block itself.

Each of these methods may or may not have side effects, and may or may not return a value (all of which but the last will be ignored).

Let

The let form does the same thing as do but it also gives you the ability to add local variables to the mix:

(let [foo "foo"
      bar "bar"
      arg1 (generateValueFrom foo)
      arg2 (generateValueFrom bar)]
  (.someMethod objectInstance arg1 arg2)
  (.someOtherMethod objectInstance foo bar "baz")
  (.aMethod anotherObjectInstance 0))

Again, the method calls will be run step by step in order, only this time they use the local variables defined in the let's bindings (the stuff between the []), and the last call's return value will be returned as the let's return value.

let is handy for breaking a bunch of nested calls down into something more sane; e.g.:

(reduce someCombiner {} (filter keepXs (map someTransform ys)))

becomes:

(let [txd (map someTransform ys)
      xs  (filter keepXs txd)]
  ; do the last call in the let's body so that
  ; its return value is what gets returned
  (reduce someCombiner {} xs))

Is The Synatx Worth It?

You bet your sweet bippy it is!

The Lisp syntax allows the language to have macros. Macros are functions that run at compile time that rewrite the code. What this means is that you can take code that is repetitititve or complex and write a macro that is simpler to call, and that macro will write the complex version for you.

For example, say we have the following do block:

(do (.someMethod      objectInstance arg1 arg2)
    (.aSillyMethod    objectInstance "foo" "bar" "baz")
    (.aSadMethod      objectInstance "alpha")
    (.anHappyMethod   objectInstance 1 2 3)
    (.someOtherMethod objectInstance \a \b \c)
    (.aMethod         objectInstance 0))

We have to repeat the same object instance six times! That's annoying. The doto macro can make things a bit lighter for us typing-wise:

(doto objectInstance
  (.someMethod      arg1 arg2)
  (.aSillyMethod    "foo" "bar" "baz")
  (.aSadMethod      "alpha")
  (.anHappyMethod   1 2 3)
  (.someOtherMethod \a \b \c)
  (.aMethod         0))

The doto macro allows us to specify the object instance once, up front, instead of having to repeat ourselves in the code later on -- it rewrites the expressions we give into the do block shown above. (This particular macro is similar to the with syntax some languages support.)

Now, this is a really simple example, but just imagine what sorts of coding tasks you could automate! And that's where the power of Lisp's macros comes in: you can write functions to make the programming language write your code for you!

Side Effects

You can still have side-effects in Clojure code, especially if you're doing interop with Java OOP since that OOP has side-effects. If you use more of the core Clojure code than Java, you'll not have as many side effects because it'll be more purely functional.

A lot of what I've found in my functional programming experience is that it's about partitioning the side-effect causing code from the purely functional code. You can do this in pretty much whatever language you're using (even Java).

Comparison to Scala

I've not used Scala, so I can't help you there. But hopefully the above was informative.

2
  • You can partition the side-effect code in Java, but it's not very idiomatic. Commented Apr 4, 2013 at 23:59
  • 1
    @tieTYT Right, since with OOP you have little bundles of state all over the place. You can use stuff like Command-Query Separation though to help know when you will cause side effects.
    – paul
    Commented Apr 5, 2013 at 12:05
2

These are matter of taste. I have converted gradually over the years to preferring Lisp syntax over Java syntax. Why? Good question. Can't really pinpoint a single reason, but here are some thoughts.

Lisp syntax is very simple, almost trivial, albeit maybe bit strange at first (before you unlearn some old habits). Trivial syntax makes it easy to programmatically mangle Lisp code. Code is data is code. Make macros and DSL way more easier and effective. "Lisp is a programmable programming language."

This gets bit philosphical, but I strongly feel that Lisp syntax is closer to "true nature of programs". Structure of programs is not linear -- it's more tree/network like. Java point of view is more linear, line oriented. Lisp POV is hierarcial and tree oriented. Those parenthesis inside paranthesis is just a way to write trees.

Thinking more in tree oriented (rather than line oriented) way enables new ways of thinking, editing and working with code. For example some powerfull tools such as Paredit, which demonstrates some concepts which might not make much sense in Java syntax, such as barf and slurp:

slurp:
(foo (bar |baz) quux zot) -->  (foo (bar |baz quux) zot)

barf:
(foo (bar |baz quux) zot) -->  (foo (bar |baz) quux zot)

Code is a tree! :)

0

The principles of the functional language like lisp or clojure are very useful for writing self modifing code.

So for adding some values you have only to generate a instruction with a + at the beginning and the numbers after that, in an imperative language you have to add each additional value manually.

The thing is, languages like lisp or clojure don't really have any syntax, ist only a description of a tree structure as text.

19
  • 1
    Yes, Lisps do have syntax in multiple forms: 1) numbers, strings, symbols, lists, etc.; 2) special form syntax -- such as define <symbol> <form> or lambda <parameter list> <body forms>; 3) macro syntax, both reader macros and the other kind; 4) syntax extensions such as Clojure uses, i.e. [...] for vectors, {...} for object literals, etc. So it's really false advertising to say that Lisps are syntax-free.
    – user39685
    Commented Apr 4, 2013 at 11:33
  • 1
    The fact that lisp is functional has nothing to do with the self modifying code trick. Besides, Lisp is not a functional language. Commented Apr 4, 2013 at 12:00
  • 2
    @FlorianMargaine LISP is not a functional language? I'm sorry, but whaaaaa? A language developed to be the programmatic form of lambda calculus and you're saying it's not functional? How do you figure? Commented Apr 4, 2013 at 14:21
  • 1
    @JimmyHoffa Lisp is as procedural as it can get. If any language with first-class functions is "functional", then C is one too (thanks to function pointers). And Lisp is just as functional as C. Commented Apr 4, 2013 at 14:46
  • 2
    @JimmyHoffa Lisp is, in general, not a "pure functional language". It encourages 9and makes it relatively easy) to write code in a functional style, but provides (specifically in Common Lisp, but probably in other languages in the family) a wide variety of imperative tools to use when that makes expressing your code easier.
    – Vatine
    Commented Apr 4, 2013 at 15:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.