Lisp and Erlang Atoms, Ruby and Scheme Symbols. How Useful Are They

Lisp and Erlang Atoms, Ruby and Scheme Symbols. How useful are they?

A short example that shows how the ability to manipulate symbols leads to cleaner code: (Code is in Scheme, a dialect of Lisp).

(define men '(socrates plato aristotle))

(define (man? x)
(contains? men x))

(define (mortal? x)
(man? x))

;; test

> (mortal? 'socrates)
=> #t

You can write this program using character strings or integer constants. But the symbolic version has certain advantages. A symbol is guaranteed to be unique in the system. This makes comparing two symbols as fast as comparing two pointers. This is obviously faster than comparing two strings. Using integer constants allows people to write meaningless code like:

(define SOCRATES 1)
;; ...

(mortal? SOCRATES)
(mortal? -1) ;; ??

Probably a detailed answer to this question could be found in the book Common Lisp: A Gentle Introduction to Symbolic Computation.

Why doesn't Haskell have symbols (a la ruby) / atoms (a la erlang)?

I agree with camccann's answer that it's probably missing mainly because it would have to be baked quite deeply into the implementation and it is of too little use for this level of complication. In Erlang (and Prolog and Lisp) symbols (or atoms) usually serve as special markers and serve mostly the same notion as a constructor. In Lisp, the dynamic environment includes the compiler, so it's partly also a (useful) compiler concept leaking into the runtime.

The problem is the following, symbol interning is impure (it modifies the symbol table). Because we never modify an existing object it is referentially transparent, however, but if implemented naïvely can lead to space leaks in the runtime. In fact, as currently implemented in Erlang you can actually crash the VM by interning too many symbols/atoms (current limit is 2^20, I think), because they can never get garbage collected. It's also difficult to implement in a concurrent setting without a huge lock around the symbol table.

Both problems can be (and have been) solved, however. For example, see Erlang EEP 20. I use this technique in the simple-atom package. It uses unsafePerformIO under the hood, but only in (hopefully) rare cases. It could still use some help from the GC to perform an optimisation similar to indirection shortening. It also uses quite a few IORefs internally which isn't too great for performance and memory usage.

In summary, it can be done but implementing it properly is non-trivial. Compiler writers always weigh the power of a feature against its implementation and maintenance efforts, and it seems like first-class symbols lose out on this one.

Why to use LISP today, when there are Scala, Erlang, Haskell etc

First, Lisp in general and Common Lisp in particular is more "multi-paradigm" than "functional". From here on, I will mostly discuss Common Lisp (CL).

There isn't "one killer feature", though. CL is a programmable programming language, making it easy to do meta-programming. That is, you can usually do "meta-level" things directly in the language instead of having to reach for a pre-processor that does textual transformation into your base language.

CL also has a very powerful object subsystem (CLOS), that has, so far, supported anything I've wanted to do with class hierarchies and/or methods.

As far as the syntax goes, I guess it's a bit different from the more classic syntax of the "Algol family" languages, but it's no more and possibly less weird than APL.

What is the difference between an atom and a symbol in Common Lisp?

In Common Lisp, atom is precisely defined as any object that is not a cons. See http://l1sp.org/cl/atom for more details.

I don't know about other languages in the Lisp family.

Behaviour of ruby with regards to numerical exactness (Scheme comparison)

Ruby's class Float is not exact. Class Integer on the other hand more or less "is" since it shifts seamlessly from Fixnum to Bignum

>> 1.4534346345235236346363574564356435
=> 1.45343463452352
>> 10**400
=> 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
>> 10.0**400
=> Infinity

It looks like others would love to see the test you are asking about, namely Numeric#exact? -- see this feature request.

Lisp Flavored Erlang - Messaging primitives

I'm not an LFE user, but there is a user guide in the source tree. From reading it I would guess it is something like this:

(let ((A 2))
(let ((Pid (spawn (lambda ()
(receive
(B (when (is_integer B))
(: io format "Added: ~p~n" (list (+ A B))))
(_ nan))))))
(! Pid 5)))

But I'm very likely to have made a mistake since I haven't even evaluated it in LFE.

Some questions of mine:

  • Is there a LET* form or is it behaving like one already?
  • Are guards called the more lispy is-integer and not is_integer as I wrote?

valid characters for lisp symbols

A detailed answer is a bit tricky. There is the ANSI standard for Common Lisp. It defines the set of available characters. Basically you can use all those defined characters for symbols. See also Symbols as Tokens.

For example

|Polynom 2 * x ** 3 - 5 * x ** 2 + 10|

is a valid symbol. Note that the vertical bars mark the symbol and do not belong to the symbol name.

Then there are the existing implementations of Common Lisp and their support of various character sets and string types. So several support Unicode (or similar) and allow Unicode characters in symbol names.

LispWorks:

CL-USER 1 > (list 'δ 'ψ 'σ)
(δ ψ σ)

Since everyone is coding Lisp and Erlang now, does that mean that we will see Forth coders in 2010?

If you want something Forth-like, you would much better be served by looking at the Factor programming language. It is concatenative, like Forth, and it also has a lot of modern properties and an efficient implementation that targets multiple platforms.



Related Topics



Leave a reply



Submit