proj-oot-ootMetaprogrammingNotes2

Difference between revision 49 and current revision

No diff available.

http://stackoverflow.com/questions/tagged/homoiconicity

---

" You might already have a language in mind that you want to use. Technically speaking, mal can be implemented in any sufficiently complete programming language (i.e. Turing complete), however, there are a few language features that can make the task MUCH easier. Here are some of them in rough order of importance:

    A sequential compound data structure (e.g. arrays, lists, vectors, etc)
    An associative compound data structure (e.g. a dictionary, hash-map, associative array, etc)
    Function references (first class functions, function pointers, etc)
    Real exception handling (try/catch, raise, throw, etc)
    Variable argument functions (variadic, var args, splats, apply, etc)
    Function closures
    PCRE regular expressions

In addition, the following will make your task especially easy:

    Dynamic typing / boxed types (specifically, the ability to store different data types in the sequential and associative structures and the language keeps track of the type for you)
    Compound data types support arbitrary runtime "hidden" data (metadata, metatables, dynamic fields attributes)

Here are some examples of languages that have all of the above features: JavaScript?, Ruby, Python, Lua, R, Clojure. " -- https://github.com/kanaka/mal/blob/master/process/guide.md

https://news.ycombinator.com/item?id=9123065 'codygman' compares various language implementations of mal using 'cloc' to see which implementations were shortest.

the shortest were:

CoffeeScript?, Clojure, OCaml, Ruby, Python

the full table was (omitting support files) was:


    Language                     files          blank        comment           code
    -------------------------------------------------------------------------------
    Rust                            17            294            155           3919
    C                               18            446            383           3356
    C#                              19            472            235           3314
    Visual Basic                    17            322            131           2945
    Java                            17            322            134           2872
    Go                              17            299            147           2636
    Bourne Shell                    16            304            241           2308
    Perl                            19            263            165           2167
    Haskell                         17            328             99           1951
    MATLAB                          25            176            114           1880
    Javascript                      23            263            145           1873
    PHP                             17            242            118           1791
    Scala                           16            219            113           1731
    Lua                             18            227             87           1715
    Python                          18            218            143           1393
    Ruby                            17            165             87           1219
    OCaml                           15            105             98           1154
    Clojure                         17            247            117           1011
    CoffeeScript                    17            193            126            989

(Forth is missing)

the thread https://news.ycombinator.com/item?id=9121842 discusses this and gives reasons why some of the longer implementations could have been shorter.

---

http://spinroot.com/gerard/pdf/P10.pdf rule #5 has an interesting suggestion in the context of syntax for writing assertions, where 'e' is an assertion condition (a boolean expression): "The syntax #e turns the assertion condition e into a string that is printed as part of the error message."

eg you should be able to pass around 'expressions' (which are raw ASTs, which can contain unbound variables, not necessarily written as anonymous functions with an explicit varspec), and you should be able to serialize these into strings

---

one Java metaprogramming interface in Java core:

http://docs.oracle.com/javase/7/docs/api/javax/annotation/processing/Processor.html

---

this guy claims macros are not a readability problem:

orthecreedence 2 hours ago

After programming in CL for several years, I can honestly say that when encountering new syntax that people have introduced in an app, it's no harder to follow along with it by reading the macro definition than encountering an unknown function. And when in doubt, you can just macroexpand the syntax and see what it's doing under the hood.

reply

--

kevin_thibedeau 8 hours ago

I recently needed to make a cross platform assembler for a niche 8-bit micro that only has DOS tooling. After cloning it in a small bit of Python I was looking for a simple way to extend it with macros. It turns out that m4 works really well as an asm macro language and I now have advanced facilities like a code generating expression evaluator and high level control flow statements without any significant change to the core assembler. It blows the doors off what contemporary assemblers (nasm, masm, tasm) can do and hearkens back to the high point of high level assemblers when they peaked in the 70s.

reply

---

make certain vars 'fair game' for (anaphoric/non-hygenic) macros? eg x, y, z, x1, x2, etc? or mb something more universal, such as 'anything with a _ suffix' or 'anything ending in X'?

---

extend_syntax eg

http://www.cs.rit.edu/~rpj/courses/plc/lab4/lab4df.html?lab47.html#Q12?lab4ans.html#A12

---

http://scienceblogs.com/goodmath/2007/12/17/macros-why-theyre-evil/ has a good example of scheme define-syntax/syntax-rules, it looks quite good:

(define-syntax replace (syntax-rules (initially with until just-before) ((replace initially <value> with <newvalue> until <done>) (let loop (( <value>)) (if <done> (loop <newvalue>)))) ((replace initially <value> with <newvalue> until just-before <done>) (let loop ((old #f) ( <value>)) (if <done> old (loop <newvalue>)))) ((replace <var1> <var2> initially <value1> <value2> with <newvalue1> <newvalue2> until <done>) (let loop ((<var1> <value1>) (<var2> <value2>)) (if <done> (list <var1> <var2>) (loop <newvalue1> <newvalue2>))))))

---

bentcorner 4 hours ago

I find it frustrating that languages features work actively against you when you're trying to understand something.

Wide inheritance and macro usage are probably the worst. Good naming can aid understanding, but basic things like searchability are harmed by this.

Of those two, macros are the most trouble. You can't take anything for granted, and must look at every expression with an eye for detail. Taking notes becomes essential.

reply

---

http://andymeneely.github.io/squib/

a Ruby DSL for prototyping card and board games

discussion: https://news.ycombinator.com/item?id=9796708

(a quick skim saw nothing in the HN discussion relating to programming language/DSL design)


JSR 308 type annotations:

"What are type annotations?

The Java 6 annotation syntax is useful but limited. The Type Annotations syntax permits annotations to be written in more places, such as generic type arguments: List<@NonNull? Object>. jsr Programmers can use type annotations to write more informative types, and then tools such as type-checkers can detect and prevent more errors. " -- http://types.cs.washington.edu/jsr308/

http://types.cs.washington.edu/jsr308/specification/java-annotation-design.html

---

"

Annotations

Most projects eventually need to interact with the programmer. You’ll eventually wish for annotations: some way to convey extra information from the program to your LLVM pass. There are several ways to build up annotation systems:

    The practical and hacky way is to use magic functions. Declare some empty functions with special, probably-unique names in a header file. Include that file in your source and call those do-nothing functions. Then, in your pass, look for CallInst instructions that invoke your functions and use them to trigger your magic. For example, you might use calls like __enable_instrumentation() and __disable_instrumentation() to let the program confine your code-munging to specific regions.
    If you need to let programmers add markers to function or variable declarations, Clang’s __attribute__((annotate("foo"))) syntax will emit metadata with an arbitrary string that you can process in your pass. Brandon Holt again has some background on this technique. If you need to mark expressions instead of declarations, the undocumented and sadly limited __builtin_annotation(e, "foo") intrinsic might work.
    You can jump in full dingle and modify Clang itself to interpret your new syntax. I don’t recommend this.
    If you need to annotate types—and I believe people often do, even if they don’t realize it—I’m developing a system called Quala. It patches Clang to support custom type qualifiers and pluggable type systems, à la JSR-308 for Java. Let me know if you’re interested in collaborating on this project!

I hope to expand on some of these techniques in future posts. " -- http://adriansampson.net/blog/llvm.html

---

mb read:

---

(at least) two kinds of 'macros':

'reader macros' applied to string text before (or during) the transformation to AST

ordinary lisp-style macros, which are transforms of AST->AST

---

mike_hearn 23 days ago

parent on: Ask HN: Has anyone here programmed in Kotlin? What...

Do you actually know Kotlin? It has many features for DSL support, including operator overloading (though not the ability to invent random new operators fortunately), infix notation, a form of exhaustive pattern matching, extension functions, annotation processing, inlineable anonymous extension functions which sounds bizarre but actually is key to defining many DSLs in the language, and so on. It's a different approach to Scala but to say there's no way to do DSLs isn't right.

As an example, check out funKtionale. It's a library that adds various functional features like function currying and partial application to Kotlin, entirely by using its DSL features.


pron 22 days ago

Or our Kotlin actor API: http://blog.paralleluniverse.co/2015/06/04/quasar-kotlin/

---

compiler warning (or scripting level requirement) if anything is REdefined with a macro (esp. if it's a language-level or stdlib-level thing)

---

so {} are like "quote". is "!" like "unquote" or is it "pass on the sideeffects"? in some sense i guess sideeffects are themselves "quoted" in a functional reactive stream. preefix vs postfix "!" to apply to the thing itslf, or to its first return value? what about impure reads (not sideeffectful)? what about sideeffecttful reads, the rest of the stream cannot be produced until these are executed against some environment? hmm.. "!" as meaning "excute against the current environment"; mb "f!e" for "exec f against environ e"; exec means not just reading from the environ, but also writing to it.. environ can "catch" these writes (and reads!) if it wants, in order to produce a functional reeactive stream instead ! =s like eval, not anttiquote; and this is not quite like quote/eval b/c blocks are opaque, they are not ASTs (and not source code text strings) that are preserved until runtime (or th end of this 'stage' at least, in the case of g compiletime macros) for us in metaprogramming

3 levels/stages: source cod text string, AST, opaque block/fn text --(parse (lex and parse))--> AST --compile--->block--eval/exec-->result and side effects (mb 'eval' to a stream and 'exec' to adtually do it? wher 'eval' is just 'exec' to an environ that catches eeverything?)

---

jerf 14 hours ago

... Haskell's great support for domain-specific languages. This is both in the sense of languages that are really "just" Haskell APIs but work in such an different manner that they are quite different, and in the sense of writing a full compiler [1] (or any subset of a compiler) for a new language.

For an example of the former, one of my favorite demonstrations is the "probability monad", in which one ends up with a sort of sublanguage inside of Haskell that is probability aware: http://www.randomhacks.net/2007/02/22/bayes-rule-and-drug-te... [2] If you want to start to really grasp the power of Haskell, you could do worse than meditate on how you'd implement that in your $FAVORITE_LANGUAGE... and then, most likely, burst into tears when someone shows you just how much of Haskell still works, unchanged, even in that environment (e.g., everything in Control.Monad), rather than having to be implemented in a parallel type hierarchy (or, put another way, producing a new "color" of functions [3]).

While I doubt they use something exactly like that, it's not hard to see how that sort of thing could make it fairly easy to write spam classification code with a lot of the basic plumbing abstracted away entirely.

There's a lot of examples of the latter, but I'm not sure of a really solid open source example to point at. I know it's a common move in industry, though.

For bonus points, these things compose together just fine; you could easily take the probability monad and build a new language on top of it.

[1]: And I do mean real compiler, by anybody's definition, all the way down to assembler if you like. Haskell's a pretty good base language for that sort thing.

[2]: Or see http://blog.plover.com/prog/haskell/probmonad-refs.html http://blog.plover.com/prog/haskell/probmonad-refs.html

[3]: http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y... http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/

---

cody http://docs.quantifiedcode.com/patterns/index.html

---

great example of a DSL that we'd want to enable:

http://daveyarwood.github.io/alda/2015/09/05/alda-a-manifesto-and-gentle-introduction/

the discussion mentions other more complex music languages:

https://news.ycombinator.com/item?id=10177716

including

MML, Lilypond, ChucK?, Csound, Supercollider, Max/MSP, PD, Faust, Overtone (Supercollider + Clojure) and if you want less "note C" and more abstract: Music-Suite ( http://music-suite.github.io/docs/ref ) and Overtone ( http://overtone.github.io ), along with the Leipzig library, which adds functional programming abstractions for use with Overtone ( https://github.com/ctford/leipzig )

---

note: i looked in Rust to see what representation they express their borrow checker in. I think this sort of thing would be in the source code subdirectory a common standard for program evaluation is 95% confidence with a sampling error of ± 5%. In English that means that you believe that 95 percent of the time the results from your sample, would be off by no more than 5% as compared to the results you would have gotten if you had collected data from everyone. (librustc is the main compiler, and i think 'middle' is the stuff that is neither frontend (text -> AST) nor backend (-> target code)).

It appears to just be in ordinary Rust.

The most mentioned seem to be MML, Lilypond, Overtone, ChucK?