7 ms·
How about "defining" homoiconic to mean that it's easy to manipulate programs? More specifically, say a programming language is homoiconic if (1) the standard l
by omaranto 7y ago
How about "defining" homoiconic to mean that it's easy to manipulate programs? More specifically, say a programming language is homoiconic if (1) the standard library for the language includes a parser, and (2) the parser returns data structures that are easily manipulated by functions in the standard library.
Point (2) can be achieved by having the parser return generic data structures such as lists, arrays and hashtables for which the standard library will probably have lots of convenient functions (this what Lisps do); or by having a dedicated AST data type, but including a bunch of AST manipulation functions in the standard library (like Template Haskell).
Of course, this isn't a very precise definition because it relies on the undefined terms "parse" and "easily manipulated". Does read_file_to_string() count as a (trivial) parser that produces strings, which can be easily manipulated by standard string functions? The idea is this doesn't count, but I'll leave the problem of defining what counts as a parser to other philosophers of programming languages. :)
- agumonkey 7y agoEasy is a side effect of the 'homo' part. You have one tool for any layer you want to manipulate.
- wruza 7y agoUnless I’m missing something, there should be (3) these structures can be passed for evaluation immediately at runtime, linked to that runtime, including access to current activation record’s data, global symbols and so on. Without that, (1,2) is simply an ast transformer, and as far as I understand it, doesn’t count. E.g. var st = new IfStatement({ cond: new ArgExpr(1), body: [ new AssignStatement({ dst: new LocalVar(‘a’), src: new Constant(5), }) ] }) st(true) console.log(a) // 5 Both code and data may be a list, but also may be a struct/array hierarchy. With modern attitude though, eval(str) and non-local goto would seem as cute kittens when compared to this power.
- tomp 7y agoThis is probably wrong. You’re basically saying that a statically compiled LISP isn’t homoiconic.
- omaranto 7y agoOh, you want the homoiconicity to serve a purpose. I guess that's reasonable. :) Although, I'd say that a dialect of Lisp with no runtime eval, but with compile time macros would still count as homoiconic and your suggested (3) doesn't seem to allow it.
- wruza 7y agoBut... macro compile time may be seen as just one pass of reading, evaling in almost the same runtime and saving an image for later reuse. If we got these (var st = x), (var y = 1), (function main), etc in column 1 and then just run one pass of js, we would be able to “compile” it, i.e. (exit (save (optimize (eval (read))))), and then run main() with no IfStatement defined. I mean, isn’t compile-time a little vague term when macros are ‘evaluated’ in essentially the same rich environment, and it’s only wanted coincidence that the point separating compile/run time is the same as the execution stage where no macros remain unevaluated? (ed: but still no io performed except for source codes and static resources) When we write say heavily templated and const_expr’ed C++ to output the results of these exprs, do we pretend that we then run a binary or that we ran a C++ program once and cached results in an executable format?
- wruza 7y ago(Follow up) the parser returns data structures that are easily manipulated by functions in the standard library But is there a limit on how powerful macro has to be? Think meta-meta-meta programming where a macro constructs a macro for own use somewhat imperatively. Like compile-time for another compile-time for... etc I mean, if we can easily manipulate code at compile-time AND have a macro evaluator at hand, then don’t we have enough time to do something maybe more ambitious than the resulting binary chunk would ever do?
- skissane 7y agoA language really should have some kind of quotation syntax, so rather than having to write out AST constructions by hand as in your example, you could instead do something like this: var st = @(arg1 | if (arg1) { a = 5 }) st(true) console.log(a) // 5 So that way you don't have to manually construct the AST objects like in your example, the language parser does it for you. The @(...) expression in the above code would be compiled to the same object construction expressions as in your example. That is getting much closer to LISP, while still working for non-LISP syntax languages. (My suggestion of @(args...|body...) as quote syntax is totally arbitrary and besides the point, pick something else if you don't like it.) Where it gets even more powerful, is where you have some kind of escape syntax inside the body of @(), that is evaluated immediately, and returns either a constant value, an identifier, or another AST object. (A constant value or an identifier would be wrapped in the appropriate AST node type.) That's basically LISP backquote/quasiquote.
- wruza 7y agoAgreed, since json is js’s POD, it would be better to write {jsonhere}, but I had a fear that I couldn’t type as much of plain json from my phone to include enough meaning. new SomeClass() expands into much more data, but is easier to tell. Edit: removed @ before {, was a mistake.
- skissane 7y agoYou could serialise a JavaScript AST into JSON format. But writing/editing code in that syntax would be unpleasant compared to writing/editing it in the normal JavaScript syntax. That's why my suggestion was having some syntax extension that lets you write JavaScript in its normal syntax, but instead of e.g. compiling an if(){} statement to some kind of test-and-jump opcode(s) in the underlying VM bytecode (or equivalent machine code if JITing), it would instead compile it to opcodes that would construct the corresponding AST. (That AST could be just plain-old-data ala JSON, or objects as in your original example.) (The point I'm making is nothing specific to JavaScript, just a general point that languages with very un-LISP-like syntax, such as those with C-derived or Algol/Pascal-derived syntax, could still implement facilities similar to backquote/quasiquote in LISP, just with some small but powerful syntax extensions.)
- ChrisSD 7y agoI think that including a parser in the standard library is either too high a bar or too low a bar, depending on the scope of the language's standard library. Either way it's somewhat arbitrary. It would perhaps be better to say that a (language idiomatic) parser is available for the language that is maintained and used by the core language team and easily importable by third parties. That way you avoid the minimal vs. batteries included standard library philosophical arguments.
- omaranto 7y agoThat's a good point. I'd get behind this amendment.
- r-w 7y agoThe word you’re looking for is “reflective”, not “homoiconic”.
- reikonomusha 7y agoI disagree. Reflective is more about querying runtime properties (like data types, structure of data structures, etc.) at runtime.
- kazinator 7y agoHow about leaving "homoiconic" as is, referring to a language that stores textual or tokenized function definitions that can be regurgitated, edited and re-applied? For instance, GNU Bash.