Ok, let me clarify this for you. Warning: this is going to be rather Haskell centric. My argument isn't really about the syntax of the target language at all but the internal program representation in the compiler/interpreter.
Representing tree structures like abstract syntax trees is really easy and convenient in Haskell. The traditional example, curried λ-calculus with integers looks something like this:
data Expr =
Constant Int |
Identifier String |
Application Expr Expr |
Lambda String Expr
Haskell makes it easy to handle such tree structures using pattern matching. Of course it is at least as easy to represent s-expressions with a similar tree structure. However, there are multiple benefits of using a tree structure like that, for example you will get a compiler warning if you forget to handle one or more of the possible cases in a non-exhaustive pattern match.
It is also easier to write a parser and pretty printer (using Parsec and Pretty combinators) to convert that tree structure to/from human readable/writable strings (ie. program source code) than it is to convert s-expressions to such a tree structure.
So while using s-expressions is certainly possible, in a nicely typed language like Haskell, it will be nicer to have a proper syntax tree data structure and the advantages of s-expressions are not really there unless you are writing an interpreter for a homoiconic language like Lisp or Prolog.
The way I usually start a programming language project (I do lots of those) is writing "programs" by specifying syntax trees for test cases in Haskell using literals and then running them through the interpreter/compiler/type checker. Only when I have something that actually works (and developing and debugging by writing the tree structures becomes unwieldy), I start thinking about the syntax and write the parser and pretty printer.
So if you're using a host language other than Lisp to implement your compiler/interpreter and your source language isn't homoiconic, using s-expressions as your internal program representation might end up being unwieldy. The syntax of your source language is pretty much irrelevant in this discussion, but writing a proper parser to produce tree structure might make sense rather than reading s-expressions.
Does my explanation make sense to you? If you have any questions, don't hesitate to ask.
Here's a few of my toy language projects for your enjoyment, you'll find examples of the things discussed above there:
It is also easier to write a parser and pretty printer (using Parsec and Pretty combinators) to convert that tree structure to/from human readable/writable strings (ie. program source code) than it is to convert s-expressions to such a tree structure.
Well, parsing S-expressions is easy in most language. Unless somehow parsing an S-expression is hard in Haskell, it doesn't matter whether parsing easier. Normally, it just means both S-expressions and generic parsing is easy.
Your overall argument could better be written "S-expressions aren't part of the particular syntax transformation method I use." OK, sure.
An S-expression based interpreter is still pretty darned easy to write (unless Haskell has weird barriers that other language don't have, if so, it doesn't reflect well on Haskell).
> Well, parsing S-expressions is easy in most language. Unless somehow parsing an S-expression is hard in Haskell, it doesn't matter whether parsing easier. Normally, it just means both S-expressions and generic parsing is easy.
> An S-expression based interpreter is still pretty darned easy to write (unless Haskell has weird barriers that other language don't have, if so, it doesn't reflect well on Haskell).
Oh, I knew I was too Haskell-centric in my answer because you missed my point entirely.
You can write an S-expression parser in Haskell, it's just as easy as writing it in any other language. But this wasn't the point at all.
Using S-expressions as the program internal representation just doesn't make sense in Haskell. You can do it and you should do it if implementing a Lisp dialect or other homoiconic language, but in general, it's a lot better to build a proper tree structure. The major advantage of s-expressions in the Lisp world is not really the syntax, but it is an universal tree structure.
Same thing applies if your host language isn't Lisp and you have a proper abstract syntax tree structure (either enums + unions or a class hierarchy or whatever). You can parse s-expressions but you need an extra step to get into the tree structure you're going to be using internally.
Your impression that S-expressions are hard in Haskell is just incorrect, my point was that there are better facilities for tree structures and parsing than S-expressions are.
As I said first thing in the earlier post... my argument isn't really about syntax, it's about the internal program representation.
Representing tree structures like abstract syntax trees is really easy and convenient in Haskell. The traditional example, curried λ-calculus with integers looks something like this:
Haskell makes it easy to handle such tree structures using pattern matching. Of course it is at least as easy to represent s-expressions with a similar tree structure. However, there are multiple benefits of using a tree structure like that, for example you will get a compiler warning if you forget to handle one or more of the possible cases in a non-exhaustive pattern match.It is also easier to write a parser and pretty printer (using Parsec and Pretty combinators) to convert that tree structure to/from human readable/writable strings (ie. program source code) than it is to convert s-expressions to such a tree structure.
So while using s-expressions is certainly possible, in a nicely typed language like Haskell, it will be nicer to have a proper syntax tree data structure and the advantages of s-expressions are not really there unless you are writing an interpreter for a homoiconic language like Lisp or Prolog.
The way I usually start a programming language project (I do lots of those) is writing "programs" by specifying syntax trees for test cases in Haskell using literals and then running them through the interpreter/compiler/type checker. Only when I have something that actually works (and developing and debugging by writing the tree structures becomes unwieldy), I start thinking about the syntax and write the parser and pretty printer.
So if you're using a host language other than Lisp to implement your compiler/interpreter and your source language isn't homoiconic, using s-expressions as your internal program representation might end up being unwieldy. The syntax of your source language is pretty much irrelevant in this discussion, but writing a proper parser to produce tree structure might make sense rather than reading s-expressions.
Does my explanation make sense to you? If you have any questions, don't hesitate to ask.
Here's a few of my toy language projects for your enjoyment, you'll find examples of the things discussed above there:
https://github.com/rikusalminen/funfun LLVM compiler for a toy functional programming language (with type inference!)
https://github.com/rikusalminen/slolog Prolog-esque logic programming language