Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> So the very first thing you do with your macro powers, and pretty much the only useful thing you can do, is break s-expressions. Once the first macro is written you can no longer assume that inputs to macros will be s-expressions with the function at the beginning and arguments following. Every future macro must account for every previous macro. The more you use the capability to manipulate code as data gracefully, the less graceful it becomes.

You should check out Racket's macro system. It's a lot more sophisticated than Common Lisp's. Common Lisp macros are to C (e.g., gensym is a macro-level malloc, you need to manually destructure S-expressions, etc.) as Racket macros are to ML and Haskell (syntax objects are aware of which variables are in scope, so automatic fresh name generation is possible; user-defined syntax classes and patterns let you process arbitrarily complicated structures in a sane way, etc.). If you like the idea of metaprogramming, but `defmacro` left you with a bad taste in the mouth, Racket is totally the language for you.

> First-class functions are a much more coherent, consumable way of using code-as-data.

First-class functions are easier to use than macros, but they are not “code as data”. Furthermore, “code as data” itself is only true with a caveat: the full version is ”code in an object language is data in the metalanguage”, which is obvious to anyone who has written a compiler. Of course, macros make it easy to use Lisp as its own metalanguage, but there's still a phase distinction between macro-expansion time and when the generated code is actually used.

> And to be honest, other languages have done a lot better things with the functional programming aspects of Common Lisp.

Common Lisp is a ridiculously powerful language, but it isn't a functional language. It fails to meet the zeroth nonnegotiable requirement in a practical functional language, namely, a notion of compound value: https://news.ycombinator.com/item?id=12199981



>You should check out Racket's macro system. It's a lot more sophisticated than Common Lisp's. Common Lisp macros are to C (e.g., gensym is a macro-level malloc, you need to manually destructure S-expressions, etc.) as Racket macros are to ML and Haskell (syntax objects are aware of which variables are in scope, so automatic fresh name generation is possible; user-defined syntax classes and patterns let you process arbitrarily complicated structures in a sane way, etc.). If you like the idea of metaprogramming, but `defmacro` left you with a bad taste in the mouth, Racket is totally the language for you.

Better yet, check out some other schemes. ir, er, and sc macros has the raw procedurual power of defmacro, but with the hygene and safety of syntax-case/syntax-rules, without the declarative syntax of syntax-rules, and the disadvantages of syntax-case (stupidly complex, breaking the standard macro abstraction with syntax/datum distinctions, etc.).

Given, syntax-case has some advantages, but I don't think it carries its own weight from a programmer's perspective.


> You should check out Racket's macro system. It's a lot more sophisticated than Common Lisp's. Common Lisp macros are to C (e.g., gensym is a macro-level malloc, you need to manually destructure S-expressions, etc.) as Racket macros are to ML and Haskell (syntax objects are aware of which variables are in scope, so automatic fresh name generation is possible; user-defined syntax classes and patterns let you process arbitrarily complicated structures in a sane way, etc.).

Agreed. I did play around with this part of Racket quite a bit and I'm convinced that it's the best system if I wanted to create a domain-specific language. But it still runs into the problem where you're defining a new language with new syntax, which forces you to define even more new language with more syntax in order to make that language useful. It's the best way to build a DSL

But given we already have a pretty good multi-purpose language with a lot of work put into it (Racket) the number of situations where it's worthwhile to create an equally-well-thought-out DSL is pretty low. Racket makes it easier, but it's still not easy. Add to this the fact that other people are going to write half-assed DSLs in my code, the net tradeoff is still usually negative, even with Rackets clearly superior macro system.

> Furthermore, “code as data” itself is only true with a caveat: the full version is ”code in an object language is data in the metalanguage”, which is obvious to anyone who has written a compiler.

Uh, I've written a compiler and that's not obvious.

If you want to disagree with me on what "code as data" means, you're welcome to do so. As long as you understood what I said I don't care which words got me there.

> Common Lisp is a ridiculously powerful language, but it isn't a functional language. It fails to meet the zeroth nonnegotiable requirement in a practical functional language, namely, a notion of compound value: https://news.ycombinator.com/item?id=12199981

How about we assume when I said "functional programming" I'm using the Wikipedia definition[1].

[1] https://en.wikipedia.org/wiki/Functional_programming


> Racket makes it easier, but it's still not easy. Add to this the fact that other people are going to write half-assed DSLs in my code, the net tradeoff is still usually negative, even with Rackets clearly superior macro system.

You have a point there. In any case, metaprogramming shouldn't be an everyday activity.

> If you want to disagree with me on what "code as data" means, you're welcome to do so.

What I mean by “code in an object language is data in the metalanguage” is that, in the (meta)language in which you're writing a compiler or interpreter, the (object language) program that you're processing is represented as a data structure (say, as a syntax tree). It's just a triviality, I'm not saying anything really deep.

From this point of view, it should be clear that a macro system is essentially a language-integrated facility for writing compiler plugins.

> How about we assume when I said "functional programming" I'm using the Wikipedia definition[1].

My definition of functional programming is “using (procedures that compute) mathematical functions whenever possible”. A mathematical function is a mapping from values to values, so if a language doesn't have a good notion of (possibly compound) value, then you're going to run into trouble writing procedures that compute mathematical functions.

EDIT: The Wikipedia definition essentially agrees with me.

“In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions [emphasis mine] and avoids changing-state and mutable data.”

And a mathematical function is a mapping from values (in a domain) to values (in a codomain). Again, quoting Wikipedia[0]:

“A function can be defined by any mathematical condition relating each argument (input value) to the corresponding output value.”

[0] https://en.wikipedia.org/wiki/Function_(mathematics)#Specify...


> What I mean by “code in an object language is data in the metalanguage” is that, in the metalanguage in which you're writing a compiler or interpreter, the object language program that you're processing is represented as a data structure (say, as a syntax tree). It's just a triviality, I'm not saying anything really deep.

I understood what you were saying, and didn't ask for an explanation. I just don't see why you felt the need to correct me on my calling first=class functions "code as data" and substitute your own definition that had nothing to do with what I was saying.

> My definition of functional programming is “using (procedures that compute) mathematical functions whenever possible”. A mathematical function is a mapping from values to values, so if a language doesn't have a good notion of (possibly compound) value, then you're going to run into trouble writing procedures that compute mathematical functions.

Again, I didn't ask what your definition was, because it wasn't relevant to the conversation.

You're basically interrupting me to tell me I'm not using the same definitions of words as you are, and it's not particularly endearing. If you don't understand what I'm saying, I'll be happy to explain. If you do, however, understand what I'm saying well enough to correct me on my usage of the English language, then my usage of the language has been clear enough for my goals, so I wouldn't be interested in your corrections even if they represented HN's common usage (which they don't).


> I just don't see why you felt the need to correct me on my calling first=class functions "code as data"

Because first-class functions aren't “code as data”. When you have a first-class function, the only thing you can do with it is call it. If it were a data structure, you could analyze its constituent parts.

> You're basically interrupting me to tell me I'm not using the same definitions of words as you are,

The Wikipedia article you linked says:

“In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions [emphasis mine] and avoids changing-state and mutable data.”

The emphasized part is just what I said. That a mathematical function is a mapping from values to values is unquestionable - it's not something I'm saying, it's a mathematical fact. So if you can't express compound values, you're limited to a world where mathematical functions can only manipulate primitive values.


> Because first-class functions aren't “code as data”. When you have a first-class function, the only thing you can do with it is call it. If it were a data structure, you could analyze its constituent parts.

There are lots of ways in which you can treat a first class function as data, even if it can't be treated 100% equivalently to data in every single situation. You can, for example, pass it to other functions like data. Sure, most languages don't let you inspect the internals, but that's only one way of treating code as data.

> The emphasized part is just what I said. That a mathematical function is a mapping from values to values is unquestionable - it's not something I'm saying, it's a mathematical fact. So if you can't express compound values, you're limited to a world where mathematical functions can only manipulate primitive values.

I said, "And to be honest, other languages have done a lot better things with the functional programming aspects of Common Lisp." Common Lisp isn't a purely functional language, but it does have functional aspects. Note in the article I quoted, that Common Lisp is the first language listed in the "prominent programming languages which support functional programming such as" section.

You're literally not even disagreeing with me, you're just defining "functional programming language" as "purely functional programming language", when I literally never even called Common Lisp a functional programming language.

Insisting on your definitions instead of trying to understand what I said doesn't make me want to engage with you further.


> There are lots of ways in which you can treat a first class function as data, even if it can't be treated 100% equivalently to data in every single situation. You can, for example, pass it to other functions like data.

Strictly speaking, you can't pass functions as data. You can only pass thunks that, when forced, yield functions. A thunk is data, but a function is a computation. Computations are “too active” to be stored or passed around unthunked. The technical details are here: http://www.cs.bham.ac.uk/~pbl/cbpv.html, http://www.cs.bham.ac.uk/~pbl/papers/. (I am not the owner of the website, just in case.)

> Sure, most languages don't let you inspect the internals, but that's only one way of treating code as data.

What are the others?

> You're literally not even disagreeing with me, you're just defining "functional programming language" as "purely functional programming language",

How did you conclude that? I never said anything of the sort. What I said is “functional programming is programming with procedures that compute mathematical functions whenever possible”. Pure functional programming imposes further requirements, like effect segregation (as in Haskell) or even the total absence of effects (obviously unsuitable for a general-purpose language). FWIW, I'd count ML, Racket, Clojure and Erlang as functional languages.

> when I literally never even called Common Lisp a functional programming language.

You said Common Lisp has “functional aspects”. Well, closures make a language higher-order, but so do Java-style objects! For a language to be called “functional”, however, it has to make functional programming actually pleasant. I showed one fundamental limitation of Common Lisp in this regard: you can't define functions that take or return compound values, because Common Lisp doesn't have compound values in the first place.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: