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

The original paper [0] this article is based on raises a few questions for me. It compares the authors' new technique against StableDiffusion but fails to specify which version of SD they're using for that comparison. It doesn't mention how example outputs were chosen (were they cherry-picked?). For non-square images, they seem to have specifically chosen resolutions that the other models weren't trained to output (e.g., 384 x 512) without also including ones that they were trained on (e.g., 896 x 1152). I wonder how this new technique would compare with all of that accounted for.

[0] https://openaccess.thecvf.com/content/CVPR2024/papers/Haji-A...


That's distinct from the existing per-creation billing in a few ways, with the most obvious being that the existing method is automated while consumable purchases require user input. Trying to create a SKU for every possible per-creation price is also just incredibly janky and hacky in a fundamental way that would never scale and would probably make accounting next to impossible.


I'm not an expert in this stuff, but I did notice that the Ladybird website mentions only accepting unrestricted donations. That doesn't prevent power dynamics from evolving between sponsors and the project, of course, but it at least means that no sponsors get to explicitly demand specific things.


I think it's a valiant cause but even if that's what they claim right now, eventually they will have to weigh whether it's worth taking the $100k donation from sponsor A who doesn't demand working on feature X but just suggests it or risk the future runway of the project.


I think he best protection against that is to have many different sponsors so a single one of them pulling out isn't a death sentence. In that regard, Ladybird already seems to be much better off than Firefox.

Still the concern remains valid and without leadership sticking to strong principles nothing will protect against external influence forever.


Do you mind elaborating on why this approach would be bad in general? It avoids the overhead of creating new classes and wrapping your objects when all you care about is the type-safety that the class would provide.

How would you "approach the problem from the way the language lends itself to be solved"?


Did you... read the article?


It is "erased" in this example in the sense that the hashes are just strings at runtime, and not other objects instead. That's because the `generateHash` function in the article's example uses `as Hash` to tell the compiler "yes, I know I'm only returning a string here, but trust me, this is actually a `string & { [__brand]: 'Hash' }`".

That `as Hash` is known as a type assertion in Typescript and is normally used when the developer knows something info about the code that Typescript can't.


What's nice here is that `makeHash: Hash = (x: string) => x as Hash` checks that `x` actually overlaps with the branded type (string, in this case).

At first it looks like you could just lie and say `x as User` or something but it's not the case.


I'm legitimately confused about why so many people in this thread are showing off template literal types as if actual hash functions just prepend "hash_" onto strings and call it a day. There are a lot of different types of data that don't have a predictable shape that TLTs just don't help with at all.

While the pitfalls of mindlessly slapping `as XYZ` on lines to make it compile certainly exist (when the type definition changes without the areas with `as` being updated, etc), I don't know if branded values are really the place where they pop up. You brand a primitive when you want to differentiate it from other primitives that otherwise have the same underlying type. In that scenario, you can't really change the definition of the underlying primitive, so you can't really run into issues there.


> The other implementation is detailed in this article, and requires the addition of unique field value to objects.

That's not quite what ends up happening in this article though. The actual objects themselves are left unchanged (no new fields added), but you're telling the compiler that the value is actually an intersection type with that unique field. There a load-bearing `as Hash` in the return statement of `generateHash` in the article's example that makes it work without introducing runtime overhead.

I definitely agree about native support for discriminated unions / nominal typing though, that would be fantastic.


Big thank you for clarifying -- I missed that. This approach is far less unsavory that some other attempts that I've seen.


> In this case where the wrong order of parameters was the issue, you can solve it with Template Literal Types

You can solve the issue in this particular example because the "hashing" function happens to just append a prefix to the input. There is a lot of data that isn't shaped in that manner but would be useful to differentiate nonetheless.

> And for `hash.toUpperCase()`, it's a valid program.

It's odd to try and argue that doing uppercasing a hash is okay because the hash happens to be represented as a string internally, and strings happen to have such methods on them. Yes, it's technically a valid program, but it's absolutely not correct to manipulate hashes like that. It's even just odd to point out that Typescript includes string manipulation methods on strings. The whole point of branding like this is to treat the branded type as distinct from the primitive type, exactly to avoid this correctness issue.


The real problem is that hashes as strings is wrong.

Hashes are typically numbers.

Do you store people's ages as hex strings?


> Hashes are typically numbers

If we want to get really pedantic, hashes are typically sequences of bytes, not a single number, so really `UInt8Array` is obviously the best choice here. It wouldn't fix the whole "getting arguments with the same types swapped around" issue though. Without named parameters, you have to pull out some hacks involving destructuring objects or branded types like these.


I think what they meant is that at runtime, you don't end up with objects that look something like:

  {
    "brand": "Hash",
    "value": "..."
  }
which would be the case if you used the more obvious wrapper route. Using this branding approach, the branded values are exactly the same at runtime as they would be if they weren't branded.


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

Search: