Live data from Hacker News

Use Your Type System

dzombak.com

171–180 of 357 posts

Re: Use Your Type System

#171

Swift has a typealias keyword but it's not really useful for this since two distinct aliased types with the same underlying type can be freely interchanged. Wrong code may look wrong but it will still compile. Wrapper structs are the idiomatic way to achieve this, and with ExpressibleByStringLiteral are pretty ergonomic, but I wonder if there's a case for something like a "strong" typealias ("typecopy"?) that indicat…

This sounds elegant in theory but very thorny in practice even with a standards change, at least in C++ (though I don't believe the issues are that particular to the language). Like how do you want the equivalent of std::cout << your_different_str to behave? What about with third-party functions and extension points that previously took strings?

Isn't that where C++20 concepts come in?

Re: Use Your Type System

#173

Earlier quoted context omitted.

I don't see this having much to do with OOP vs FP but maybe the ease in which a language lets you create nominal types and functions that can nicely fail. What sucks about OOP is that it also holds your hand into antipatterns you don't necessarily want, like adding behavior to what you really just wanted to be a simple data type because a class is an obvious junk drawer to put things. And, like your example of a prob…

> Even in an OOP language you probably want `UUID.from(string): Maybe `, not `new UUID(string)` that throws. One way to think about exceptions is that they are a pattern matching feature that privileges one arm of the sum type with regards to control flow and the type system (with both pros and cons to that choice). In that sense, every constructor is `UUID.from(string): MaybeWithThrownNone `.

The best way to think about exceptions is to consider the term literally (as in: unusual; not typical) while remembering that programmers have an incredibly overinflated sense of ability.

In other words, exceptions are for cases where the programmer screwed up. While programmers screwing up isn't unusual at all, programmers like to think that they don't make mistakes, and thus in their eye it is unusual. That is what sets it apart from environmental failures, which are par for the course.

To put it another way, it is for signalling at runtime what would have been a compiler error if you had a more advanced compiler.

Re: Use Your Type System

#174
post #34

Earlier quoted context omitted.

There seem to be two competing nomenclatures around strong/weak typing where people mean static/dynamic instead.

Some people mistakenly call dynamic typing "weak typing" because they don't know what those words mean. PSA: Static typing / dynamic typing refers to whether types are checked at compile time or runtime. "Static" = compile time (eg C, C++, Rust). "Dynamic" = runtime (eg Javascript, Ruby, Excel) Strong / weak typing refers to how "wibbly wobbly" the type system is. x86 assembly language is "weakly typed" because regis…

It's strongly typed, but it's also duck typed. Also, in ruby everything is an object, even the class itself, so type checking there is weird.

Sure it stops you from running into "'1' + 2" issues, but won't stop you from yeeting VeryRawUnvalidatedResponseThatMightNotBeAuthorized to a function that takes TotalValidatedRequestCanUseDownstream. You won't even notice an issue until:

- you manually validate

- you call a method that is unavailable on the wrong object.

Re: Use Your Type System

#175
post #73

Earlier quoted context omitted.

The “Stop at first level of type implementation” is where I see codebases fail at this. The example of “I’ll wrap this int as a struct and call it a UUID” is a really good start and pretty much always start there, but inevitably someone will circumvent the safety. They’ll see a function that takes a UUID and they have an int; so they blindly wrap their int in UUID and move on. There’s nothing stopping that UUID from…

> This is where the concept of “Correct by construction” comes in. This is one of the basic features of object-oriented programming that a lot of people tend to overlook these days in their repetitive rants about how horrible OOP is. One of the key things OO gives you is constructors . You can't get an instance of a class without having gone through a constructor that the class itself defines. That gives you a way to…

[deleted]

Re: Use Your Type System

#176
post #25

Earlier quoted context omitted.

Yep. For this reason, I wish more languages supported bound integers. Eg, rather than saying x: u32, I want to be able to use the type system to constrain x to the range of [0, 10). This would allow for some nice properties. It would also enable a bunch of small optimisations in our languages that we can't have today. Eg, I could make an integer that must fall within my array bounds. Then I don't need to do bounds ch…

Ada has this ability to define ranges for subtypes. I wish language designers would look at Ada more often.

Academic language designers do! But it takes a while for academic features to trickle down to practical languages—especially because expressive-enough refinement typing on even the integers leads to an undecidable theory.

Re: Use Your Type System

#177

Type systems, like any other tool in the toolbox, have an 80/20 rule associated with them. It is quite easy to overdo types and make working with a library extremely burdensome for little to no to negative benefit. I know what a UUID (or a String) is. I don't know what an AccountID, UserID, etc. is. Now I need to know what those are (and how to make them, etc. as well) to use your software. Maybe an elaborate type sy…

foo(UUID, UUID); foo(AccountId, UserId);

I'd much rather deal with the 2nd version than the first. It's self-documenting and prevents errors like calling "foo(userId, accountId)" letting the compiler test for those cases. It also helps with more complex data structures without needing to create another type.

  Map>
  Map>

Re: Use Your Type System

#178
post #38

Earlier quoted context omitted.

FYI: Ruby is strongly typed, not loosely. > 1 + "1" (irb):1:in 'Integer#+': String can't be coerced into Integer (TypeError) from (irb):1:in ' ' from :168:in 'Kernel#loop' from /Users/george/.rvm/rubies/ruby-3.4.2/lib/ruby/gems/3.4.0/gems/irb-1.14.3/exe/irb:9:in ' ' from /Users/george/.rvm/rubies/ruby-3.4.2/bin/irb:25:in 'Kernel#load' from /Users/george/.rvm/rubies/ruby-3.4.2/bin/irb:25:in ' '

irb(main):001:0> a = 1 => 1 irb(main):002:0> a = '1' => "1" It doesn't seem that strong to me.

These are two entirely different a's you're storing reference to it in the same variable. You can do the same in rust (we agree it statically and strongly typed, right?):

let a = 1;

let a = '1';

Strongly typing means I can do 1 + '1' variable names and types has nothing to do with it being strongly typed.

Re: Use Your Type System

#179
There are benefits of such things, especially if it can be handled by the compiler so that it does not make the code inefficient. In some cases it might even automatically convert the type, but often it is better to not do so. Furthermore, there may be an operator to ignore the type and use the representation directly, which must be specified explicitly (in order to avoid bugs in the software involving doing it by mistake).

In the example, they are (it seems) converting between Celsius and Fahrenheit, using floating point. There is the possibility of minor rounding errors, although if you are converting between Celsius and Kelvin with integers only then these rounding errors do not occur.

In some cases, a function might be able to work with any units as long as the units match.

> Public and even private functions should often avoid dealing in floats or integers alone

In some cases it makes sense to use those types directly, e.g. many kind of purely mathematical functions (such as checking if a number is prime). When dealing with physical measurements, bit fields, ID numbers, etc, it does make sense to have types specifically for those things, although the compiler should allow to override the requirement of the more specific type in specific cases by an explicit operator.

There is another article about string types, but I think there is the problem of using text-based formats, that will lead to many of these problems, including needing escaping, etc.

Re: Use Your Type System

#180
post #127

In TypeScript you can enable this by using BrandedTypes like this: type UserId = string & { readonly __tag: unique symbol }; In Python you can use `NewType` from the typing module: from typing import NewType from uuid import UUID UserId = NewType("UserId", UUID)

In Python 3.12 syntax, you can use type UserIs = UUID

`type UserId = UUID` creates a TypeAlias, not the same thing (from a type checker's point of view) as a NewType [1].

[1] https://typing.python.org/en/latest/spec/aliases.html

Post reply on HN