Hacker News new | past | comments | ask | show | jobs | submit | strongly-typed's comments login

I'm not an expert in Go, and my experience is somewhat limited, however, a few years back I fixed a really subtle bug in a project that was related to the fact that errors _weren't_ being handled correctly. As a relative newbie to Go, the code in the diff[0] didn't appear to be doing anything wrong, until I added some print statements and realized that the numbers were not adding up correctly. IMO, if the returned value had been more like a Rust optional or result type, I think this issue would have either not been a bug in the first place, or it would have been easier to spot the bug.

[0]: https://github.com/semilin/genkey/commit/fafed6744555c5a81fd...

EDIT: The fact that this was a bug at all makes me fear for the rest of the code base. If this one slipped through the cracks, how can I know that the rest of the code base is correct?


> The fact that this was a bug at all makes me fear for the rest of the code base.

The fact that the commit was accepted into a release without any changes to accompanying tests is what is most concerning. You should be afraid.


Is the wrong code in the new part of the diff or in the old one?

Generally getting zero value from a map is a feature, but the code that the diff replaces did look overly complex and fragile to me already tbh


The way you're describing it sounds to me like you are adding failure handling to handle cases where the programmer is intentionally misusing the thing. I would argue that in this type of situation, the error handling is not necessary because it would hide the fact that the thing is being misused.

Or perhaps I'm misunderstanding your comment. When you do `as OrgId` or `as UserId`, where do you envision those casts in ways that would require handling failures?


My point is that the API consumer does not guarantee types (say it's REST or something), so the assumption that the string you send it will always be the right type (or call it "format") seems like a bad one. Unless you control API usage from end-to-end (which kind of defeats the point of an API), you need error checking (or at least exceptions to bubble up).

A lot of times branding is used to mark data received from a database, e.g.: this field is an `OrgId`, but I can do all kinds of things to that string which might make it not-an-`OrgId` at any point. Then, I'll try to reuse it the `OrgId`, and I'll get some weird error or blowup and I'll have no idea why. So the point is that (a) branding is a dubious feature because it can obfuscate soft-type-breakage (I call it "soft" because at the end of the day, we're just dealing with strings), and (b) it still doesn't preclude runtime error checking unless you're okay with blowups.


> My point is that the API consumer does not guarantee types (say it's REST or something), so the assumption that the string you send it will always be the right type (or call it "format") seems like a bad one.

The raw data from the API will not have any of your internal types applied to it yet, it'll be raw bytes or typed `string`. So I don't really see the connection between this and "I will still always be able to do `as OrgId` or `as UserId` so you need to do have some failure handling". Only your own trusted code can do "as OrgId", so... don't do that unless you have an OrgId. And once your own trusted code has made an OrgId, you don't need any runtime checking to see if it actually is an OrgId.

> A lot of times branding is used to mark data received from a database, e.g.: this field is an `OrgId`, but I can do all kinds of things to that string which might make it not-an-`OrgId` at any point.

What kind of things? Strings aren't mutable so you must be making a new string. But a new string will have a type like `string`, not `OrgId`, and then using it as an OrgId won't compile.


> And once your own trusted code has made an OrgId, you don't need any runtime checking to see if it actually is an OrgId.

Right, and once I have a verified OrgId, I'll just keep using the `myOrgId` variable throughout my code, and I don't really need branding. Maybe I can do type aliasing to make the code easier to read (type OrgId = string), but hardline type verification via branding seems moot unless you can make strong runtime guarantees. I mean, don't get me wrong, I think it's a cute novelty, but it doesn't really do anything.

> But a new string will have a type like `string`, not `OrgId`, and then using it as an OrgId won't compile.

Exactly. Maybe I'm wrong, but in a real codebase, I bet branding would probably just confuse people. "Why can't I change the last number of an OrgId?"—well, you see, once you do that, you lose the brand so now you need to manually do `as OrgId`.


> Right, and once I have a verified OrgId, I'll just keep using the `myOrgId` variable throughout my code, and I don't really need branding. Maybe I can do type aliasing to make the code easier to read (type OrgId = string), but hardline type verification via branding seems moot unless you can make strong runtime guarantees. I mean, don't get me wrong, I think it's a cute novelty, but it doesn't really do anything.

I would rather put that information in the type system than in the variable name.

It prevents passing the wrong variable, is that not useful?

> Exactly.

I don't see how what I said agrees with what you said. Making it not an OrgId prevents the weird blowups.

A compilation error because you used the wrong type is not a blowup, it's preventing random blowups.

And you shouldn't be shuffling digits using string code, that's the point. If you have a way to transmute OrgIds, it should be a function that returns an OrgId.

I'd question whether people even need to know OrgId is a string.


> I'd question whether people even need to know OrgId is a string.

I mean, with numbers it's even more confusing (this might be a TS bug?):

    type SpecialNumber = number & { __brand: "SpecialNumber" };
    let n: SpecialNumber = 42 as SpecialNumber;
    
    n++;        // works (but should break)
    n+=1;       // breaks
    n = n + 1;  // breaks
I understand the purpose behind it, I just think it's needlessly confusing and obtuse, and would be curious to see any serious code base that uses branding.


This sounds like an argument against TypeScript in general, no?

e.g. If I am parsing a string to a number via Number.parseInt, I don’t need a “: number” annotation because I can just call the variable “myNumber” and use that.

Branding a string is in many ways an extension on the idea of “branding” my “myNumber” variable as “: number” rather than leaving it as “: any”. Even if the TS type system is easy to bail out of, I still want the type annotations in the first place because they are useful regardless. I like reducing the number of things I need to think about and shoving responsibility off to my tools.


If you have a function addMemberToOrg(memberId: string, orgId: string), you can accidentally call it like this: addMemberToOrg(myOrgId, myMemberId) and nobody will complain. With branded types, the compiler would mark it as an error.


Function signatures already solve that problem. We have all kinds of functions that take two numbers that mean different things (width/height, x/y, etc.). Branding seems like a solution looking for a problem. I just think it's too much overhead and confusion for too little gain.

In fact, a common pattern is to pass fully-qualified objects, e.g. `dimensions = {width: number, height: number}`, which makes mixing up variables even less likely since you have to explicitly specify them.


>Function signatures already solve that problem.

I literally just showed you how they don't. And you even go on to describe a pattern that makes the problem "even less likely" in the next sentence..

>Branding seems like a solution looking for a problem.

You do you.


> Unless you control API usage from end-to-end (which kind of defeats the point of an API)

Isn't this all frontend client bundles that talk to their own private backend API? Those are controlled end-to-end. My company has one, yours probably does too!


The ray traced version is absolutely phenomenal. If you've never seen it or heard of it, check it out here:

- https://github.com/BredaUniversityGames/DXX-Raytracer

- https://www.youtube.com/watch?v=2eQJKFFEc7E


I wish I could play the ray-traced version in my humble linux laptop (。•́︿•̀。)


Don't feel bad with your Linux, my windows 10 won't run it either.


Have you tried the ray traced version? In my opinion it's the best way to play it. https://github.com/BredaUniversityGames/DXX-Raytracer


Thanks for sharing, that’s super cool. I loved Descent when I was a young lad.


What does it run on, and does it use a modern joystick?


If you want to use a non-modern joystick on a modern machine, there are adapters; I have one I like, and if I remember, I'll post the brand when I get home.


I have the Rockfire RM-203.

Only downside is that it is self-calibrating, so if you have a joystick pot that sometimes jumps to very high (or very low I suppose) resistance, it will scale the axis to that very high value; calibration on the PC in that case will give you terrible resolution (since it is quantized before being sent to the PC). I suggest cleaning the pots with DeoxIT if that happens.


I’ve actually played it on my steam deck and it runs well.


No, it’s ròu


This post coincides with a sudden major uptick in cancer related video recommendations on my YouTube feed. I wonder if other people are seeing the same thing.


public discussion of increased prevalence of cancers is definitely en vogue at the moment.


I had to search for this post to find it after it suddenly disappeared.


Curious why you say Ubuntu is not the best. What would you consider better?


I think it's a question of how sharp the tool is, and how 'idiot-proof' it's been made. Ubuntu is as idiot-proof as Linuxes go, but the more knowledgeable you get the more you find use for a sharper tool that fits your domain requirements better.

Debian is molded to the server domain pretty closely; Red Hat / Rocky is ideal in regulatory environments; Alpine when the priority is to be lightweight and reliable; I run Void on my personal machines because it's the most BSD of the Linuxes and stays out of my way when I need to do weird stuff.

If you're a beginner, you can append 'ubuntu' to your google searches instead of 'linux' and get an answer that makes sense. You'll know when to move on from ubuntu when you have to search 'foo bar linux' to get a usable answer.


I tend to append archlinux to google searches to get quality answers from ArchLinux Wiki.


Heh, fair. I should see if that does any better.


Personally I think debian is the best server OS to learn. It's sufficiently similar to ubuntu that you can get away with either one really, they aren't very different as ubuntu is based on debian.


I'd recommend Debian as the uncontroversial Linux server distro.

But for someone who might have some Ubuntu experience and little other Linux experience, running an Ubuntu server might lead to fewer surprises. Ubuntus do ship with things that Debians don't have. I can't name any, since I don't use Ubuntu, but they're the things about which you either say "how nice!" or "how dull!"


Not OP, but ubuntu is among the best documented an supported, which is critical for beginners.

What may not be good about Ubuntu for some is that it's too bloated and not difficult to learn for someone who wants to hang their hat on that.

Moving from Ubuntu to another Debian installation, including debian is no big deal.


Nix?


Can the keyboard shortcuts be modified? One of my personal pain points with other task managers such as Asana is that I can't remap the keyboard shortcuts. This is very important to me since I use alternative keyboard layouts such as Dvorak, Colemak, MTGAP, Graphite, and have continued to experiment with many others.


Yes! Super important feature that I'm realizing we never mention on the website, I'll have to add it. Thanks for bringing that to our attention :)


I recognize this comment has little to do with this post, but as a fellow obscure language aficionado, I just wanted to recommend probably most obscure language I’m legitimately interested in called Modern Indo European. It’s a conlang that takes the reconstructed Proto Indo European and fills in the missing parts to turn it into a usable language.

That’s all, just wanted to give them a shoutout.

———

I’m also an avid language learner myself, and I feel like what has worked for me has been to study a wide range of words with Anki, but then also try to use them in conversation. Often times I wouldn’t remember the words I studied, but it would be “on the tip of my tongue”. Then the speaker would fill in the word, and from then on I would never forget the word.

Finally, I think you guys are also headed in the right direction with the idea of “expression”, because I feel it is often difficult to map words 1 to 1 with a language you already know. Oftentimes the expression will be said differently “my name is” vs “to me the name is”. The idea is to capture a unit of meaning in a set expression and to be able to combine the expressions to communicate.


This sounds like a good topic for an HN submission! what's the best article about it?


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

Search: