6 ms·
This: "(b Box[T]) Map[U any](f func(T) U) Box[U]" is the type of cognitive weight I was happy that Go avoided.
by baalimago 2mo ago
This: "(b Box[T]) Map[U any](f func(T) U) Box[U]" is the type of cognitive weight I was happy that Go avoided.
- adrianmsmith 2mo agoI never understood the convention of using single letter names for generic parameters. I guess this started in C++ and every language has copied that convention. I think that code would be a lot easier to read if the types were called IN and OUT or In and Out or TIn and TOut or something like that.
- jiehong 2mo agoWe all know letters are expensive ^^
- phplovesong 2mo agoIm pretty sure it came from the MLs, where you usually have a/b/c instrad of the T,U etc combo. I dont find it confusing, as its pretty clear that it only an placeholder. In generics the name usually does not matter or is REALLY hard to name so that it makes sense. More specifically in Go where you have interfaces, concrete types and generics.
- asQuirreL 2mo agoFairly sure it would predate even that, and go all the way to lambda calculus, and predicate logic before that, and that's where my knowledge stops and somebody else can tell us where the current conventions around variables in logic and mathematics come from.
- teh64 2mo agoIn ocaml (and I assume SML) it helps that the generic types have a `'` before them, so val map : ('a Box) -> ('a -> 'b) -> 'b Box
- spockz 2mo agoCompletely agree and I personally name generic type parameters as I would name types and parameters. It helps a lot.
- Someone 2mo agoFor maps, a convention is to use K and V for key, respectively Value. I think that’s best as you’ll soon learn the “single-character capital letter ⇒ generic parameter” convention
- Laurel1234 2mo agoIn C# this is the convention.
- eterm 2mo agoIt's a mix, because some stuff tends to just use `T`, but there's better descriptors elsewhere. There's IList<T> but Task<TResult> There's Action<T1, T2, T3, T4, T5, T6> but also Dictionary<TKey, TValue> and Map<TIn, TOut> This stuff kind of "makes sense" once you're used to it, because it's difficult to say what IList<T> ought to have been called otherwise, IList<TContainee> is a mouthful, and Action<T1,...> simply suffers from the inability to specify an unknown number of generic parameters. https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ilist-1?view=net-10.0 https://learn.microsoft.com/en-us/dotnet/api/system.collecti... https://learn.microsoft.com/en-us/dotnet/api/system.action-2?view=net-10.0 https://learn.microsoft.com/en-us/dotnet/api/system.action-2... https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=net-10.0 https://learn.microsoft.com/en-us/dotnet/api/system.collecti...
- setopt 2mo agoI believe Haskell did that for decades before C++.
- logicchains 2mo agoHaskell was created in 1990, five years after C++.
- toinebeg 2mo agoI often use whole word for type annotation, when I can find meaningfull ones. I just type them in all caps to stay close to the convention. I guess the single letter thing is laziness for a part. It's not simple to find words that represent the abstract idea behind the generic type without narrowing the possibilities. For array function, the Key Value from the sibling comment work but for more complex use case, it get complicated.
- wwalexander 2mo agoSwift generics tend to idiomatically use longer names, like Element or View or Content.
- fooooor 2mo ago[flagged]
- majewsky 2mo agoI'm not necessarily disagreeing, but I'm following the single-letter convention because "TIn" or "InputType" looks too much like an exported symbol. Whenever I tried to write type arguments like that, this threw me off, so I reverted to single letters.
- kitd 2mo agoIt looks more reasonable (literally lol) with syntax highlighting though.
- fooooor 2mo ago[flagged]
- twsted 2mo agoI really understand your feeling, I escaped from C++ years ago when I was overwhelmed by meta programming (initially i loved it). But anyway I find this in Go much more bearable.
- zerr 2mo agoAs a C++ (including modern) developer for more than 20 years, I had written a "template" keyword only for a handful of times. Maybe once in every 5 years on average... :) Unless you are a compiler/stdlib vendor or contributing to Boost, there are features that you just don't use it daily.
- majewsky 2mo agoBut you would still have to deal with the mental load associated with having templates in your stack. I don't know if C++ is still as bad as it was back in my day about vomiting 20-line error messages at you because "std::vector<std::string>" or something has a billion implied template arguments. That was definitely a cost that one had to bear even if one never used the "template" keyword.
- zerr 2mo agoAh, yes, it is much better now!
- fauigerzigerk 2mo agoIt's hard to avoid, because (naming aside) the cognitive load is caused by higher order functions, which are hard avoid without causing massive code duplication. I understand the desire to keep things concrete and avoid high level abstractions, but it's a decision not to automate stuff that can easily be automated. It runs counter to the basic instincts and purpose of our field/industry. That's why it never sticks.
- Pay08 2mo agoLisp manages it. Even if you do use type annotations.
- fauigerzigerk 2mo agoNo. This cognitive load is conceptual. You can't avoid it by using slightly different syntax.
- stingraycharles 2mo agoI think the problem is that the Box / Map / any stuff and all the explicit declarations that Go requires makes it harder. In Haskell as well, you can let the compiler infer a lot of things but that doesn’t appear to be the case with this example. I’d want the compiler to infer things, but that - I think - is at odds with Go desiring a fast compiler, which I also understand.
- Pay08 2mo agoI was about to mention Haskell as well, I feel like it also avoids this sort of cognitive load. Maybe it's something to do with the languages being designed as functional languages instead of languages with functional components.
- hnlmorg 2mo agoHonestly, I’ve written some applications that, on paper, should be the perfect candidate for generics. And yet I can still count on one hand the number of times generics have saved me from massive code duplication. Most of the time generics might be useful, I’ve ended up needing reflection too anyway. And at that point, I’m really no better off for generics.
- dvdkon 2mo agoMaybe it's just familiarity, but I think it would look a lot more comprehensible with some punctuation. Just because a syntax is formally unambiguous doesn't mean it looks that way to humans. func (b: Box[T]).Map[U: any](f: func(T) -> U) -> Box[U]
- scotty79 2mo ago> (b Box[T]) Map[U any](f func(T) U) Box[U] Map method of b (of type Box[T]) that takes f (of type function that takes value of type T and returns value of type U (which could be any type)) and returns value of type Box[U] is defined as follows return Box[U]{v: f(b.v)} func[U any] b:Box[T].Map(f:func(T)->U)->Box[U]: return {v: f(b.v)} func[U any] Box[T].Map(f:func(T)->U)->Box[U]: return {v: f(this.v)} // maybe all of the types could be inferred from usage? func Box[].Map(f): return Box[]{v: f(this.v)} Eh... I think you'd need to avoid generics altogether.
- inigyou 2mo agothat's literally what Go was supposed to do! If I want a language like C++, I know where to find a language like C++ (it's C++).
- wannabe44 2mo agoMap/Filter/Reduce is a bad example for an imperative language. But look at slices and maps packages, or the new proposal for container types. There are many good examples how generics are like salt to food. Another example is errors.AsType.
- teh64 2mo agoI think naming conventions might help: (b Box[InType]) Map[OutType any](transformFunction func(InType) OutType) Box[OutType] Same in Python: def map[U](self, f: Callable[[T], U]) -> Box[U] vs def map[OutType](self, transform_function: Callable[[InType], OutType]) -> Box[OutType] and Java: public <OutType> Box<OutType> map(Function<InType, OutType> transformFunction) vs. public <U> Box<U> map(Function<T, U> f)
- mseepgood 2mo agoIt's not good Go code anyway. In Go you would use a for loop. Just because Go has generics nowadays doesn't mean you should abandon good taste and write ML/Haskell/Rust/C# in it.
- wannabe44 2mo agoSure it's a bad example. But you can't simplify something like this without losing type safety: SortBy[T, K comparable](slice: []T, key: func (T) K)
- HumblyTossed 2mo agoRight? Sigh. I really dislike this. There are 37000 programming languages, stop forcing every single one that gets popular to look like this.
- majewsky 2mo agoIt's almost as if the choices that make languages "look like this" are the ones that are generally associated with a more productive development workflow.
- HumblyTossed 2mo agoActually no. It’s dev FOMO. A tremendous amount of go has been written without all this. Go has just gotten more popular so devs want to make it into something they’ve used before instead of learning to use the language the way it is.
- JyB 2mo agoThat's funny because that's factually untrue. Go's early popularity and professional adoption were largely driven by its perceived productivity boost linked in part to the small surface area of its spec, long before this subpar generic implementation was forced into it.
- zerr 2mo agoWe often forget that our profession (computer programming) belongs to STEM. Some (like Go 1.0 :)) wish to think it is Arts & Humanities. The sooner we realize that yes, it is OK and actually expected to bear a cognitive weight of "(b Box[T]) Map[U any](f func(T) U) Box[U]" the sooner we get back to reality... :)
- thebytefairy 2mo agoPeople should stop using these simplified high level programming languages with low cognitive weight, like Go. I only write assembly. ;)
- red_admiral 2mo agoJust because we can, doesn't mean we have to. I'd prefer to have some more brain-cache free to concentrate on the problem I'm trying to debug rather than doing type resolution in my head.
- yladiz 2mo agoPlease. I’m sorry, but you kind of can’t avoid needing to think about types unless you use a language like JavaScript which is super loose with its type conversions, and you especially can’t avoid in a language like Go. With generics in Go you don’t even need to prefill the types like you go with a lot of other cases, so I’m dubious about the cognitive overhead.
- kfuse 2mo agoNo need to insult JavaScript. In two out of three times the "JavaScript" written will be something like: interface Box<T> { value: T } function map<T, U>(input: Box<T>, func: (value: T) => U): Box<U> { return { value: func(input.value) } }
- 4ndrewl 2mo ago(b Box[T]) Map[U any](f func(T) U) Box[U] _is_ for the Arts and Humanities. Unless you're writing assembler in vim you're not STEM.
- red_admiral 2mo agoIndeed, we're now one step away from monads. I know https://go.dev/doc/effective_go https://go.dev/doc/effective_go hasn't been updated for while, but it also seems to have been forgotten. "Go is an open-source programming language that focuses on simplicity ..." the page begins.
- inigyou 2mo agoApparently the people responsible for the simplicity retired. Since it's Google, some new people want to be promoted for adding features to Go.
- treyd 2mo agoYou've already been able to badly implement monads in Go for 10+ years. Why wouldn't you be able to implement them in a way that the compiler can enforce correctness of? If you don't want it don't use it. It's that simple.
- fooooor 2mo agoNo it’s definitely not that simple. Code are read more often than written, no one works in a vacuum, especially not in open source. Also, when in Rome...
- golem14 2mo agoIf a project's owner feels that strongly about not using generics, that's a choice. No dependencies using generics, no generics allowed in PRs etc. Perfectly doable. Also, screw those Romans ;)
- skywhopper 2mo agoIs it worse than having to create endless functions for each type pair? (b IntBox) MapToStringBox(f func(int) string) StringBox (b IntBox) MapToBoolBox(f func(int) bool) BoolBox (b StringBox) MapToIntBox(f func(string) int) IntBox Etc etc etc? The T, U, and f names are the cognitive load here, because they are meaningless variables. For a specific solution, those would have meaningful names that would make it easier to understand.