Readit News logoReadit News
gergo_barany commented on The Simplicity of Prolog   bitsandtheorems.com/the-s... · Posted by u/thunderbong
upghost · a year ago
> Could you elaborate on all of these?

Certainly.

> For [1], you would either need to pass the AVL tree around or to have it as a global (which is not wanted), and instead pass the key (the "this" context which is different for different mazes) for the context around.

Not sure how tracking references is different here than any other programming language. Passing objects around is pretty common in OO programming. An AVL tree is just the underlying abstraction used to implement the object. You don't have to implicitly supply the "this" parameter to each "object" predicate if you don't want to -- you could insert that yourself via compilation (with term/goal expansion)[4] or interpretation (via meta-interpreters) if you were really interested in that level of syntactic sugar.

> For [2] again you have a global table (with copying semantics as for assert/retract? or maybe without copying? the docs don't say). But again you would need to pass a key around.

You could use global or local semantics as desirable. The blackboard has a global version that is persistent across application state or a local version that provides lexical context which unwinds on backtracking. Not sure how "passing a key around" is different than most other forms of programming, but if you wanted to compile it away, please review the techniques listed above.

> [3] is... yeah. I mean, sure, you could demonstrate this with a toy metainterpreter on a toy example. But do you really want to run your whole application inside a metainterpreter?

A "toy" meta-interpreter? Meta-interpreters are as fundamental to Prolog as for-loops are to Python. Certain applications, such as games, are run "entirely inside a loop", so I'm not sure how this criticism applies. You can run as much or as little of your application inside a meta-interpreter as you want. The value proposition of Prolog is that the simple homoiconic syntax allows for powerful meta-interpretation. I'd suggest checking out the video I linked to in [3] if you have doubts on that topic.

> One could also abuse DCG syntax, with the context being some state object instead of a DCG list.

State transitions (a sequence of states) are a great use for DCG syntax! I wouldn't call that "abuse".

> A more practical way would be Logtalk-like code generation. The most practical way would be actual Logtalk.

If you are willing to give up the most powerful features of Prolog, sure.

> Unfortunately, last I heard Scryer refused to host Logtalk for no real reason.

Hmm, I wonder if that's the real story :)

[4]: Please see Section 3 of my talk if you are interested in a more thorough explanation of goal/term expansion. https://docs.google.com/presentation/d/e/2PACX-1vR4Q0Ohs66mj...

gergo_barany · a year ago
> You don't have to implicitly supply the "this" parameter to each "object" predicate if you don't want to [...] if you were really interested in that level of syntactic sugar.

Given that the original "feature request" (https://news.ycombinator.com/item?id=42829985) was this:

>>>> There is no concept of "current maze", like in OO you would have with this.maze, or in Haskell you would do with a reader monad. As a result, all the "internal" predicates in a module get full of parameters all they do is pass around until some final predicate uses it to do some calculation.

I would say that yes, the OP wanted exactly that level of syntactic sugar and your previous suggestions [1] and [2] were addressing something else entirely.

> you could insert that yourself via compilation (with term/goal expansion)[4]

Yes, that's why I meant above by "Logtalk-like code generation". Suggesting that I study the thing that I suggested feels a little condescending.

gergo_barany commented on The Simplicity of Prolog   bitsandtheorems.com/the-s... · Posted by u/thunderbong
upghost · a year ago
There are multiple ways to accomplish this, but the one that is the most straightforward is to simply make an object mapping of the type you are familiar with via AVL trees[1]. Easy way to get the "this.maze" semantics. You can get global context and local context via "blackboard"[2] semantics.

However quite frankly the most powerful way to do this is not obvious because it doesn't translate well to other languages: meta-interpreters[3].

[1]: https://www.scryer.pl/assoc

[2]: https://www.scryer.pl/iso_ext.html#bb_get/2

[3]: https://youtu.be/nmBkU-l1zyc

gergo_barany · a year ago
Could you elaborate on all of these?

For [1], you would either need to pass the AVL tree around or to have it as a global (which is not wanted), and instead pass the key (the "this" context which is different for different mazes) for the context around.

For [2] again you have a global table (with copying semantics as for assert/retract? or maybe without copying? the docs don't say). But again you would need to pass a key around.

[3] is... yeah. I mean, sure, you could demonstrate this with a toy metainterpreter on a toy example. But do you really want to run your whole application inside a metainterpreter?

One could also abuse DCG syntax, with the context being some state object instead of a DCG list.

A more practical way would be Logtalk-like code generation. The most practical way would be actual Logtalk. Unfortunately, last I heard Scryer refused to host Logtalk for no real reason.

gergo_barany commented on Tilde, My LLVM Alternative   yasserarg.com/tb... · Posted by u/davikr
negate32 · a year ago
One of the big things which makes LLVM very slow is the abundance of passes, I believe I last counted 75 for an unoptimized function? My solution for this is writing more combined passes, due to the SoN design I'm combining a lot of things which are traditionally separate passes. For instance, my equivalent to "SimplifyCFG" "GVNPass", "InstCombine", "EarlyCSEPass" and "JumpThreadingPass" is one combined peephole solver which runs faster than all of these passes separately. This is for two main reasons:

* Less cache churn, I'm doing more work per cacheline loaded in (rather than rescanning the same function over and over again).

* Combining mutually beneficial optimizations can lead to less phase ordering problems and a better solve (this is why SCCP is better than DCE and constant prop separately).

In a few years when TB is mature, I'd wager I'll have maybe 10-20 real passes for the "-O2 competitive" optimizer pipeline because in practice there's no need to have so many passes.

gergo_barany · a year ago
If this is one of the main things you want to demonstrate, wouldn't it be better to focus on this one goal first, instead of the whole pipeline from a C preprocessor to directly linked executables?

Essentially, if you say that LLVM's mid-end in particular is slow, I would expect you to present a drop-in replacement for LLVM's mid-end opt tool. You could leave C-to-LLVM-bitcode to Clang. You could leave LLVM-bitcode-to-machine-code to llc. Just like opt, take unoptimized LLVM bitcode as input and produce optimized LLVM bitcode as output. You would get a much fairer apples to apples comparison of both code quality and mid-end compiler speed (your website already mentions that you aren't measuring apples-to-apples times), and you would duplicate much less work.

Alternatively, look into existing Sea of Nodes compilers and see if you can build your demonstrator into them. LibFIRM is such a C compiler: https://libfirm.github.io/ There may be others.

It just seems like you are mixing two things: On the one hand, you are making some very concrete technical statements that integrated optimizations are good and the Sea of Nodes is a great way to get there. A credible demonstrator for this would be very welcome and of great interest to the wider compiler community. On the other hand, you are doing a rite-of-passage project of writing a self-hosting C compiler. I don't mean this unkindly, but that part is less interesting for anyone besides yourself.

EDIT: I also wanted to mention that the approach I suggest is exactly how LLVM became well-known and popular. It was not because of Clang; Clang did not even exist for the first eight years or so of LLVM's existence. Instead, LLVM focused on what it wanted to demonstrate: a different approach to mid-end optimizations compared to the state of the art at the time. Parsing C code was not part of that, so LLVM left that to an external component (which happened to be GCC).

gergo_barany commented on Tilde, My LLVM Alternative   yasserarg.com/tb... · Posted by u/davikr
gergo_barany · a year ago
> a decent linear scan allocator which will eventually be replaced with graph coloring for optimized builds.

Before setting out to implement 1980s-style graph coloring, I would suggest considering SSA-based register allocation instead: https://compilers.cs.uni-saarland.de/projects/ssara/ , I find the slides at https://compilers.cs.uni-saarland.de/projects/ssara/hack_ssa... especially useful.

Graph coloring is a nice model for the register assignment problem. But that's a relatively easy part of overall register allocation. If your coloring fails, you need to decide what to spill and how. Graph coloring does not help you with this, you will end up having to iterate coloring and spilling until convergence, and you may spill too much as a result.

But if your program is in SSA, the special properties of SSA can be used to properly separate these subphases, do a single spilling pass first (still not easy!) and then do a coloring that is guaranteed to succeed.

I haven't looked at LLVM in a while, but 10-15 years ago it used to transform out of SSA form just before register allocation. If I had to guess, I would guess it still does so. Not destroying SSA too early could actually be a significant differentiator to LLVM's "cruft".

gergo_barany · a year ago
Also, for a different notion of "cruft", informally it seems to me like new SSA-based compilers tend to choose an SSA representation with basic block arguments instead of the traditional phi instructions. There are probably reasons for this! I'm not aware of a Sea of Nodes compiler with (some notion corresponding to) block arguments, but it might be fun to explore this when designing a new compiler from the ground up. Might be too late for TB, though.
gergo_barany commented on Tilde, My LLVM Alternative   yasserarg.com/tb... · Posted by u/davikr
gergo_barany · a year ago
> a decent linear scan allocator which will eventually be replaced with graph coloring for optimized builds.

Before setting out to implement 1980s-style graph coloring, I would suggest considering SSA-based register allocation instead: https://compilers.cs.uni-saarland.de/projects/ssara/ , I find the slides at https://compilers.cs.uni-saarland.de/projects/ssara/hack_ssa... especially useful.

Graph coloring is a nice model for the register assignment problem. But that's a relatively easy part of overall register allocation. If your coloring fails, you need to decide what to spill and how. Graph coloring does not help you with this, you will end up having to iterate coloring and spilling until convergence, and you may spill too much as a result.

But if your program is in SSA, the special properties of SSA can be used to properly separate these subphases, do a single spilling pass first (still not easy!) and then do a coloring that is guaranteed to succeed.

I haven't looked at LLVM in a while, but 10-15 years ago it used to transform out of SSA form just before register allocation. If I had to guess, I would guess it still does so. Not destroying SSA too early could actually be a significant differentiator to LLVM's "cruft".

gergo_barany commented on Recursive Program Synthesis using Paramorphisms [pdf]   theory.stanford.edu/~aike... · Posted by u/luu
almostgotcaught · 2 years ago
That's not at all what they define in that section. They define their chosen combinator that is called "para" which is as close to what they're alluding to as a bicycle is to a semi truck.
gergo_barany · 2 years ago
What they're "alluding to" is a standard combinator that has existed under the name "paramorphism" for a long time. Here is Jeremy Gibbons's "Origami programming" https://www.cs.ox.ac.uk/jeremy.gibbons/publications/origami.... from 2003:

    [...] capture this modified recursion pattern explicitly as a higher-order operator; in this case, the operator is known as a paramorphism [92]. In the case of lists, we could define
    
    paraL :: (α → (List α, β) → β) → β → List α → β
    paraL f e Nil = e
    paraL f e (Cons x xs) = f x (xs, paraL f e xs)
I don't know of an older source for this particular syntactic presentation, though apparently if you put in the work to translate Squiggol to Haskell, you can read this concrete syntax out of Meijer et al.'s Bananas paper from 1991.

I think what you're criticising is that the name "paramorphism" was adopted from category-theory-on-a-blackboard to functional-programming-that-actually-runs-on-a-computer. That ship sailed 30 years ago; this paper from 2024 is not what caused it to sail. I also think that you're incorrect to criticize this. Analogies are often very helpful.

You are right that the people who pointed you to Meertens's paper were not helpful in clearing up the confusion, since your confusion is exactly that Meertens's paper talks about the other meaning of the term "paramorphism".

gergo_barany commented on Recursive Program Synthesis using Paramorphisms [pdf]   theory.stanford.edu/~aike... · Posted by u/luu
almostgotcaught · 2 years ago
> present in the literature for a long time:

You're the second person to link the exact same paper I did in my first response.

> To your original point, the title of this paper is only using terms ("paramorphisms", "recursive", and "program synthesis") that are decidedly not novel.

They italicize paramorphism in the abstract. We all very well know that italics means novel usage. So again: let's drop the charade that everything is above board here.

gergo_barany · 2 years ago
> let's drop the charade that everything is above board here.

OK. Since you're opposed to merely alluding to things (your italics, which I find funny), could you please state explicitly the misconduct you are alleging?

gergo_barany commented on Beating the L1 cache with value speculation (2021)   mazzo.li/posts/value-spec... · Posted by u/nickdevx
bobmcnamara · 2 years ago
The nodes are adjacent.
gergo_barany · 2 years ago
The nodes are adjacent in sum1.

The nodes are adjacent in sum2, and sum2 executes more instructions than sum1, and sum2 is faster than sum1.

The nodes are adjacent in sum3, and sum3 executes more instructions than sum1, and sum3 is faster than sum1.

gergo_barany commented on Beating the L1 cache with value speculation (2021)   mazzo.li/posts/value-spec... · Posted by u/nickdevx
xiphias2 · 2 years ago
It's cool, I would love to have this for ARMv8 Mac
gergo_barany · 2 years ago
The LLVM project has a tool called llvm-mca that does this. Example: https://gcc.godbolt.org/z/7zcova1ce

The version in the Compiler Explorer wouldn't work on AArch64 without an -mcpu flag and I didn't know what to pass, so I copied -mcpu=cyclone from https://djolertrk.github.io/2021/11/05/optimize-AARCH64-back.... You'd have to look up the correct one for your Mac's CPU.

gergo_barany commented on Unification in Elixir   ericpfahl.com/from-patter... · Posted by u/weatherlight
weatherlight · 2 years ago
The full quote.

    The one-sided pattern matching seems to be a     peculiarity of Elixir. The post above discusses an     implementation of full unification without that     restriction. To clarify, unification is not     generally restricted to one-sided matching only.
I'm saying that, except for Erlang, the languages mentioned (Haskell, OCaml, F#, etc.) don't support one-sided pattern matching like Elixir does.

Elixir's pattern matching is more like unification. For example:

    [a, a, b, a] = [1, 1, 2, 1]
This kind of pattern matching, where a variable can appear multiple times on the left-hand side and must match the corresponding parts of the list, can't be done easily in Haskell or OCaml. In those languages, each variable can only appear once on the left side of a pattern match.

the OP is saying that this style of one sided pattern matching is indeed a peculiarity of Elixir (and Erlang)

gergo_barany · 2 years ago
I see, that's indeed an important distinction. Thanks!

u/gergo_barany

KarmaCake day124September 7, 2017View Original