3 ms·
Maybe I am misreading, but the Little Types section I think recommends an ugly habit, especially for libraries. When calling this "func Set(ID, Width, Height,
by axaxs 11y ago
Maybe I am misreading, but the Little Types section I think recommends an ugly habit, especially for libraries. When calling this "func Set(ID, Width, Height, Color)", assuming I acquire integers through user input, this means my code would then look like -
extLibrary.Set(extLibrary.ID(id), extLibrary.Width(width), extLibrary.Height(height), extLibrary.Color(color)), which frankly is a nightmare. I'd expect the function to take 4 integers, then convert internally if needed.
Further, it makes Godoc more confusing, since now I have to search around to figure out what each of these custom types is.
- jerf 11y agoAs a general rule, convert incoming input (user or otherwise) to the correct type as quickly as possible, and convert it to the outgoing type as late as possible. When programming in this style, for you to reach the point in the code where the Set call is being made and for you to still have 4 "int"s almost certainly means you should have converted them earlier. Generally you've got a validation phase somewhere, which is where this should be converted, which means that your "Width" isn't just "what happended to be in the input" but is a "validated Width" as well. Since it floats on the validation code it's often not even 1 extra line of code, just another token that I mentally model as sort of "sealing" the fact that this is a good value. This rule goes way beyond "tiny types"; for instance you will drive yourself insane in the web world if you don't do this, with double-decodings and double-encodings quite challenging to prevent with any other policy. [1] "Further, it makes Godoc more confusing, since now I have to search around to figure out what each of these custom types is." Absolutely yes. In any language where this is possible and easy, I do wish the auto-doc systems either automatically, or with some metadata (preferably a label on the type somehow), would make it clear when something is just a one-layer wrapper around a simple type in all the places where it appears in the docs, without having to click through. I consider it worth the cost, but this is definitely an entry on the cost column that is accidental, not essential. This is especially true when the base type is numeric, which means that Go will let you pass in a literal number in the source code, but that is not always your first expectation. [1]: In a statically-typed language you can also play games with having the type label what "layer" of decoding the value is currently in, which works but gets more tedious. This is theoretically possible in a dynamic language, but goes against the grain, since generally people program with just 'strings' in those languages.
- carbocation 11y agoIn this example, Set() is in the public API, so it is exposed to the end user. I am interested both in your approach and in axaxs's concerns, and I'm wondering if there is resolution to be had.
- rpedela 11y agoUsing little types is fine, but I prefer to not use them nor do I recommend them. I have dealt with several large C/C++ codebases which used them extensively. It often took too much time to find the declaration to see the real type. Then I would forget and repeat the process again and again and again. Overall they wasted more time than they saved. Some of the fancy IDEs help with this problem, but not everyone likes using them so you can't count on that as a solution.
- wtetzner 11y agoI suppose it depends on what you're using the tiny types for. For example, if you have a unique ID type, you might have a function called nextId(), and it gives you a new UniqueId. But it really doesn't matter what the "real" type is. It could be an int, or maybe a UUID, or maybe generated string.
- rpedela 11y agoIt could matter, depends what you are doing. But yes your larger point that they can be useful sometimes is true. I would generally recommend that tiny types should be used sparingly when there is a good reason rather than most or all of the time like the OP seemed to be suggesting.
- echlebek 11y agoHi, I enjoyed your article. You have some good points about tiny types, but if you read through the code in the Go standard library, you won't find this style applied in very many cases. Typically, only if one wishes to attach a method to a built-in. As another poster rightly pointed out, it complicates the documentation quite a bit, and I think a lot of Go programmers would consider it pointless abstraction. // NewBox creates a new Box. It returns an error if length // or width are less than or equal to zero. func NewBox(length, width float64) (Box, error) { ... } Consider the simplicitly of NewBox. We may omit a Length and Width type, and any validation of these by stating the expectations of the inputs in the documentation. So, the API is dramatically simpler. The expectation is that the programmer will not pass invalid arguments to NewBox, by way of common sense or reading the documentation. Programmers who pass in positive numeric literals may even opt to ignore the error returned, since the documentation states the the only reason an error would be returned is if the input parameters were <=0. Go puts a big emphasis on documentation, and this often compensates for lack of power in its type system.