So what language would you chose then? Java, PHP, JavaScript? None of the big languages where perfect from day one and it does not really matter, since day one is over already.
Sometimes I think Go was specifically made for Google to dictate its own preferences on the rest of us like some kind of power play. It enforces one single style of programming too much.
Ew, that’s awful. Go is not one of my programming languages but I had always held it in high esteem because Ken Thompson and Rob Pike were involved in it.
Honestly, it does not happen often that I have a ln unused variable that I want to keep. In my mind it is the same thing when wanting to call a function that does not exists. Also my editor is highlighting error Long before I try to compile, so this is fine too for me.
Worse than not having a unused variable check at all? Dunno, the underscore assignment are very visible for me and stand out on every code read and review.
Yes, worse, because now if you want to use the underscore assignment to indicate that you really want to discard that variable - it gets confused with underscore assignments that were put there “temporarily” for experimentation purpose.
Say I’m having some issue with a function. I comment out half the function to see if that’s where the weirdness is. Golang says “unused variable, I refuse to compile this dogshit!” I completely fool Golang by just using _ = foo. Yes, I was correct, that’s where the problem was. I rewrite that section of the code, and test it out, things work perfectly. Only now, it turns out I’m not using foo anymore, and Golang has no idea because I so cleverly fooled it with _ = foo.
Now, something that could be caught by a linter and expressed as a warning is missed by the language police entirely, and may make it into production code.
Police the code that people put into a repository / share with others. Don’t police the code that people just want to test on their own.
The underscore is used in production code too. It’s a legitimate way to tell the compiler to discard the object because you don’t intend to use the pointer/value.
From what I’ve heard from Google employees Google is really stringent with their coding standards and they usually limit what you can do with the language. Like for C++ they don’t even use half the fancy features C++ offers you because it’s hard to reason about them.
I guess that policy makes sense but I feel like it takes out all the fun out of the job.
Just about any place I know that uses C++ also does that with C++ so that’s nothing unusual for C++ specifically. It’s too big of a language to reason about very well if you don’t, so you’ve gotta find a subset that works.
Too many patterns. If you do not do this every author will have a different use of the language and you will have to read a book of documentation each time you change files.
I think this is a good thing. The styles are just opinions anyway and forcing everyone to just follow a single style takes a lot of bikeshedding away, which I really like.
I hate this in C++ when it does this with parameters of an overidden function. I don’t need that specific parameter, but if I omit the variable name, I reduce readability.
I usually merge because I like to see commit history as it happened and because rebasing multiple commits with conflicts is more time-consuming than fixing it in one merge commit.
I do rebase smaller changes though to reduce merge commit clutter and like interactive rebase to clean up my local commit mess before pushing.
I create a new branch locally with git switch --create, pull everything from main, sacrifice a small squirrel, and run the project to make sure everything still works.
If something doesn’t work or I can’t figure out how to resolve conflicts, I quietly switch back to my previous branch like nothing happened. That problem is for future me.
I actually kind of like the error handling. Code should explain why something was a problem, not just where it was a problem. You get a huge string of “couldn’t foobar the baz: target baz was not greebleable: no greeble provider named fizzbuzz”, and while the strings are long as hell they are much better explanations for a problem than a stack trace is.
a desperate fear of modular code that provides sound and safe abstractions over common patterns. that the language failed to learn from Java and was eventually forced to add generics anyway - a lesson from 2004 - says everything worth saying about the language.
The language was designed to be as simple as possible, as to not confuse the developers at Google. I know this sounds like something I made up in bad faith, but it’s really not.
The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt. – Rob Pike
"It must be familiar, roughly C-like. Programmers working at Google are early in their careers and are most familiar with procedural languages, particularly from the C family. The need to get programmers productive quickly in a new language means that the language cannot be too radical. – Rob Pike
The infamous if err != nil blocks are a consequence of building the language around tuples (as opposed to, say, sum types like in Rust) and treating errors as values like in C. Rob Pike attempts to explain why it’s not a big deal here.
Having a Result[T, Err] monad that could represent either the data from a successful operation or an error. This can be generalised to the Either[A, B] monad too.
Its how rust does error handling for example, you have to test a return value for “something or nothing” but you can pass the monadic value and handle the error later, in go you have to handle the error explicitly (almost) all the time.
Here’s an example (first in Haskell then in Go), lets say you have some types/functions:
type Possible a = Either String a
data User = User { name :: String, age :: Int }
validateName :: String -> Possible String
validateAge :: Int -> Possible Int
then you can make
<span style="color:#323232;">mkValidUser :: String -> Int -> Possible User
</span><span style="color:#323232;">mkValidUser name age = do
</span><span style="color:#323232;"> validatedName ← validateName name
</span><span style="color:#323232;"> validatedAge ← validateAge age
</span><span style="color:#323232;"> pure $ User validatedName validatedAge
</span>
for some reason <- in lemmy shows up as <- inside code blocks, so I used the left arrow unicode in the above instead
in Go you’d have these
(no Possible type alias, Go can’t do generic type aliases yet, there’s an open issue for it)
In the Haskell, the fact that Either is a monad is saving you from a lot of boilerplate. You don’t have to explicitly handle the Left/error case, if any of the Eithers end up being a Left value then it’ll correctly “short-circuit” and the function will evaluate to that Left value.
Without using the fact that it’s a functor/monad (e.g you have no access to fmap/>>=/do syntax), you’d end up with code that has a similar amount of boilerplate to the Go code (notice we have to handle each Left case now):
<span style="color:#323232;">mkValidUser :: String -> Int -> Possible User
</span><span style="color:#323232;">mkValidUser name age =
</span><span style="color:#323232;"> case (validatedName name, validateAge age) of
</span><span style="color:#323232;"> (Left nameErr, _) => Left nameErr
</span><span style="color:#323232;"> (_, Left ageErr) => Left ageErr
</span><span style="color:#323232;"> (Right validatedName, Right validatedAge) =>
</span><span style="color:#323232;"> Right $ User validatedName validatedAge
</span>
Swift and Rust have a far more elegant solution. Swift has a pseudo throw / try-catch, while Rust has a Result<> and if you want to throw it up the chain you can use a ? notation instead of cluttering the code with error checking.
The exception handling question mark, spelled ? and abbreviated and pronounced eh?, is a half-arsed copy of monadic error handling. Rust devs really wanted the syntax without introducing HKTs, and admittedly you can’t do foo()?.bar()?.baz()? in Haskell so it’s only theoretical purity which is half-arsed, not ergonomics.
It’s not a half-arsed copy, it’s borrowing a limited subset of HKT for a language with very different goals. Haskell can afford a lot of luxuries that Rust can’t.
It’s a specialised syntax transformation that has nothing to do with HKTs, or the type system in general. Also HKTs aren’t off the table it’s just that their theory isn’t exactly trivial in face of the rest of Rust’s type system but we already have GATs.
It actually wouldn’t be hard writing a macro implementing do-notation that desugars to and_then calls on a particular type to get some kind of generic code (though of course monomorphised), but of course that would be circumventing the type system.
Anyhow my point stands that how Rust currently does it is imitating all that Haskell goodness on a practical everyday coding level but without having (yet) to solve the hard problem of how to do it without special-cased syntax sugar. With proper monads we e.g. wouldn’t need to have separate syntax for async and ?
Note: Lemmy code blocks don’t play nice with some symbols, specifically < and & in the following code examples
This isn’t a language level issue really though, Haskell can be equally ergonomic.
The weird thing about ?. is that it’s actually overloaded, it can mean:
call a function on A? that returns B?
call a function on A? that returns B
you’d end up with B? in either case
Say you have these functions
<span style="color:#323232;">toInt :: String -> Maybe Int
</span><span style="color:#323232;">
</span><span style="color:#323232;">double :: Int -> Int
</span><span style="color:#323232;">
</span><span style="color:#323232;">isValid :: Int -> Maybe Int
</span>
and you want to construct the following using these 3 functions
<span style="color:#323232;">fn :: Maybe String -> Maybe Int
</span>
<span style="color:#323232;">class Chainable f a b fb where
</span><span style="color:#323232;"> (?.) :: f a -> (a -> fb) -> f b
</span><span style="color:#323232;">
</span><span style="color:#323232;">instance Functor f => Chainable f a b b where
</span><span style="color:#323232;"> (?.) = (<&>)
</span><span style="color:#323232;">
</span><span style="color:#323232;">instance Monad m => Chainable m a b (m b) where
</span><span style="color:#323232;"> (?.) = (>>=)
</span>
and then get roughly the same syntax as rust without introducing a new language feature
though this is more general than just Maybes (it works with any functor/monad), and maybe you wouldn’t want it to be. In that case you’d do this
<span style="color:#323232;">class Chainable a b fb where
</span><span style="color:#323232;"> (?.) :: Maybe a -> (a -> fb) -> Maybe b
</span><span style="color:#323232;">
</span><span style="color:#323232;">instance Chainable a b b where
</span><span style="color:#323232;"> (?.) = (<&>)
</span><span style="color:#323232;">
</span><span style="color:#323232;">instance Chainable a b (Maybe b) where
</span><span style="color:#323232;"> (?.) = (>>=)
</span>
restricting it to only maybes could also theoretically help type inference.
I was thinking along the lines of “you can’t easily get at the wrapped type”. To get at b instead of Maybe b you need to either use do-notation or lambdas (which do-notation is supposed to eliminate because they’re awkward in a monadic context) whereas Rust will gladly hand you that b in the middle of an expression, and doesn’t force you to name the point.
Or to give a concrete example, if foo()? {…} is rather awkward in Haskell, you end up writing things like
<span style="color:#323232;">foo x y = bar >>= baz x y
</span><span style="color:#323232;"> where
</span><span style="color:#323232;"> baz x y True = x
</span><span style="color:#323232;"> baz x y False = y
</span>
, though of course baz is completely generic and can be factored out. I think I called it “cap” in my Haskell days, for “consequent-alternative-predicate”.
Flattening Functors and Monads syntax-wise is neat but it’s not getting you all the way. But it’s the Haskell way: Instead of macros, use tons upon tons of trivial functions :)
It’s not. The default sorter does that, because that way it can sort pretty much anything without breaking at runtime. You can overwrite it easily, though. For the example above you could simply do it like this:
I said ESLint. Not Javascript. ESLint is a linter for JavaScript. That’s why I put JavaScript in brackets. Some people don’t know what ESLint is. I’m talking about ESLint the whole time. Its not JavaScript specific but it’s mostly used for JavaScript
You yourself are talking about ESLint. You said that ESLint won’t prevent me from creating unused variables and functions when it clearly does. It won’t even run and throw an error
Edit: ohh it’s a Lemmy bug. The comment didn’t update yet. Originally I said “ES6” then I changed it to “JavaScript” and then I changed it to “ESLint (JavaScript)”
What the post is about is compiler based clean code enforcement. JS doesn’t do this, but your editor in combination with ESLint prevents you from running the program. However this isn’t a general JS thing, just the way your setup works.
Just saw your edit, and yeah, that makes sense as to the confusion.
Either way, your comment enquired as to whether it was “the same” and it still isn’t because for Go it’s a language feature and ESLint is not a language, it just allows you to create similar behaviour for JavaSvript which, by default, does not exhibit that behaviour.
programmer_humor
Newest
This magazine is from a federated server and may be incomplete. Browse more on the original instance.