Hacker News new | past | comments | ask | show | jobs | submit | Guthur's comments login

I don't understand the anxiety with open source and big corporations, while deciding to use permissive licenses like MIT. Surely one could use a good copy left license and have far less anxiety.

Yeah, to become dependent.

I don’t think this is the wrong take but I like the idea of moving dependence towards something that could be run locally vs. a search engine which can not.

Humans are highly dependent creatures. Just watch "Alone" (tv show) if you think otherwise.

That has never been the definition of hallucination, until LLMs. It's actually just called lying, dishonesty or falsehood.

Hallucination is a distortion (McKenna might say liberation) of perception. If I hallucinate being covered in spiders, I don't necessarily go around saying, "I'm covered in spiders, if you cant see them you're blind" (disclaimer: some might, but that's not a prerequisite of an hallucination).

The cynic in me thinks that use of the word hallucination is marketing to obscure functional inadequacy and reinforce the illusion that LLMs are some how analogous to human intelligence.


"Hallucination" is what LLMs are always doing. We only name it that when what they imagine doesn't match reality as well as we'd like, but it's all the same.

Lying, dishonesty, and falsehood all imply motive/intent, which is not likely the case when referring to LLM hallucinations. Another term is "making a mistake," but this also reinforces the similarities between humans and LLMs, and doesn't feel very accurate when talking about a technical machine.

Sibling commenter correctly calls out the most similar human phenomenon: confabulation ("a memory error consisting of the production of fabricated, distorted, or misinterpreted memories about oneself or the world" per Wikipedia.)


IMHO Lying means thinking one thing and saying another, hiding your true internal state. For it to be effective it also seems to require something like “theory of mind” (what does the other person know / think that I know).

I believe the term hallucination comes from vision models, where the model would "hallucinate" an object where none exists.

Wouldn't 'illusion' be the more precise term here ? When one thinks he recognizes something, whereas 'hallucination' is more of unreal appearing out of the blue ?

You might be thinking of deepdream... https://en.wikipedia.org/wiki/DeepDream

"Hallucinations" have only really been a term of art with regards to LLMs. my PhD in the security of machine learning started in 2019 and no-one ever used that term in any papers. the first i saw it was on HN when ChatGPT became a released product.

Same with "jailbreaking". With reference to machine learning models, this mostly came about when people started fiddling with LLMs that had so-called guardrails implemented. "jailbreaking" is just another name for an adversarial example (test-time integrity evasion attack), with a slightly modified attacker goal.


Brave New World here we come, seems you can make this stuff up...

The RFC said "SHOULD not" not "MUST not" couldn't we have just ignored the 2 connection limit?

That's what browsers actually did.

Browsers started ignoring the 2 connection limit on H/1.x long before H2 came along

Came here to say the same thing; had they read the RFC they'd realize it's not actually a limit, just a suggestion - thats why its in the "Practical Considerations" section too.

Whomever downvoted you is probably unaware words like SHOULD have specific meaning in RFCs


Considering that at any moment the US can conjure into existence as many dollars as it needs how can it possibly default?


By actively choosing to? The current administration appears to view funds as being permanently capable of getting clawed back, based on their current whims as to whether the payments are "fraudulent" or not.

When they talked about upending the current order, the banking system and debt in general being sacrosanct are pretty foundational to said order


Government debt that is denominated in a currency that the government can create at any time is hardly the same as the debt you or I have.

Governments do not actually borrow from anyone if they can create their own money. They provide a facility for the rich to park some of their immense wealth but that is not where the money comes from, those individuals accumulate they do not create money.


You are ignoring the part where I said they would actively chose to not pay the debt.

I understand how nation states who print their own currency have a different set of rules than any agent working with currency that they do not control the monetary supply of.

None of that changes the fact that they can _choose_ to default


Because they know the cost of everything but the value of nothing.


In my opinion that's literally what we're aiming for, whether or not intentionally.


I think if you're overly using dynamic scoping in clojure you are probably doing it very wrong.

I used clojure professionally for a number of years and I can't remember ever really using it.


Parent's not talking about dynamic scoping in clojure. It's a comparison to designed to elicit a reaction, e.g. "probably doing it very wrong".

I.e. if you're not tracking ownership in new-lang, you're probably doing it very wrong.


It's not a comparison to bash non-ownership as "very wrong", but arguing that lexical scope and ownership are literally the equivalent things.

Lexical scope allows you to reason about scope on a syntactical level.

Take this code:

  let x = 5;

  fn foo():
  return x \* 2

  fn bar():
    let x = 10
    return foo()

  fn baz():
    let x = "hello world!"
    return foo()

  bar()
With lexical scope, you can look at `foo` and you know immediately what it returns, the behaviour of the function is completely local to it and is a syntactical property of the program.

With lexical scope you just don't know what's returned, for all you know x might not even be a number, but could be any type that just happens to be brought into scope.

Ownership is similar, in that you make the existence of a value a local property. If a 'thing' is inside a variable then you can move it somewhere else, but it also means that it is no longer in that variable. And that tracking of where what is, is a syntactical property, just like with lexical scope.

Clojure has software transactional memory. With ownership it wouldn't need half of the machinery (only that for rollback):

  let blocked_accounts = Set()

  let account_bob = new ExclusiveBankAccount(200)
  let account_alice = new ExclusiveBankAccount(600)

  // ExclusiveBankAccount cannot be copied or cloned

  fn transfer(source_acc, target_acc, ammount):
    if source_acc.deduce(amount.copy()):
      target_acc.deposit(amount.copy())

  fn block(acc):
    let acc_identifier = acc.identifier.copy() // identifiers can be copied and are not exclusive
    blocked_accounts.put(acc)
    return acc_identifier

  fn unblock(acc):
    return blocked_accounts.take(acc)

  fn do_invalid_stuff():
    let good_account = new ExclusiveBankAccount(200)
    let bad_account = new ExclusiveBankAccount(200)

    block(bad_account)
  
    transfer(bad_account, good_account) // <- this will fail because bad_account was moved by the block function and no longer exists here, it's invalid syntactically

  fn do_valid_stuff():
    let good_account = new ExclusiveBankAccount(200)
    let bad_account = new ExclusiveBankAccount(200)

    let ident = block(bad_account)
    let restored_account = unblock(ident)
  
    transfer(restored_account, good_account, 100)
The above code makes sure that:

- you can only transfer funds between two accounts in a thread safe way

- you can only transfer funds between accounts that are not blocked

Simply by not allowing something like this on a syntactical level you get a much cleaner understanding of what your code does. After a while you're wondering why we allowed anything else in the first place.

  let x = thing();
  let y = x; // the thing is moved from x to y here
  print(x)
The fact that automatic memory management falls out of this is almost accidental, if you can track where a value is at any given time, you can also track the references to it, and the resources it uses. But that is not what truly makes it amazing, the fact that you can mentally think about digital objects as if they were physical objects is.


Oh right. Enlightening!

It's interesting you mention STM. That's one of the reasons I only dabble in Rust instead of switching over to it completely.

In do_invalid_stuff(),

  block(bad_account)
  transfer(bad_account, good_account) // <- this will fail
This can only be determined syntactically if you and the compiler agree to stay on a single thread, right? I would expect STM to be the thing which safely bridges across threads - since it will realistically a customer will call transfer() but a bank manager will call block().


My sibling comment is explaining this correctly but maybe using a bit too much Rust jargon for the uninitiated.

Put simply: How would two thread get access to the same account at the same time? There would be a point in time where the object would have to be split to end up in both of them, which would be invalid.

This is why you see things like this in rust

  let x = 5;
  let thread = thread::spawn(move || { // <- important bit
    println!("{}", x);
  });

  println!("{}", x); // <- this would fail
The thread uses a 'move closure', which takes ownership of all values from the outside scope that are used inside its body.

Note that a value needs to be marked as `Send` so that it can pass across threads, but most types are.

That's what makes it so powerful. You know that any value you have in any variable, is only in that variable and nowhere else in the "world". Sure that value can be `Copy` and automatically copied, but that's still a new value. Your value can be `Sync` which means that a reference of it is `Send`, but you're not splitting up the value, you're creating and tracking a new reference value that is then send across the thread boundary.


Right, I was thrown by the real world terms. The ownership system (and syntax) isn't doing anything about a blocked account, it's just preventing a use-after-move, which has nothing to do with this-thread-blocks, that-thread-transfers.

(This is also why I walk past Erlang. Such emphasis on share-nothing prevents an account from being shared by a customer and a bank manager.)

So, what is the actual solution for a thread asking if any other thread has blocked(acct1), so that transfer() can proceed or be rolled back?


Pinning objects to single threads and message passing and/or transactions systems. Transactions typically aren’t seen at the language level and are more common within database systems. Typically a thread-per-core design with objects pinned to cores randomly would scale the best even for transactions; you can develop hotspots but in practice you win a lot more by not having to synchronize unless you’re doing cross-object transactions.

I’m not aware of any language that provides these kinds of guarantees statically or what that would even look like. You’re asking for a lot vs where the cutting state of language design is.


Yeah, the idea in the above code is that you can't do anything with an account so long as it's placed in the "blocked" set, because that means that it's effectively taken out of circulation for other operations.

The way I solve situations like this is, by making sure that all accounts are moved to the same thread for a short period of time, which then handles the transaction. Which I find a lot easier to think about. It's essentially sharding by account and flexibly moving the shards into the same single writer context.

But if you want to do things the "old fashioned way" Rust provides plenty of classical synchronisation primitives like `Mutex` or `RwLock`.

You can also combine both approaches, e.g. have a single Mutex<AccountPool> that you can check out accounts from.


> This can only be determined syntactically if you and the compiler agree to stay on a single thread, right?

No. This will fail because in block the ownership of bad_account is lost. You’d need block(&bad_account) or block(&mut bad_account) to pass a reference while letting the transfer succeed. The only way the transfer would succeed silently like that is if Account implemented Copy (the compiler would automatically inject block(bad_account.clone())). This has nothing to do with threading (which Rust would still have protection mechanisms for). It would work identically if the block and transfer were member methods (i.e. bad_account.block(); good_account.transfer_from(bad_account)) since even member methods can be declared to be requiring ownership.


> This has nothing to do with threading

Right, but all the promises of the type system have to lead to a first-class multi-threading experience at some point.

The safest concurrent program isn't a single-threaded program.


It doesn’t automatically solve every possible problem for you just like something like Lean doesn’t prevent you from writing buggy proofs. I’d argue it does lead to a first-class multi-threading experience within the systems programming domain it’s targeting. Even outside systems programming it can be quite surprisingly good and most incorrect code fails to compile to begin with. Is there a language that in your opinion offers a better multi-threading experience?


I think 70's style ACID-ish transactions are the way to go. Single-threaded reasoning safely transfers across to multi-threaded situations.

Parent mentioned them above, argued that that ownership supersedes them,

>>> Clojure has software transactional memory. With ownership it wouldn't need half of the machinery (only that for rollback)

but then only showed a single-threaded demo of ownership. I don't need STM either if I'm only on a single thread.

STM's a good model. You just write code as if it were single-threaded, slap an 'atomically' around the lines which shouldn't be logically divisible, and you're basically done.


I'm not sure how to make the multithreaded case more explicit. The point is that with ownership the multi-threaded case does look exactly like the single threaded case, just with some machinery to "aquire" all the things you want to own snd do the transaction over.

But "aquiring" them just looks like having them all in local scope, so there is no explicit `lock`.

In the transactional case you mentioned there can still be multiple transactions having access to the same values, so you need the explicit transaction semantics. Ownership never has such cases, unless you use locks to manage ownership between threads, but it could also be done via channels, or CSP, or whatever.


Sorry it's probably just me but there is nothing syntactically obvious about ownership to me in the example you gave other than the names you gave things.


It all boils down to forbidding this:

  let x = 5
  do_something_with(x)
  do_something_with(x) // <- boom compiler error
you simply cannot use the same variable twice, because you lost ownership of the value it stores on the first call.

But that's a bit cumbersome, so people add an operator that allows you to derive a new value from a variable without taking the old one. Let's call that one `&`. It takes a variable that would normally only have been allowed to be used once (because using it removes the value from it), and returns a "thing" that borrows the contents of the variable out, and behaves a lot like the original thing, but not quite.

  let x = 5
  do_something_with(&x)
  do_something_with(&x)
And because those derived things can be tracked and only used once just like the original object we can make sure that no two things can derive such borrowed things at the same time in a way that is mutating the original thing. Let's call the operator that only allows one mutable borrow to exist at a time `&mut`.

The ability to track that only one of these can exist at any given moment for any value is based not on some special properties of "borrows" or "references", but because of the ability to uniquely track ANY value.


Also this is not necessarily a universally bad thing.

For example if I need to work through something from first principles as opposed to instant recall I may develop a more novel understanding.

Anecdotally I have found a number of individuals that are great a remembering and therefore excel in some academic situations are the quite intellectual rigid and unable to think beyond what they are told.

But as a disclaimer I'm not advocating marijuana use as it can have detrimental affects on motivation for example.


You are misunderstanding the meaning of working memory. Working memory is very short-term and small; it's what you use when you read the second half of an equation and still remember what the first half said.

A reduction in working memory is a direct reduction in cognitive capability for some/many tasks.


Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: