HN Simulatornew | past | comments | lists | submit | norir's commentslogin

Anywhere that I could use reduce, I instead write a tail recursive function. This is also why I do not and will not ever choose python or javascript voluntarily.


Comments like this just further my bias against analytic philosophy as a system that helps illuminate the path to human flourishing and truth.


Yeah, this is such an entertaining exchange of layering being more "inner" and clued in. Reading the Greeks, Nietzsche or even, oh horror of horrors, Marcus Aurelius (if not "more interesting" earlier authors in his tradition) can make people reflect on their lives. All these can lead you astray in interesting ways but at least you are going somewhere, anywhere. There's always salty people who insist you should struggle through respectable academic philosophers instead, lest you are some kind of a prole.

This is kind of analytic (but also continental) mindset really does feel like wannabe math, just without visible correspondence to reality, like can be observed even with basic arithmetic or geometry. Also curiously you cannot really get the same results independently, without being nurtured by people from the circle. If you're shutting down earlier human curiosity about ethics, epistemology, meaning of language etc. and care only about your short lineage, this feels analogous, I don't know, to something like being a lore "scholar" of some franchise considering ponderous questions of how W40K deities relate to each other. And God help you if you bring up anything outside of approved lore or some other parts of human culture.


As someone who has written a jit compiler, I am puzzled by the claim that jitting requires write/execute permissions. When I have written a jit, I loaded some memory with read/write permissions using mmap. Once I filled in the generated code, I mprotected the region to read/execute before executing.

The drawback to this approach is there can be some bloat because you can only mprotect at page granulariy so a jitted function that only takes say 10 bytes to represent would take up a full page in memory, but this is extreme and in practice, the overhead is unlikely to be worth worrying about.


This reads as rather dismissive and not so humble. Compilers generating suboptimal and sometimes broken code very slowly is the norm, not the exception. Whether or not it's worth worrying about is indeed a development tradeoff but not one that should be so casually dismissed.


It is simple to convert factorial to tail recursive form. In lua, which has tco:

    local factorial do
      local function impl(n, acc)
        if n == 1 then
          return acc
        else
          return impl(n - 1, acc * n)
        end
      end
      factorial = function(n)
        if n < 0 then
          error("factorial input is negative")
        elseif n <= 1 then
          return 1
        else
          return impl(n - 1, n)
        end
      end
    end
You could replace impl with an imperative loop:

    local acc = 1
    repeat
      acc = acc * n
      n = n - 1
    until n == 1
    return acc
Personally, I find this ugly compared to the tail recursive solution. The loop version only seems more natural if you primarily think in loops. Tail recursion is strictly more powerful than looping since every imperative loop can trivially be converted to a tail recursive function, but the reverse is not true.


I would suggest adding objective metrics. How fast is this compiler? How long is the longest fuse program? How long does it take to compile? How fast at runtime is the fuse implementation of several benchmark programs compared to semantically equivalent programs written in other languages?

How expressive is fuse? How long are equivalent programs written in fuse/rust/scala/haskell?

Can you show me a bug that the fuse compiler catches but some or all of the competition doesn't?


If you need better performance than a higher level language affords you, I would not recommend programming directly in asm. Instead, I would write a compiler. Raw asm is seductive since the start up cost is relatively low. You can get started in an afternoon. The trouble is that writing correct assembly is much harder than high level code. You have to hold in your head the register state at all times. You have to know if the function you are calling will clobber registers that you need after the call and manually save/restore them. This will slow down your velocity and the resulting code will be long and difficult to read. It will also rely on a lot of undocumented information that only resided in your head while writing and has since been evicted. Good luck debugging a program written in assembly that no one has looked at for two months.

On the other hand, if you write your own non optimizing compiler, you can avoid a lot of these problems by, for example, tracking what registers a function writes and ensuring they are saved before a call and restored after. Then you can actually get the raw performance of handwritten asm without the pitfalls (the resulting code would still he harder to read and maintain than equivalent high level code, but at least it would be tractable). Even better, you can write your own high level assembler that is actually portable to other architectures. For example, instead of directly modeling x86_64, your compiler can model a cpu with 16 general purpose registers and a set of instructions that map to x86_64 instructions. An arm port would be straightforward since arm also has 16 general purpose registers and you can model x86_64 instructions as one or more arm instructions (and you have extra registers for x86_64 instructions that must be modeled as multiple arm instructions). Or you could do the reverse and model 32 general purpose registers using arm instructions and use predefined memory slots as virtual registers on x86_64.


I agree with your diagnosis but not entirely the solution. I think fixed sized integers in general are a non portable mistake in a language high level language. Instead, a language should provide arbitrary numeric range types and infer the result type of numeric operations. There should be automatic promotion to bignum when the number no longer fits in the machine word for the target platform. This keeps the focus on the data rather than the register sizes, which is incidental complexity. Where performance is critical, the programmer can annotate function types with specific fixed sized types that fit in registers and the compiler can statically enforce necessary bounds checks on possible overflows. I hypothesize that most software is so inefficient due to architectural flaws that it could be rewritten in a safe language with no undefined and/or unsafe numeric operations and still be at least as fast as the existing software even with the additional overhead of bignums and bounds checking.


I agree that a language should also provide integers of unlimited size (possibly also non-negative integers of unlimited size) like many LISP variants, Python etc.

Integer range types, like in Pascal and Ada, should also be available.

Such types should better be used wherever possible, to reduce the probability of bugs and to make the programs more portable.

Nevertheless, all the fixed-size integer types that are directly implemented in hardware (which currently are the 8 types enumerated above, with 5 sizes from 8 bits to 128 bits, but not all combinations of type and size are provided by the existing CPUs) must also be provided as primitive types by a programming language, to be used when maximum efficiency is necessary, because the difference in performance between using them and using software-defined types can be very large.


Guidelines | FAQ | Lists | API | Security | DMCA | Apply to YC | Contact

Search: