Hacker Timesnew | past | comments | ask | show | jobs | submit | brabel's commentslogin

What the hell why are you thinking you decide anything?? The man has his project and can do whatever he wants with it. Read the license.

Yes, he is free to do whatever he wants with it. And others are also free to say that what he's doing is bad and is causing them problems when trying to use this well established software that is known for being stable and reliable.

Freedom of speech goes both ways, not just the way you like.

Everyone forgets D. It’s probably the fastest to compile, even faster than Go

Is this a case of people saying one thing and doing another?? Everyone's experience is different, but to me it seems most people love AI?! I see reports in the news about people not being able to do anything anymore without asking AI first, people dating AI boy/girlfriends, students using AI to do homework, teachers using AI to catch AI cheating by students, people writing emails via AI, improving their own writing with AI... and so many more! I personally use it a lot for coding (though I still try to do some manual work so I don't just forget everything), translations, quick queries about things, in the computer (specially CLI commands, AI is just incredibly good at it - no matter the CLI, seemingly) and in the physical world (e.g. what's the name of that thing you turn on a tap to open it - English is not my first language), it even helped me a lot figure out legislation in two different countries, where finding and understanding the law was next to impossible by myself (and it gave me links to everything so I could check by myself).

Yeah this strikes me as everyone loudly complaining about how they hate McDonald’s and yet every McDonald’s has a drive thru line 24/7.

If you and 5 others go to McDonald's for 3 meals a day, it will always appear busy to you even if it had no traffic outside those moments you were there with the 5 others. Similarly the news can report on outliers using AI while most people you know IRL may not use it. In other words, it is accurate, the groups are not the same, and statistics often don't feel like they reflect reality.

Humans are complex. I think it's perfectly reasonable to assume there are a lot of people who both rely on and don't like the idea of AI. People can need a car to get around and also be worried about the effects of car emissions. People can dislike cigarettes and be smokers.

I keep seeing this argument in various places on HN that usage implies a positive opinion, when it very much does not. AI has put most people in prisoner's dilemma, and in prisoner's dilemma you can simultaneously play the game and hate the game. To go through a few of your examples:

> Students using AI to do homework

Either you don't use AI, where you have to spend a lot of time studying or graduate bottom of your class, or you do and get on with your life. You can acknowledge that the studying is the valuable part (most students do in my experience) yet skip it for whatever reason (procrastination / life issues / etc).

> Teachers using AI to catch AI cheating by students

We've added an extra step to their already overloaded schedules. If they don't do this they're basically encouraging students to cheat this way.

> Translations

You can now easily get a translation with much better accuracy than before (presumably, I'm a monolingual English speaker), but now you aren't talking to any other human beings for this information. This goes for a lot of other knowledge-value work / hobbies too where asking questions is valuable.


Tinkering means different things to different people! Want to tinker with your hardware, as bare metal as possible? Or extract every inch of performance out of your CPU? Zig is great for that.

I just upgraded some code to Zig 0.16.0 and I am actually really happy with the results. It impacted A LOT of things, but the changes were actually very good and seems to have set the language for a bright future, especially with the new IO mechanism which allows supper efficient code that looks good whether it's implemented single-threaded, multi-threaded or just via an event loop!

If you haven't tried Zig since 0.16.0 was released, I highly recommend having a look. The release notes for this release were huge!!

https://ziglang.org/download/0.16.0/release-notes.html


the “(super) efficient” is not there yet. Io is still dynamic dispatch with multiple layers of indirection. afaik it’s slower than before.

the upcoming releases are expected to provide a solution to this “dispatch is comptime-known, but still dynamic” problem, and drop the loses in efficiency.


Hmm in the 2025 talk ( https://youtu.be/f30PceqQWko?si=qZESxMaSyt7fYMfz ), Andrew emphasizes that this approach is more efficient than before- even showing compiled assembly iirc. I guess that was a one-off?

My guess is that one of these (Andrew) is measuring syscalls and the other is measuring vtable indirections.

A vtable indirection is essentially free when you're going to perform a syscall. What matters is that the buffer is above the vtable (which is already the case for the current implementation) so that you don't pay for the indirection when hitting the buffer.

Apples and oranges, yeah. I should have spelled that out more, thanks.

And if you write to a bytes buffer?

Wow that’s gnarly it’s using dynamic dispatch. I mean I get it, but I thought zig was some sort of performance demon.

if youre doing io, one pointer indirection seems unlikely tp be rate limiting. same for allocation (the other dynamic dispatch in zig)

It's not just I/O, it's also mutexes, condition variables, time, etc. It's not horrible, but it does add up, so calling it super efficient is a stretch.

A modern allocator with per-thread cache can satisfy some allocations in 20-30 cycles - dynamic dispatch can easily double that, even if the target is still in cache.

It's one of these things where it's extremely use case dependant - like many performance issues, you probably don't care about it - but when you do it matters.


Inderect call cost is a few cycles, if predicted. Now, you can argue, that it may be mispredicted and misprediction would cost about 20-30 cycles. But if it is mispredicted, then you are not calling into allocator often enough. And if you don't hammer it hard, then why do you care about preformance?

I believe their plan is using "restricted function pointers", where you can specify that a pointer will only ever be to a function defined in the codebase. I'm pretty sure they also have plans for devirtualization, but I haven't followed super closely.

"you can specify that a pointer"

i dont think you need to specify that. the compiler can figure it out and do an optimization pass at the end.


Oh, is it not a specific keyword? I thought they were thinking of it being a keyword so you could be sure that it was restricted, in case a variable or function was exported that took in a foreign pointer.

There are going to be builtins to control this. The compiler will not do it on its own.

The parent seems to be talking about efficient code style, not necessarily performance implementation, as they go on to discuss how it looks.

That is, I think the point was DevX not io performance.


> especially with the new IO mechanism which allows supper efficient code that looks good whether it's implemented single-threaded, multi-threaded or just via an event loop!

I had some trouble understanding how the async/await mechanism works:

  var foo_future = io.async(foo, .{args});
  defer if (foo_future.cancel(io)) |resource| resource.deinit() else |_| {}

  var bar_future = io.async(bar, .{args});
  defer if (bar_future.cancel(io)) |resource| resource.deinit() else |_| {}

  const foo_result = try foo_future.await(io);
  const bar_result = try bar_future.await(io);
My assumption is that calling io.async using an event loop implementation of IO, it will internally start a "task" (or whatever it should be called) and that the future is a handle to it. So far so good.

The part that I don't understand is what happens when you call future.await(io). Will the IO implementation somehow suspend the current function and resume once the future is resolved? If so, does that mean that every function in zig is a stackless coroutine?


> If so, does that mean that every function in zig is a stackless coroutine?

No and yes.

If you're using Io.Threaded, then the concurrency model is multithreading and calling Future.await will block your thread on a OS futex.

If you're using Io.Evented, then the concurrency model is green threads / fibers and calling Future.await will suspend the current green thread by yielding (swapping CPU state with another fiber).

Zig currently does not support stackless coroutines so today you can't have that, but we used to have them (pre self-hosted compiler), and there's an accepted proposal to bring them back, in which case any function that calls await, or that otherwise has a suspension point, would have to be transformed into a stackless coroutine by the compiler, yes. The plan is for that to happen transparently without requiring an `async` annotation in the function signature, like we already did in the past.

This is an old post of mine that explains how that worked at a high level: https://kristoff.it/blog/zig-colorblind-async-await/


> there's an accepted proposal to bring them back, in which case any function that calls await, or that otherwise has a suspension point, would have to be transformed into a stackless coroutine by the compiler, yes. The plan is for that to happen transparently without requiring an `async` annotation in the function signature, like we already did in the past.

If the compiler will treat functions that call a library function (Future.await) as special and change how the call site is compiled, why not just have an `await` keyword that when present will convert it into a state machine that can be suspended/resumed?

In other words: What is gained by not having a keyword that changes how a function is emitted if the compiler will change it anyway based on detection of a library call?


It's not Future.await that is special per se, it's that it (Future.await) will have in it somewhere either in its body or in another function that it calls in turn, a use of `suspend` (using old Zig syntax).

`suspend` is the keyword that best fits your description (you can think of it as being closely related to `yield` in other languages), and it's kind of a lower-level primitive compared to async/await.

This also means that, according to our plans, Zig will have to propagate "stackless-ness" upwards in the call chain while analyzing the code (thus making Future.await not special per-se).


> This also means that, according to our plans, Zig will have to propagate "stackless-ness" upwards in the call chain while analyzing the code (thus making Future.await not special per-se).

Very interesting, will be following Zig development more closely. Thanks for sharing!


For future reference you can format code on hn with a newline first then indenting each line by 2+ spaces. (Rather than triple tick)

Fixed it. thanks!

Maybe one day it'd be possible to use these new features, but I find myself using `.use_llvm = true` in my zig builds for stability and lesser tested targets.

No OP, but MCP really is just a logical next step once you've got an API. The API is the "low level" protocol, the MCP is the high level one, suited perfectly to an LLM that can call tools (since MCP essentially turns an API into a LLM tool).

With just an API, the agent needs to "read your API docs" to know how to call it (that can be an OpenAPI spec or even just text).

With MCP, the agent sees a bunch of tools it can call, and they've been trained to call tools so they nail it.

One more very important factor is authorization, which no one seems to mention in these discussions. CLIs were made for humans and use primitive mechanisms for authorization: either an API key you hardcode in your environment, or they literally run a background HTTP Server to get a callback OAuth call to receive a token from a browser authentication flow. Incredible that people are happy with that, appparently. With the MCP Authorization spec, you solve authorization across multiple MCP servers in the same standardized way, the LLM client you use just need to know the protocol, not how to login for every single MCP server.

Very importantly, if the MCP client does the authorization, the MCP provider has auditability: is this a call from a human or from a LLM? That's important in Enterprise! People think it's ok to let an LLM act on behalf of the human but that will eventually bite a lot of people. Did the LLM just try to hack the API while you were mindlessly clicking "yes" when it asked if you wanted to let it do something? Tough luck, there's no way to distinguish an LLM making a mistake from a human maliciously running some attack.

And as the post mentions, there's also more benefits like being able to "elicit" user input (not just request/response cycles) and the ability to have documentation and assets (skills also have this though).


This is a great example of the AI-hype-induced reply.

> to an LLM that can call tools (since MCP essentially turns an API into a LLM tool).

"Tools" is literally an API call

> With MCP, the agent sees a bunch of tools it can call,

Yes, the agent first calls a specific API that returns the schema for that particular server. It's literally the same.

> One more very important factor is authorization, which no one seems to mention in these discussions.

Yes, API calls to services are often gated behind auth. OAuth that MCP uses is from 2006, and its version 2 is from 2012. What do you think it was created for?

> the MCP provider has auditability: is this a call from a human or from a LLM? That's important in Enterprise

We had "differentiate these two accounts and audit log their activity" probably since the 1950s

> there's also more benefits like being able to "elicit" user input

Two-way communication is also a thing since the 1950s, probably.


If you think tool call and letting the LLM call an API via curl are the same thing, you haven’t a clue how LLMs work and honestly shouldn’t be commenting on the topic at all.

sometimes I remind myself HN is reddit now except when Dang is awake

A "tool call" is literally a JSON-RPC call with a predefined schema.

You'd know that if you actually did any of the following:

- read the specification

- implemented an MCP server

- observed communication between client and server

- had any experience beyond what LLMs tell you


The CL condition system always gets brought up when people unfamiliar with effects see effects for the first time (example: https://qht.co/item?id=38813484, another example: https://lobste.rs/s/12m2f0/algebraic_effects_another_mistake).

But while the condition system can do many things you can also do with effects, they cannot do everything.

Here's another discussion on this: https://qht.co/item?id=44078743


CL conditions do what you actually need if you program. CL gives you deterministic state, safe resource management etc.

Nondeterminism is not a feature you want. Algebraic effects treat the execution stack (continuation) as data, you have total freedom over what you do with it. This flexibility is exactly where you get nondeterminism. This is how logic solvers or probabilistic algorithms work, but you don't want it as a programming language feature in general purpose programming language.


It’s pretty annoying to comment “this thing was already present in CL 30 years ago”, then to have someone correct you (pointing out that this is a common misconception, and algebraic effects are not equivalent to conditions at all), and then to respond not to admit you’re wrong but instead to say “algebraic effects are bad anyway because you shouldn’t want one of the extra features they give you (nondeterminism)”.

By the way, nondeterminism is not the only difference between the two.


Standard usable feature. Features must work with everything else in the language.

I like programming language theory as much as anyone else, but there is a reason some language features exist only so that people can blog and think about them.


> Nondeterminism is not a feature you want.

I can get behind the sentiment, but you absolutely need nondeterminism. You can separate the d from the non-d, but only Haskellish languages even attempt it. It's a coarse separation to make (IO vs non-IO), which is where effect systems come in - I guess you can categorise code into more fine-grain buckets. The 'algebraic' part is currently beyond my knowledge.


A real effect system allows you to do things like NOT continue execution after using the effect (like the error effect does - if you "implement" this by using Exceptions, you're not using effects at all, just using Exceptions with extra steps) or only continuing it after some asynchronous work happens (the Future effect), or even "continue" execution several times. That just cannot be done with "just passing stuff in". You still don't seem to have understood effects.

Thanks for your response. Perhaps I'm missing some fundamental things. Could you help?

> A real effect system allows you to do things like NOT continue execution after using the effect

Right, Bluefin's Request allows you to do that too. For example here is an example of handling the request by continuing or not, depending on what the value yielded to the Request is.

    example :: Either String ()
    example = runPureEff $ try $ \ex -> do
      forEach
        ( \r -> do
            request r True
            request r True
            request r False
            request r True
            request r True
        )
        ( \case
            True -> pure ()
            False -> throw ex "Stopped"
        )
> if you "implement" this by using Exceptions, you're not using effects at all, just using Exceptions with extra steps

Not sure I follow that. Above you can see I used an exception (Bluefin's Throw capability), but I couldn't have used only an exception because that would have aborted unconditionally. What am I missing here, that makes "using Exceptions" "not using effects at all"?

> only continuing it after some asynchronous work happens (the Future effect)

I'm not really sure what "a Future effect" is, but I don't see how it's not something that can be run as a function call, at least in Haskell.

> or even "continue" execution several times

Right, these are the multishot continuations which Bluefin doesn't support. I haven't discovered many particularly compelling use cases for multishot continuations but would be very interested in finding some. The developer of the Kyo effect system for Scala, Flavio Brasil, suggested parsing, with multiple parse results, which makes sense.

I'm also not entirely sure Bluefin couldn't simulate common use cases of multishot continuations with threads, but I haven't thought about it very hard.

> You still don't seem to have understood effects.

Possibly true, and part of my puzzlement! I'm always happy to try to improve my understanding. Can you help me see what I've missed?


I think the parent may be getting at the continuation aspect of effects? Effect systems make the stack a first class object you can reuse, I think a standard example is implementing a scheduler. I'm not familiar with your Bluefin library so maybe it already handles this:

  effect Sched =
    yield : unit -> unit
    fork  : (unit -> unit) -> unit
  end
  
  let mut run_queue = []
  let enqueue t = run_queue := List.concat run_queue [t]
  
  let dequeue () =
    match run_queue with
    | [] -> ()
    | t :: rest ->
      run_queue := rest;
      t ()

  let rec spawn task =
    handle
      task ()
    with
    | return _ -> dequeue ()
    | yield () k ->
      enqueue (fn () -> resume k ());
      dequeue ()
    | fork f k ->
      enqueue (fn () -> resume k ());
      spawn f

  let run main = spawn main

  let worker name steps =
    let rec loop i =
      if i > steps do ()
      else do
        print $"{name}: step {i}";
        perform yield ();
        loop (i + 1)
      end
    in
    loop 1
  
  let () =
    run (fn () ->
      print "main: starting";
      perform fork (fn () -> worker "A" 3);
      perform fork (fn () -> worker "B" 3);
      print "main: forked workers, now yielding";
      perform yield ();
      print "main: done")
output:

  main: starting
  A: step 1
  B: step 1
  A: step 2
  main: forked workers, now yielding
  B: step 2
  A: step 3
  main: done
  B: step 3

Ah yes, OK, I missed the point that the timeout is applied to the entire continuation, not just the part of the computation until the next await. Bluefin can't currently do that. I think I could make it do that, using the same implementation strategy as awaitYield (fork a thread, communicate through an MVar) but I wonder what the point is, given that Bluefin allows you to run the continuation at most once. Is the use case of "run the continuation in a modified environment (e.g. with a timeout)" really that compelling? Maybe it is! But I don't see it yet.

On the other hand, I don't see any difficulty with implementing a scheduler using Await/Yield. I don't think it needs access to the full continuation.


Yes, this article is doing a bad job at explaining why you would want effects, and one of the main advantages is exactly that it becomes part of the type system, essentially coloring every single function with a set of effects it needs to be called. As the article used JavaScript it shows what untyped effects would look like, which in my opinion is awful. If you want to use algebraic effects today, I highly recommend Unison. If you’re on the JVM, Flix is doing major advances with effects!

https://www.unison-lang.org/

https://flix.dev/


If they’ve done it using Secure Enclave it’s essentially physically impossible to spoof.

The github OP reports that browser-based login still works, so it'll likely be circumventable.

Wouldn’t any Volkswagen keys need to cross the network to get into the Secure Enclave? Or couldn’t you exploit the Volkswagen app itself?

Keys in the Secure Enclave never leave the device (or the SE for that matter) and cannot be extracted even physically.

Newer devices support Remote Key Provisioning (RKP), so you still can't export keys but you can import them. (Physical attacks are still possible, just very difficult)

If the data is going through the air or a wire it can be sniffed, right? Is every message signed or encrypted like ssl/tls, or is this just some kind of extra header(s)?

Yes, it can be sniffed. It will at least use transport encryption, like TLS. For everything, yes. So you'll only get encrypted data you cannot read. You could attempt a Man-in-the-middle attack on this connection. Unless the app is badly made, this will not succeed.

And then, even if you could look inside, there's another type of asymmetric cryptography going on: the remote attestation itself. Again, if properly designed and possibly backed by a hardware security chip, it cannot be spoofed. This isn't something trivial like a shared secret in an HTTP header.


Wrong.

Okay, well that's a start. Could you help me understand where I went wrong? I'm not trying to be stupid here but just saying "wrong" is extremely unhelpful.

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

Search: