Hacker News

Top stories

Live mirror
30 storiesupdated just nowView source snapshot
  1. Android 17 is the first since 3.x to add new APIs without releasing to the AOSP(grapheneos.social ↗)
    267comments
  2. Cloudflare Quick Tunnels(cloudflare.com ↗)
    252comments
  3. Saving another 100TB of RAM(cloudflare.com ↗)
    45comments
  4. The Farnese letter(simonklee.dk ↗)
    5comments
  5. How to Write with an LLM(sockpuppet.org ↗)
    274comments
  6. Xcode 27.1 Beta Release Notes(developer.apple.com ↗)
    68comments
  7. Show HN: LiveWorld – Every 24/7 YouTube live camera on one globe(liveworld.info ↗)
    7comments
  8. Photon-Emission-Guided Laser Fault Injection Enables RP2350 Secure Debug(ledger.com ↗)
    54comments
  9. Show HN: Cactus Needle 3: 8-29MB automation models can match DeepSeek V4 Flash(cactuscompute.com ↗)
    78comments
  10. The first new cat species discovered in 100 years(nationalgeographic.com ↗)
    62comments
  11. Cache-to-Cache: Direct Semantic Communication Between LLMs (2025)(arxiv.org ↗)
    12comments
  12. OpenJev(openjev.com ↗)
    247comments
  13. How OpenAI Used Its Own LLMs to Design Its Jalapeño Chip(ieee.org ↗)
    59comments
  14. Claude Code now reads AGENTS.md if there is no Claude.md(claude.com ↗)
    186comments
  15. LispBM is a concurrent Lisp for microcontrollers with message passing(lispbm.com ↗)
    1comments
  16. Cyclomatic Complexity in C#(ndepend.com ↗)
    15comments
  17. Why building a Rust LSP is hard(rust-glancer.github.io ↗)
    discuss
  18. Minimal Phone 2(minimalcompany.com ↗)
    190comments
  19. Warez: The Infrastructure and Aesthetics of Piracy (2021)(archive.org ↗)
    29comments
  20. Two parallel neural ectoderm progenitors contribute to the developing brain(newscientist.com ↗)
    56comments
  21. The Implications of Linguistic Illegibility for LLM Security(arxiv.org ↗)
    20comments
  22. Inside ZCode: Silently uploading your Git history to the cloud(ferstar.org ↗)
    93comments
  23. C++26: Trivial infinite loops are no longer undefined behaviour(sandordargo.com ↗)
    203comments
  24. Alibaba open-sources AI model that can detect cancer and nearly 150 conditions(scmp.com ↗)
    4comments
  25. How SpaceX streamlined the Raptor engine(construction-physics.com ↗)
    42comments
  26. Column built an issuer processor from scratch(column.com ↗)
    5comments
  27. A search-and-inference database from scratch in pure Zig(antfly.io ↗)
    16comments
  28. I vibed a proof of Conway's conjecture(overreacted.io ↗)
    186comments
  29. Size-Specialized Memory Allocation(go.dev ↗)
    3comments
  30. North Korean nuclear test sets off years of earthquakes(science.org ↗)
    152comments

The Meta-Circular Evaluator (2016)

40 pointsby 7y agorooijakkers.software
7 comments
7y agoHN ↗

That's a very interesting topic and nice write-up, thank you for sharing!

For comparison, here is a small interpreter for a subset of Prolog, written in Prolog:

    mi([]).
    mi([G|Gs]) :-
            clause_(G, Body),
            mi(Body),
            mi(Gs).

This states that: First, the empty conjunction (of goals) is true, and second, if there is at least one goal left, than the conjunction is true if there is a suitable clause whose body evaluates to true, and the same holds for all remaining goals.

For example, we can define append/3 as follows in this representation:

    clause_(append([], Ys, Ys), []).
    clause_(append([X|Xs], Ys, [X|Zs]), [append(Xs,Ys,Zs)]).

and then evaluate it with our interpreter:

    ?- mi([append([a,b,c], [d,e], Ls)]).
    Ls = [a, b, c, d, e].

Characteristically, it also works in other directions. For example:

    ?- mi([append([a,b,c], Rs, [a,b,c,d,e])]).
    Rs = [d, e].

and also:

    ?- mi([append(As, Bs, [x,y])]).
    As = [],
    Bs = [x, y] ;
    As = [x],
    Bs = [y] ;
    As = [x, y],
    Bs = [] ;
    false.

Now the point: The clauses of the meta-interpreter itself can also be stated in this form, namely as:

    clause_(mi([]), []).
    clause_(mi([G|Gs]), [clause_(G,Body),mi(Body),mi(Gs)]).

Now we only have to define what clause_/2 means, which we can do with:

    clause_(clause_(G, Body), []) :- clause_(G, Body).

And now we can use our meta-interpreter to evaluate its own code as it interprets a program, arbitrarily deeply layered:

   ?- mi([mi([mi([append([a,b,c],[d,e],Ls)])])]).
   Ls = [a, b, c, d, e].

Hence, Prolog admits a meta-circular evaluator in at most 5 lines of code. With a better representation for clauses (using list differences), you can reduce the interpreter to 4 lines of code and at the same time make it tail-recursive.

7y agoHN ↗

Nice- but why only half a meta-interpreter? A complete Prolog meta-interpreter should still be able to interpret iself, no?

7y agoHN ↗

Yes, a complete Prolog meta-interpreter would definitely be meta-circular.

However, it is quite hard to write an interpreter for complete Prolog, both in Prolog and also in other languages. Take for example handling of !/0. It is not easy to add handling of !/0 to the meta-interpreter I showed, especially if you want to retain meta-circularity. You cannot simply use a !/0 on the meta-level, because that does not get cut transparency right. One way to express !/0 on the meta-level is to use catch/3 and throw/1. However, if you do this and want to retain meta-circularity, then you must also handle catch/3 and throw/1 correctly, and soon you really will have to handle almost the entire language, which is quite complex to implement even though it has a very simple syntactic structure.

With "cut transparency", I mean that the scope of !/0 depends on its context. For example, we have:

    ?- member(X, [a,b,c]), ( true -> ! ).
    X = a.

and on the other hand:

    ?- member(X, [a,b,c]), ( ! -> true ).
    X = a ;
    X = b ;
    X = c.

Such properties make an interpreter for complete Prolog rather complex, and for this reason I have shown a meta-circular interpreter that can handle only a (Turing-complete) subset of Prolog. Thank you for your interest!

7y agoHN ↗

Ah. My apologies. As usual I lack precision. I was thinking of a meta-interpreter for the logically pure subset of Prolog- no cuts, or exceptions.

That should be covered by the classic Prolog meta-interpreter, no? I mean this one:

  prove(true,_Ps).
  prove((L1,Ls),Ps):-
    prove(L1,Ps)
    prove(Ls,Ps).
  prove((L1),Ps):-
    clause(L1,B)
    prove(B,Ps).
7y agoHN ↗

This would work if clause/2 always failed if there is no matching clause. Indeed, in some systems, that was the case. But now, clause/2 may and will throw an error for example on:

    ?- clause(true, B).
    ERROR: No permission to access private_procedure `true/0'

See also the Prolog ISO standard:

    clause(+head, ?callable_term)

    8.8.1.3 Errors

      ...
      c) The predicate indicator Pred of Head is that of a
      private procedure
      - permission_error(access, private_procedure, Pred).
      ...

In this interpreter, the third clause of prove/2 is also applicable if the others are applicable, because L1 subsumes both true and terms of the form (X,Y). For this reason, we get for example:

   ?- prove((true,true), _), false.
   ERROR: No permission to access private_procedure `true/0'

whereas we expect this to succeed, because (true,true) does. Hence, to make this interpreter actually work, more must be added, and to make it meta-circular (i.e., able to interpret its own code), these features must then also be handled.

In the representation I used, these issues do not arise because I am using a so-called clean representation. With such a representation, I can tell for certain what the individual goals are, as the individual goals are symbolically distinguished from conjunctions of goals.

7y agoHN ↗

I didn't think of the case of a missing clause. Thanks for the explanation :)

7y agoHN ↗

The post is based on SICP, and the author briefly discusses dynamic scoping after finding it in some other blog posts or something. In fact, the first edition of SICP contained a discussion of dynamic scoping. I don't know why it was removed from the second edition, because it's an interesting and important topic. Dynamic scoping also features in the SICP videos [1], which were produced around the time the first edition came out.

[1] https://www.youtube.com/watch?v=t5EI5fXX8K0&feature=youtu.be...