Live data from Hacker News

Not Lisp again (2009)

funcall.blogspot.com

61–70 of 276 posts

Re: Not Lisp again (2009)

#61

The objections the author had way back in the day are no longer the objections programmers of mainstream languages have to Lisp today. Today the objections I hear are more along the lines of: 1 - All those parenthesis. (Still a top objection) 2 - Lisp doesn't look like or work like what I'm used to. 3 - Lisp doesn't have as many libraries as the most popular mainstream programming languages. 4 - There aren't nearly a…

6: Lisp is ancient. It really is.

Also on the front page of HN today: SQL is 43 years old.

So, exactly what is the problem with being ancient? Nobody objects to SQL on those grounds. So I suspect that 6 is really "I want to object, and I found a plausible-sounding reason to do so". (It could be people who actually evaluate stuff based on how new it is. But I'd prefer to think that there aren't people who really evaluate languages that way...)

Re: Not Lisp again (2009)

#62

Earlier quoted context omitted.

Well, in 1992 (IIRC), I did a numerical integration in C. For a "first class function", it just took a function pointer. That approach would have been available in C in 1983...

That's not really the same thing at all. What's missing is the ability to close over values, which is a key part of first-class functions. You could not re-create the original example in C, which is to return a new function. You'd have to return some sort of object that keeps a reference to the function pointer, and provides a special mechanism for calling it, i.e. a poor man's closure.

> What's missing is the ability to close over values, which is a key part of first-class functions.

This is not true. You can have first-class function which are not closures: every dynamically-scoped Lisp works that way, see Emacs Lisp without `lexical-binding: t` and the `lexical-let` implementation.

Re: Not Lisp again (2009)

#63

I'm having trouble understanding why the derivative example was so impressive to the author. Can someone explain? It seems trivial to do in any language where functions are first class citizens.

Probably hadn't thought of passing a subroutine address to a routine in z80 machine language he was familiar with?

Yeah, I understand why this is difficult if not impossible to do in BASIC, but functions are first-class in just about any assembly language.

That said, remember that the author is talking about his experience as a first-year college student. When I went to college, I had extensive experience with BASIC (GW, Q, TI, and a bit of Visual), a year of C++ in high school, and a few vague attempts at assembly (386 and Z80) that got me nowhere. The idea of function pointers was one of those scary advanced C things that I had picked up to avoid, and certainly the corresponding assembly concept didn't occur to me. Of course I knew you could jump to a computed value, but the mindset was foreign. I sort of knew that this was doable with objects in C++ - I could define a base class with an abstract method calculate(), and make a Cube subclass - but thinking of functions as first-class still wasn't obvious.

Re: Not Lisp again (2009)

#64
post #19
post #14

Yeah, but it's goddamned ugly and unreadable. It considers repetition to be a design feature . If you're going to sell people on the benefits of functional programming, I think you should really be pushing more for SML or Haskell or something like that.

Repetition of what? I don't see any repetition on those snippets... What are you referring to?

Everything is nested in the form of (fn arg arg) and relies solely on indentation to create some modicum of differentiation.

Re: Not Lisp again (2009)

#65

The objections the author had way back in the day are no longer the objections programmers of mainstream languages have to Lisp today. Today the objections I hear are more along the lines of: 1 - All those parenthesis. (Still a top objection) 2 - Lisp doesn't look like or work like what I'm used to. 3 - Lisp doesn't have as many libraries as the most popular mainstream programming languages. 4 - There aren't nearly a…

To be fair, the one time I decided to do something biggish and kinda important in Lisp, I stopped at #3. Not exactly because "it does not have many libraries", but because "library for X, Y and Z are not there and I don't want to write them". Oddly, Haskell has a similar problem, but going through C code by the FFI does not feel like a problem. I don't think why it does in Lisp, it may be just a matter of better docu…

I'm working on FFI for TXR Lisp. Until this morning, it was in the stage of "collection of working API functions, written in C, exposed with Lisp bindings".

I have almost everything I want working, including callbacks. There is a decently rich type system which supports pointers that annotate data passing directions for automatic malloc and free, and this works in both directions: out calls and callbacks (closures).

I support by-value passing and returning of structures. And also of C arrays! Even though C doesn't have by-value arrays. You see, my FFI type (array 42 int) is actually equivalent to the C type struct __anon { int __anon[42] } and not the C type int [42].

Just this morning, between 6 and 6:45 a.m I wrote a partial implementation of the FFI declaration language. Here is what it looks like, with actual output:

Load the under-construction FFI macros:

  (load "ffi")
Define a show macro for showing an an expression and its value as expr -> value:

  (defmacro show (expr)
    (let ((val (gensym)))
      ^(let ((,val ,expr))
         (format t "~s -> ~!~s\n" ',expr ,val)
         ,val)))
  
Now the FFI test. Some structures we need, and declarations of FFI typedefs for them giving type info. The above typedefs aren't required; we we could just write out the (struct ...) syntax inline in the later FFI declarations.

  ;; Lisp struct representing C struct ldiv
  (defstruct ldiv nil quot rem)
  ;; FFI typedef for it
  (deffi-type ldiv (struct ldiv (quot long) (rem long)))
  
  (defstruct pipe nil rfd wfd)
  (deffi-type pipe (struct pipe (rfd int) (wfd int)))
  
  (defstruct dirent nil
    ino off)
  (deffi-type dirent (struct dirent
                             (ino int)
                             (off int)
                             (nil (array 62 int))))
                             ;; the member named nil here is anonymous padding
Now, declare some FFI functions. Very succinct and expressive:

  (with-dyn-lib (libc "libc.so.6")
    (deffi c-abs "abs" int (int))
    (deffi ldiv "ldiv" ldiv (long long))
    (deffi c-getenv "getenv" (buf 3) (str))
    (deffi c-getenv-2 "getenv" str (str))
    (deffi wcslen "wcslen" ulong (wstr))
    ;; Two ways to call pipe: using a pointer to two-int
    ;; structure, or an int[2] array. They are binary identical.
    (deffi c-pipe "pipe" int ((ptr-out pipe)))
    (deffi c-pipe-2 "pipe" int ((ptr-out (array 2 int))))
    (deffi c-read "read" int (int buf int))
    (deffi c-read-str "read" int (int (ptr (array 10 char)) int))
    (deffi opendir "opendir" cptr (str))
    (deffi readdir-r "readdir_r" int (cptr (ptr-out dirent) (ptr-out cptr))))

Finally, the test: with two lines of interactive input from the TTY to satsify two read calls from file descriptor 0. For both of these, I enter the text abc[Enter]:

  (show (c-abs -42))
  (show (ldiv 47 6))
  (show (c-getenv "HOME"))
  (show (c-getenv-2 "HOME"))
  (show (wcslen "foo bar"))
  (let ((pi (new pipe)))
    (show pi)
    (show (c-pipe pi))
    (show pi))
  (let ((pi (vector 2)))
    (show pi)
    (show (c-pipe-2 pi))
    (show pi))
  (let ((buf (make-buf 5 #xa0)))
    (show buf)
    (show (c-read 0 buf (length-buf buf)))
    (show buf))
  (let ((str "xxxxxxxxxxx"))
    (show str)
    (show (c-read-str 0 str 10))
    (show str))
  
  (let* ((de (new dirent))
         (dp (cptr 0))
         (dir (show (opendir "."))))
    (show (readdir-r dir de dp))
    (show de)
    (show dp))
Output:

  (c-abs -42) -> 42
  (ldiv 47 6) -> #S(ldiv quot 7 rem 5)
  (c-getenv "HOME") -> #b'2f686f'
  (c-getenv-2 "HOME") -> "/home/kaz"
  (wcslen "foo bar") -> 7
  pi -> #S(pipe rfd nil wfd nil)
  (c-pipe pi) -> 0
  pi -> #S(pipe rfd 4 wfd 5)
  pi -> #(nil nil)
  (c-pipe-2 pi) -> 0
  pi -> #(6 7)
  buf -> #b'a0a0a0a0a0'
  abc  4
  buf -> #b'6162630aa0'
  str -> "xxxxxxxxxxx"
  abc  4
  str -> "abc\nxxxxxx"
  (opendir ".") -> #
  (readdir-r dir de dp) -> 0
  de -> #S(dirent ino 669944 off 1)
  dp -> #

Look what is happening in (c-read-str 0 str 10). We pass in a Lisp string which contains "xxxxx...". Magically, it becomes "abc\nxxxxxx..." after the read, as if we were working in C. This is because the type for the parameter (ptr (array 10 char)). The FFI framework recognizes arrays of char and makes them correspond to strings in a two-way manner.

Re: Not Lisp again (2009)

#66

The objections the author had way back in the day are no longer the objections programmers of mainstream languages have to Lisp today. Today the objections I hear are more along the lines of: 1 - All those parenthesis. (Still a top objection) 2 - Lisp doesn't look like or work like what I'm used to. 3 - Lisp doesn't have as many libraries as the most popular mainstream programming languages. 4 - There aren't nearly a…

Learning lisp or functional programming is more of a conceptual experience than a practical one. That is not to say it is not practical to learn lisp, but the state of the programming world as it is today makes it so that it is less practical to learn lisp than it is to learn about say something like java.

If you ever have to write JavaScript, a fairly practical and widespread language, the functional paradigm proves to be highly useful.

Re: Not Lisp again (2009)

#67

Earlier quoted context omitted.

Well, in 1992 (IIRC), I did a numerical integration in C. For a "first class function", it just took a function pointer. That approach would have been available in C in 1983...

If I am not mistaken, you will need to know the signature of the function. Even if you use void* you would need to know the amount of parameters that the function uses.

Sure. For numerical (one dimensional) integration, it took one (double) parameter. In 1992, C was getting to the point where I could make that type-safe. In 1983, not so much.

Re: Not Lisp again (2009)

#68
a lot of people handwave the parenthesis and prefix notation as something you get used to, but it really is the thing that I think most people can't get a handle on.

There's a reason why DSPs and languages that look like a real languages are sought after - it makes conversion between business logic/requirements to code easier.

It makes maintenance easier - it's easy to make sure you made the right changes when the changes to your code looks like the changes to your business logic.

Re: Not Lisp again (2009)

#69
post #38
post #30

Earlier quoted context omitted.

Lisp has the minimal syntax for lists: (a b c d) where other languages require separating commas, but Haskell has minimal syntax for function application: f x y z with no parenthesis needed. (Technically, this is a triple application ((f x) y) z, masked by the convention of application being a left-associative binary operator.)

How would you write f((x y)z) in Haskell?

f $ x y $ z

Basically, f $ x = f x and is applied with lowest operator precedence (0) and is right associative. Therefore, f $ x y $ z = f (x y $ z) = f((x y) z)

Re: Not Lisp again (2009)

#70

The objections the author had way back in the day are no longer the objections programmers of mainstream languages have to Lisp today. Today the objections I hear are more along the lines of: 1 - All those parenthesis. (Still a top objection) 2 - Lisp doesn't look like or work like what I'm used to. 3 - Lisp doesn't have as many libraries as the most popular mainstream programming languages. 4 - There aren't nearly a…

> The objections the author had way back in the day are no longer the objections programmers of mainstream languages have to Lisp today.

> Today the objections I hear are more along the lines of:

> 3 - Lisp doesn't have as many libraries as the most popular mainstream programming languages.

Amusingly one of the "objections" to Common Lisp when it was standardized was its absurdly large library. Things sure look different today.

I have to say though that many languages have a large number of libraries, the quality is, naturally, variable.+ The real library strength of a language is the power of its standard library. (Admittedly some of that can come via lore or acculturation, e.g. numpy. On the other hand, what legs will, say, react have? Who knows?)

+ no examples because I have no desire to insult anyone -- rather thanks for releasing your code!

Post reply on HN