|
Yeah, but nobody expects them to be smart.
|
# ? Dec 11, 2023 06:51 |
|
|
# ? Jun 8, 2024 06:43 |
|
I haven't touch golang in a long time and even when I did, I barely started to scratch at it, but my take away from it was, 'This language seems to be extremely opinionated about how it wants you to write code', and then realized this came from it's maintainers, who happen to be extremely opinionated about how they want people to write code. Pass.
|
# ? Dec 11, 2023 08:31 |
It's called being omakase.
|
|
# ? Dec 11, 2023 08:39 |
|
Xerophyte posted:Chalmers University of Technology. Their Parallel Functional Programming course is lovely. I've given guest lectures there now and then, but I never realised they bribed students with alcohol. That is a bold pedagogical technique. In our similar course, we award students who get a top grade a coffee mug with our project logo on it. It is my impression that cheap swag actually does improve student motivation.
|
# ? Dec 11, 2023 12:41 |
|
nielsm posted:It's called being omakase. Nah, omakase means you trust the chef to deliver a good meal with minimal specifications. Golang is all about you having to write out every single step by hand because abstraction is scary, it’s literally the opposite
|
# ? Dec 11, 2023 13:24 |
|
GABA ghoul posted:Now that I think about it, their approach to throwing an exception in case of a missing keys actually makes a lot of sense. For value types anything a GetKey method could return for a missing key could be confused for an actual value, i.e. am I getting back a 0 because the inventory for this productID is 0 or because the productID is not in the hashmap at all?
|
# ? Dec 11, 2023 17:29 |
|
Option #5 is to return both a value and a boolean, which is what C# does with TryGet - albeit with the awkward out-parameter syntax from long before it had decent support for actual tuples. It's equivalent to returning an optional type, just uglier.
|
# ? Dec 11, 2023 17:39 |
|
Option #6 is the JS and Obj-C choice of having two null values, where one can be stored in the map and one indicates no value present in the map. Of those two I think obj-c's approach is the slightly less bad one: collections can't store null and by convention you use the singleton NSNull object instead, which lets failed map lookups return nil. JS's choice of objects storing null and undefined meaning "key not found" gives a nicer map API, but ends up complicating the type system.
|
# ? Dec 11, 2023 17:49 |
|
Option #7: undefined behavior.
|
# ? Dec 11, 2023 17:54 |
|
ultrafilter posted:Option #7: undefined behavior. "Return value will be indexed from the map. In the event the key is outside the map, the return value will be parsed from the memory immediately following the map"
|
# ? Dec 11, 2023 18:03 |
|
leper khan posted:"Return value will be indexed from the map. In the event the key is outside the map, the return value will be parsed from the memory immediately following the map"
|
# ? Dec 11, 2023 18:45 |
|
NihilCredo posted:Option #5 is to return both a value and a boolean, which is what C# does with TryGet - albeit with the awkward out-parameter syntax from long before it had decent support for actual tuples. It's equivalent to returning an optional type, just uglier. what's ugly about it? bool TryGetX(..., out X x) is a fine idiom. It lets you do if (TryGetX(...)) and if ( ! TryGetX(...)), which are nice and expressive.
|
# ? Dec 11, 2023 21:54 |
|
Whatever the syntax, I just don't want to do the key lookup twice
|
# ? Dec 11, 2023 21:57 |
|
ExcessBLarg! posted:Of these, throwing an exception is my personal least favorite. I'm a believer that "exceptions should be used for exceptional situations" and a key-not-present condition is usually a frequent occurrence. The collection itself is not really in the best place to decide whether a key not being present is an 'exceptional situation' or a 'totally expected situation' because collections are very commonly used in both cases. And this isn't a problem with just collections, it's an issue with almost every sort of general library, and so the maxim of "exceptions should be used for exceptional situations" falls apart quickly in practice and as a result it shouldn't be the touchstone you strive to stick to. Instead, a better approach is that 1) your method names should be verbs or verb phases; and 2) the method should throw an exception if that verb was not successfully performed; and 3) the method should not throw an exception if the verb was successfully performed. This approach handles a method named "Get" on a collection. Did you get the requested value? No? Throw an exception. And the C# approach of having a non-throwing "TryGet" alternative. Did you try to get the requested value? Yes? No exception thrown. If you stick to this approach, it's very clear when a method should or shouldn't throw an exception without any ambiguity; and nobody has to make any judgement calls on what an 'exceptional situation' is. And reading code becomes a lot more straightforward because you see a set of actions and unless an exception bopped you out of normal flow control, you know all of those actions definitively will happen. biznatchio fucked around with this message at 22:44 on Dec 11, 2023 |
# ? Dec 11, 2023 22:41 |
|
Hammerite posted:what's ugly about it? bool TryGetX(..., out X x) is a fine idiom. It lets you do if (TryGetX(...)) and if ( ! TryGetX(...)), which are nice and expressive. Out parameters are a leaked implementation detail of how returning multiple values on the stack works in IL. In 99.9% of cases there's no reason to care about the difference between a return value and an out parameter, they should look the same in high-level code. They're less ugly now that you don't need to declare them in advance, but with modern c# syntax you could have the even more expressive form: code:
which is about as good as it gets short of sum types (and I bet those will arrive in c# 18 or so).
|
# ? Dec 11, 2023 23:36 |
|
ExcessBLarg! posted:This is a problem that's faced by every dictionary/map API ever. The options are generally: I like the Python implementation of having 2 different ways to fetch something from a dict: one that raises an exception if the key isn't present, and one that returns a default value. They're both good approaches, sometimes I want the first one, sometimes I want the second.
|
# ? Dec 12, 2023 00:20 |
|
NihilCredo posted:Out parameters are a leaked implementation detail of how returning multiple values on the stack works in IL. In 99.9% of cases there's no reason to care about the difference between a return value and an out parameter, they should look the same in high-level code. The first sentence here is irrelevant and the second sentence is wrong. The most commonplace use of out parameters is as part of a pattern where the return value indicates the status of the requested operation and the out parameter(s) provide detail. These are quite different things, and it's perfectly fine for them to look different. quote:They're less ugly now that you don't need to declare them in advance, but with modern c# syntax you could have the even more expressive form: I find that code you exhibited far more ugly than if (someDict.TryGetValue(...)), and it's un-idiomatic too, given that TryGetValue() and its cousins have existed in the language for far longer than switch-expressions. The "if" formulation is more flexible (what if there's no action to be taken in one branch? what if I want to execute several statements in one or both cases?) You don't seriously use switch expressions to handle a dichotomy when if ... else ... is to hand, do you? I don't want to read code written like that. How would you choose to re-implement the following thoroughly idiomatic method while avoiding TryGetValue(TKey, out TValue)? You may imagine that your tuple-overload exists on Dictionary<TKey, TValue> if you want. code:
|
# ? Dec 12, 2023 00:31 |
|
Seriously? You think this is more expressive? As in, more clearly communicates what is happening? Personally I'd fail such "clever" code in a review. Seriously, take a step back and think about which code example would be more likely understood by someone who doesn't know anything about the language. Hint: one basically just requires understanding English. What I'd advocate for instead is a TValue GetOrDefault(TKey key, TValue defaultValue = default) method to be added to the framework for cases where you don't care whether an entry exists, but at this point it's easier to just do that yourself via extension methods.
|
# ? Dec 12, 2023 00:32 |
|
Y'all are spelling "return optional<T>" really weirdly.
|
# ? Dec 12, 2023 00:37 |
|
Hammerite posted:How would you choose to re-implement the following thoroughly idiomatic method while avoiding TryGetValue(TKey, out TValue)? You may imagine that your tuple-overload exists on Dictionary<TKey, TValue> if you want. Kotlin would do this as d.getOrPut(key, mutableListOf()).add(value), which is nice.
|
# ? Dec 12, 2023 00:47 |
|
Plorkyeran posted:Option #6 is the JS ... choice of having two null values, where one can be stored in the map and one indicates no value present in the map. Can you please explain what you mean here? I can do `var foo = {undefined: undefined, null: null}` (or `var map = new Map(); map.set(null, null); map.set(undefined, undefined)`). `Object(foo).keys()` / `Object(foo).values()` (or `map.keys()` / `map.values()`) will return `[undefined, null]`. What I mean is: I can see no difference between trying to access a key on a map that has undefined as a value or where such key doesn't exist - both will return undefined.
|
# ? Dec 12, 2023 01:37 |
|
Xarn posted:Y'all are spelling "return optional<T>" really weirdly. Yup. Which, notably, doesn't have a T floating around unless one actually is relevant.
|
# ? Dec 12, 2023 01:57 |
|
canis minor posted:I can do `var foo = {undefined: undefined, null: null}` (or `var map = new Map(); map.set(null, null); map.set(undefined, undefined)`). `Object(foo).keys()` / `Object(foo).values()` (or `map.keys()` / `map.values()`) will return `[undefined, null]`. Third base!
|
# ? Dec 12, 2023 05:22 |
|
SirViver posted:Seriously, take a step back and think about which code example would be more likely understood by someone who doesn't know anything about the language. Hint: one basically just requires understanding English. This is an exciting line of thinking! Why not take it further? Why should we assume that the future reader of the code will know what a computer is, what the problem domain is, or even what they are supposed to be doing?
|
# ? Dec 12, 2023 09:49 |
|
You say that as if it weren't already the case
|
# ? Dec 12, 2023 11:35 |
|
My style guide is based on the assumed context of my code being used in a stock photo, that stock photo being used in a printed newspaper, that newspaper being used for wrapping by a fishmonger, and the buyer of a fresh salmon idly glancing at the code while riding the bus home.
|
# ? Dec 12, 2023 12:20 |
|
starting to get a sense of why the "well-intentioned python programmer who picked up Rust last week" demographic considers Option to be magical
|
# ? Dec 12, 2023 14:06 |
|
CPColin posted:Kotlin would do this as d.getOrPut(key, mutableListOf()).add(value), which is nice. It's nice that it's a one-liner and it would be nice if the C# standard library had something like Python's DefaultDict. But isn't that always constructing a new list even when none is required? Or is Kotlin cleverer than that? (or maybe there's some singleton-empty-list plus copy-on-write cleverness or something, I don't know Kotlin or Java at all)
|
# ? Dec 12, 2023 14:17 |
|
I don't think that code compiles, the correct code is d.getOrPut(key, {mutableListOf()}).add(value). Which doesn't construct a new list each time, but might create a new short-lived lambda (which is lazily called if the default value is needed).
|
# ? Dec 12, 2023 14:35 |
|
Dijkstracula posted:starting to get a sense of why the "well-intentioned python programmer who picked up Rust last week" demographic considers Option to be magical Horror from the thread and all, but I never understood the appeal of option types. Doesn't give any more information than a pointer. You don't solve the problem of having null data. And you potentially confer that problem on to _value_ types, which implies you don't understand how data enters your system. Oh you have to check HasValue everywhere instead of != NULL. Wow great good job.
|
# ? Dec 12, 2023 16:15 |
|
The thing with using pointers is that a) they are reference types and therefore have to refer to something and therefore you have to allocate and free it which is annoying and b) they have more than just null as invalid because of use-after-free at least. Option types narrow that to valid or invalid only and don’t require allocation
|
# ? Dec 12, 2023 16:22 |
|
Jabor posted:I don't think that code compiles, the correct code is d.getOrPut(key, {mutableListOf()}).add(value). Yeah, you're right, I missed that it's a lambda
|
# ? Dec 12, 2023 16:24 |
|
leper khan posted:Horror from the thread and all, but I never understood the appeal of option types. Doesn't give any more information than a pointer. See here for a pretty good explanation.
|
# ? Dec 12, 2023 16:25 |
|
It solves the problem by clearly marking where it's possible to have None values, and crucially, makes it impossible to return them or pass them in to places that don't accept an Option<>. They often also come with lots of conveniences for common tasks like "if the result is present then transform it in this way, otherwise just leave it as None". Some languages try to do a similar thing for null by having some special syntax (e.g. the ?. and ?? operators), but it's pretty ad-hoc and limited compared to the more rigorous treatment a proper option type has. If you find-replace to wrap all your types in Option<> like a complete idiot and now you have potentially-None values everywhere then sure you're gonna find them sucky, but that's entirely a you problem.
|
# ? Dec 12, 2023 16:28 |
|
Phobeste posted:The thing with using pointers is that a) they are reference types and therefore have to refer to something and therefore you have to allocate and free it which is annoying and b) they have more than just null as invalid because of use-after-free at least. Option types narrow that to valid or invalid only and don’t require allocation You shouldn't have a need for a null value type IMO. Though sure that's a real use case of something they do that's different. How is use after no longer has value different than use after free?
|
# ? Dec 12, 2023 16:29 |
|
Jabor posted:It solves the problem by clearly marking where it's possible to have None values, and crucially, makes it impossible to return them or pass them in to places that don't accept an Option<>. It's not a me problem, it's an everywhere I've ever worked problem. Don't blame me for all the poo poo I have to deal with.
|
# ? Dec 12, 2023 16:33 |
|
leper khan posted:Horror from the thread and all, but I never understood the appeal of option types. Doesn't give any more information than a pointer. leper khan posted:Oh you have to check HasValue everywhere instead of != NULL. Wow great good job.
|
# ? Dec 12, 2023 16:44 |
|
ExcessBLarg! posted:Null references subvert the type system, while option types enforce it. If they happen sooner, you're converting from a nullable or reference type to a non-nullable value-type. Checking for a value has similar semantics and failing to do that in both cases throws an exception.
|
# ? Dec 12, 2023 16:51 |
|
leper khan posted:Horror from the thread and all, but I never understood the appeal of option types. Doesn't give any more information than a pointer. leper khan posted:Oh you have to check HasValue everywhere instead of != NULL. Wow great good job. If you're using Options in the if o.HasValue() { var t = o.get(); var u = that_fn(t); ... } style then yes it's no better, but that's not how they're really intended to be used. But, even without programming in that style, the entire point of an Option<T> is to enforce statically that you know you have a T value to operate on, either via combinators like the above, or pattern matching on both None or Some(t:T), or get_or_else(your_own_default_value) or whatever. With a T* you have to remember to do the null check yourself and the language is not there to help you if you get it wrong. Dijkstracula fucked around with this message at 17:02 on Dec 12, 2023 |
# ? Dec 12, 2023 17:00 |
|
|
# ? Jun 8, 2024 06:43 |
|
leper khan posted:You shouldn't have a need for a null value type IMO. Though sure that's a real use case of something they do that's different.
|
# ? Dec 12, 2023 17:02 |