HNHacker News
TopNewBestAskShowJobs

remexre

1,591 karma · joined October 12, 2016

PhD student at https://melt.cs.umn.edu/

Researcher at https://www.sift.net/

Email is concat ["me@", username, ".com"]

submissionscomments
remexre··on My resignation letter as R7RS-large chair
I dunno, I got a (Common) Lisp job, and I don't think I've ever had a problem where I didn't understand the semantics of a macro; people aren't just making a ton of them all the time for no reason, and the ones that are popular are from popular libraries (e.g. alexandria and iterate) and have clear semantics.

I think the same problem applies perfectly well to developers who "create their own little ecosystem of classes and functions nobody else can understand" -- I don't think [0] would be made any harder to understand by having macros involved.

[0]: https://old.reddit.com/r/programming/comments/15s0lp6/how_we... (only picking on it because I saw it recently)

remexre··on ISPs complain that listing every fee is too hard, urge FCC to scrap new rule
I'm a happy customer until the end of the month (moving out of their service area, sadly); I had symmetric gigabit. No IPv6, though.
remexre··on Ball milling destroys PFAS in contaminated soil
Or until there's not enough excess energy to continue producing hydrogen; I don't see a reason why we'd expect the hydrogen to become unprofitable (water to become too expensive?) first.
remexre··on Candle: Torch Replacement in Rust
Having types lets you get better editor completion and such; I'd say Rust pushes you into bottom-up design, which I find significantly more productive than top-down too; doing bottom-up design is much trickier in a dynamic language, though, because changes to one of your "bottom layers" break upper layers in a way that the compiler/interpreter can't see
remexre··on Compiler Development: Rust or OCaml?
https://docs.rs/bumpalo/latest/bumpalo/
remexre··on No telemetry in the Rust compiler: metrics without betraying user privacy
It sounds like "without the user using a separate not-installed-by-default program, they wouldn't get uploaded anywhere" to me?
remexre··on It's 2023, so of course I'm learning Common Lisp
I'll cover the Haskell side because I'm more familiar with its library ecosystem:

> Read some environment variables and a local file

    import System.Environment (getEnv)
    import System.FilePath ((</>))
    main = do
      dir <- getEnv "WHATEVER_DIR"
      data <- readFile (dir </> "foo.txt")
      putStrLn ("Had " ++ show (length (lines data)) ++ " lines")
> Start a monitoring thread [...]

    import Control.Concurrent
    import Control.Concurrent.Chan
    import Network.HTTP
    -- also more stuff...

    monitoringThread :: Chan String -> IO ()
    monitoringThread chan = do
      file <- openFile "log.txt" AppendMode
      forever $ do -- no $ needed if you pass -XBlockArguments [0, 1]
        batch <- replicateM 5 (readChan chan)
        let chunk = unlines batch
        hPutStr file chunk
        simpleHTTP (postRequestWithBody "" "text/plain" chunk)

    main :: IO ()
    main = do
      logChan <- newChan
      void (forkIO (monitoringThread logChan))
      -- ...
      forever $ do
        threadDelay (1000 * 1000) -- 1M usec
        writeChan logChan "Hello, world!"
> Configure and start an http server

    import Network.Wai
    import Network.Wai.Handler.Warp

    main = run 8000 $ \req respond ->
      respond (responseLBS status200 [] "Hello, world!")
> Said server has [...]

Yeah, this is long. If you're just getting the current time with the timer, that's here[2]; synchronize across threads with MVars[3] or STM[4]; JSON is in aeson[5], which should feel broadly familiar if you know Rust's serde.

> server’s gotta be able to do concurrency > 1 with parallelism

Yep, GHC Haskell has _excellent_ concurrency support on top of a parallel runtime.

> On sigterm set the server to a state that rejects new requests, waits for existing requests to complete, then flushes the monitoring channel

I haven't personally tried this, but this[6] function sounds like... exactly this, actually, so I think its example should suffice?

On two separate notes:

- Common Lisp and Python 3 are a _lot_ closer than Common Lisp and Haskell, or even Python 3 and JavaScript; the Python 3 object model is very close to Common Lisp's, and Common Lisp is not particularly pure (setf isn't unidiomatic by a longshot), and supports a very non-functional style of programming (it has gotos!).

- "Haskell is worse at IO than other high-level languages" isn't particularly true. What _is_ true is that Haskell has the same "function coloring problem" as JavaScript (Haskell has the IO monad, JavaScript has the Promise monad); Haskell also has a "uses funny academic words" problem (well, debatably a problem...) which I think confuses the issue.

[0]: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/bloc...

[1]: Haskell has a spec, one "main" implementation (GHC), little spec-committee activity, and a respect for that implementation not superseding the spec; many improvements become language extensions (-X flags or {-# LANGUAGE #-} pragmas), so when you invoke GHC you're getting a by-the-spec implementation by default.

[2]: https://hackage.haskell.org/package/time-1.12.2/docs/Data-Ti...

[3]: https://hackage.haskell.org/package/base-4.18.0.0/docs/Contr...

[4]: https://hackage.haskell.org/package/stm-2.5.1.0/docs/Control...

[5]: https://hackage.haskell.org/package/aeson-2.2.0.0/docs/Data-...

[6]: https://hackage.haskell.org/package/warp-3.3.28/docs/Network...

remexre··on Unpacking Google’s Web Environment Integrity specification
TLS doesn't verify that particular software or hardware is on the other side; one could design a custom CPU on an FPGA, write their own TLS stack for it, and be able to connect to any TLS-using site as usual without needing to get those things approved.
remexre··on S.F. says incidents by Cruise, Waymo driverless taxis are ‘skyrocketing.’
Native Minnesotan here -- I know at least 3 people who bike to work in our winters. Obviously they're all insane, but none of them are frostbitten...
remexre··on Retro: A modern, pragmatic Forth
Forth dialects typically don't have forward referencing _or_ variables that can be defined in the middle of a function, though!

(Though, writing a function long enough that you might want to declare a variable part-way through it would probably be a code smell in Forth...)

remexre··on Bubblewrap – Low-level unprivileged sandboxing tool used by Flatpak
I think that still requires lots of work, since lots of programs expect to be able to pass around paths to children/parents and have those paths remain openable.

For instance, if both cmake and ld get their own sandboxes, with their own view of /lib, it seems pretty likely that cmake's -L flags will get mixed up, in a way that's harder to debug than "oh, CMake passed -L/lib/foo; nothing should use /lib, so I need to find where that's hardcoded."

remexre··on Bubblewrap – Low-level unprivileged sandboxing tool used by Flatpak
IME (compared to other distros I've used), NixOS actually goes in rather heavily on using the sandboxing features available in systemd.

For instance, most of my machines serve ~name/www as https://.../~name/, but this doesn't work out-of-the-box on NixOS, since nginx's sandbox isn't able to read /home by default.

remexre··on A toy programming language in 137 lines of Python code
Left recursion is basic, sure, but it's not a problem for an (LA)LR parser generator like yacc. (Depending on your parse actions, you might even get slightly better memory consumption when using left recursion rather than right recursion.)
remexre··on A toy programming language in 137 lines of Python code
By "good theory," I mean being able to know properties of the parser as a result of the formalism.

For example, one can prove that any yacc-generated parser never infinite loops and runs in O(n) (at least for the actual parsing; you can write custom code in actions that runs when a construct has been parsed, and of course you can write whatever you want there).

If you hand-write a grammar like the following, you'll end up with an infinite loop without restructuring the grammar; such restructuring is of course much easier if you have written the grammar out, rather than having it implicitly exist in code.

    Expr ::= Expr '[' Expr ']'
    Expr ::= Name
    Expr ::= Int
If you try to naively parse the following, you'll never parse the marked production either. Something like yacc will warn you about this, while an ordinary compiler compiling a hand-written parser won't.

    Stmt ::= Expr ';'
    Stmt ::= Name '=' Expr ';'
    Stmt ::= Expr '[' Expr ']' '=' Expr ';'
    Expr ::= Expr '[' Expr ']'
    Expr ::= Name
    Expr ::= Int
[0] is a good introduction to regex matching, [1] is a good introduction to the algorithm yacc uses, if you want to learn either.

[0]: https://swtch.com/~rsc/regexp/regexp1.html [1]: https://web.archive.org/web/20210507215636/http://web.cs.dal...

remexre··on A toy programming language in 137 lines of Python code
I don't really think of yacc as having much more abstraction than a regex engine -- like in an (NFA-based) regex engine, you're building a big data structure a human wouldn't want to hand-engineer from a declarative specification. (And, like in regexes, you've got good theory backing it.)
remexre··on Steel Bank Common Lisp
https://github.com/quil-lang/quilc
remexre··on CSS-Only Chat
Does https://gss.github.io/ count?
remexre··on Racket frustrates me
> I'm surprised no new languages focus on ecosystem. It is much less fun than making clever syntax I suppose.

Honestly, this is a lot of the draw of Go and Rust for a lot of people; other than debugging (which I can't speak to from experience, but as I understand it, both are on par with C++), the points you hit on above are all things Go and Rust have really solid answers to compared to their contemporaries.

remexre··on CommonJS is hurting JavaScript
the import syntax makes it possible for the browser to start loading dependencies as soon as the module has been parsed, before it finishes being compiled. the require function would force the browser to download the script being depended on then and there, and since it's not async, the browser would need to pause script execution while the module is being loaded.
remexre··on So you want to write a GUI framework (2021)
tkinter is the Python one!
remexre··on PostgreSQL reconsiders its process-based model
Wouldn't those statics also be slated for removal with this change?
remexre··on Milk-V Duo: A $9 RISC-V Computer
What's the selling point of 64MB of RAM with a 1GHz CPU?
remexre··on Making a RISC-V Operating System Using Rust (2019)
Presumably MS has it in cold storage somewhere... Maybe it'll get open-sourced some day, but I'm not holding my breath.
remexre··on Making a RISC-V Operating System Using Rust (2019)
Sounds like the entire Midori project only ran for 15, and iiuc it got all of that on top of a memory-safe kernel that was reinventing the language it was written in as the project was going.
remexre··on Ask HN: Rust or C or C++ in 2023?
I mean, this surely wouldn't be written as obviously as this in real code. It'd be somewhere in a data structure that packs to be able to fit better in a cacheline, and has something like:

    struct edge {
        struct node* src;
        int dst_offset;
        int label;
    };
Knowing about integer overflow, the original programmer carefully wrote overflow checks where dst_offset gets computed, and the code was correct. Nodes were allocated into one contiguous array, and edges were allocated into another.

Later, someone else changed how nodes were allocated, so adding a new node would never trigger a realloc of the whole graph. Instead, a linked list of node arrays is allocated. Suddenly now, computing dst_offset is UB, and yet, the observed behavior of the resulting program is the same.

Even later, someone updated GCC on the CI machine, and now the inliner is more aggressive. Suddenly, mutations to node labels are unreliable, and edges are found to not refer to any node in the graph, except when debug logging logs the address they actually refer to.

This is the kinda thing that requires an understanding of UB that has nothing to do with the actual hardware to diagnose and fix, which I think is really unnecessary for a beginner.

remexre··on Ask HN: Rust or C or C++ in 2023?
Because if memory is a big array of bytes and pointers are indices into that big array, that should be numerically valid. And indeed, if instead of making xs and ys separate arrays, one writes:

    int zs[20] = {0};
    int *xs = &zs[0], *ys = &zs[10];
then no perplexing behavior is observed.

Of course, any veteran C programmer will point at that being UB, but the big-array-of-bytes (with holes) model is what one sees in userspace assembly, and what I assume most systems programmers think about most of the time, rather than the C memory model.

I wouldn't recommend that someone trying to learn systems programming (as opposed to C or C++ as languages) spend, like, any time whatsoever memorizing the list of C UBs (and then learning about pointer provenance, exposed addresses, etc., I suppose?). Assembly, Forth, safe Go, and safe Rust don't have the same kind of UB, and (Linux) kernel C has a different set of UBs than userspace/spec-compliant C.

remexre··on Ask HN: Rust or C or C++ in 2023?
Agree that C is easier to explore the concepts in, but knowing enough C to know why e.g. [0] gets the behavior it does is IMO not useful if the goal is to learn the _concepts_ before switching to a language that doesn't do this kind of thing.

So, IMO worth jumping to Rust as soon as you feel confident with the stack vs the heap, use-after-frees, etc.

[0]: https://godbolt.org/z/x7bf4edo9

remexre··on Generative AI learning path
"to continue to Qwiklabs," which is what you get when using Google to auth to 3rd-party services, not 1st-party ones
remexre··on Modern C (2019)
> Now imagine tightening those restrictions a bit.

Yep, that's one approach, though that's just as hard to compile as one that is defined to trap and end execution on an out-of-bounds write.

As I said up-thread, it's possible to define a C dialect without UB, it's just not what most of its users actually want out of the language, since on current hardware it adds significant runtime overhead.

> The machine [...] the compiler

Isn't QEMU both the machine and the compiler (EDIT: perhaps better-put, what's the distinction? They're both just the implementation.) The bytes only need to be unchanged on read from inside the program; if you compile them to something else, that's fine. Prolog programs can introspect with clause/2 and it doesn't cause trouble there.

remexre··on Modern C (2019)
> That doesn't sound like it lets me increment any stack address I want and store into the resulting pointer.

Why not?

> And are return values still on the same stack?

Other implementation-defined things might have addresses too, those things just aren't variables. I think you might be able to still allow inlining without UB if you make it implementation-defined per call-site what other things might get addresses.

← PreviousPage 7 of 23Next →