Hacker News

Top stories

Live mirror
30 storiesupdated just nowView source snapshot
  1. San Francisco Onion Futures Company(onionfutures.com ↗)
    33comments
  2. Android 17 is the first since 3.x to add new APIs without releasing to the AOSP(grapheneos.social ↗)
    353comments
  3. Typesafe-computer-use drives a Mac toward a goal for 1/50th of a cent per step(github.com/awlevin ↗)
    9comments
  4. SDCC – Small Device C Compiler(sourceforge.net ↗)
    10comments
  5. Science Is Open Software(jepedersen.dk ↗)
    21comments
  6. How OpenAI Used Its Own LLMs to Design Its Jalapeño Chip(ieee.org ↗)
    73comments
  7. Cloudflare Quick Tunnels(cloudflare.com ↗)
    272comments
  8. NASA-IBM Lunar Foundation open-Source Geospatial AI Model(usra.edu ↗)
    discuss
  9. Saving another 100TB of RAM(cloudflare.com ↗)
    59comments
  10. Why building a Rust LSP is hard(rust-glancer.github.io ↗)
    18comments
  11. How to Write with an LLM(sockpuppet.org ↗)
    307comments
  12. The first new cat species discovered in 100 years(nationalgeographic.com ↗)
    88comments
  13. Xcode 27.1 Beta Release Notes(developer.apple.com ↗)
    81comments
  14. You can run Git on object storage if you re-make packfiles(tigrisdata.com ↗)
    1comments
  15. Show HN: Cactus Needle 3: 8-29MB automation models can match DeepSeek V4 Flash(cactuscompute.com ↗)
    82comments
  16. Goroutine Leak Profiles(go.dev ↗)
    2comments
  17. OpenJev(openjev.com ↗)
    254comments
  18. Photon-Emission-Guided Laser Fault Injection Enables RP2350 Secure Debug(ledger.com ↗)
    63comments
  19. The Farnese letter(simonklee.dk ↗)
    6comments
  20. Cache-to-Cache: Direct Semantic Communication Between LLMs (2025)(arxiv.org ↗)
    12comments
  21. Minimal Phone 2(minimalcompany.com ↗)
    202comments
  22. Cyclomatic Complexity in C#(ndepend.com ↗)
    17comments
  23. LispBM is a concurrent Lisp for microcontrollers with message passing(lispbm.com ↗)
    3comments
  24. Claude Code now reads AGENTS.md if there is no Claude.md(claude.com ↗)
    213comments
  25. Inside ZCode: Silently uploading your Git history to the cloud(ferstar.org ↗)
    96comments
  26. Warez: The Infrastructure and Aesthetics of Piracy (2021)(archive.org ↗)
    43comments
  27. Alibaba open-sources AI model that can detect cancer and nearly 150 conditions(scmp.com ↗)
    11comments
  28. How SpaceX streamlined the Raptor engine(construction-physics.com ↗)
    67comments
  29. The Implications of Linguistic Illegibility for LLM Security(arxiv.org ↗)
    26comments
  30. Two parallel neural ectoderm progenitors contribute to the developing brain(newscientist.com ↗)
    60comments

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...