Hacker News new | past | comments | ask | show | jobs | submit login
Porting 4.5K lines of C to Go (kowalczyk.info)
245 points by ingve on Aug 4, 2017 | hide | past | web | favorite | 143 comments



> As far as I can tell you can’t step through tests in the debugger

Rant time. What to do when you write a test framework.

1. When test fails, print everything possible: desired expression, desired value, actual exression, actual value, line and file of failing call. When printing line and file, make the format configurable, with the default being the prevailing standard for the system, so that people can get clickable links in their text editor or terminal with a minimum of hassle.

About 90% of the time, this will give people everything they need in order to at least get started on fixing the failed test

2. Don't make it hard to use the debugger. The remaining 10% of the time, people will need to step through the code. Some measurable fraction of the other 90%, they'll also end up needing to do this, because it looked like it was something simple but actually it was more than that. So don't make this hard

3. See steps 1 and 2

This might sound obvious, but it clearly isn't, because I've used several test frameworks that make running in the debugger rocket science, and print literally nothing but your own message (if even that) when the test fails. Like, you do 'assert.equals(x,y)', and it doesn't even show you what the values of x and y are, let alone figure out that maybe "x!=y" would be an obvious thing to print.

This may not sound like a big deal with my stupid little example, but after you've written several tens of these you will start to see my point.


> Like, you do 'assert.equals(x,y)', and it doesn't even show you what the values of x and y are, let alone figure out that maybe "x!=y" would be an obvious thing to print.

That's why pytest is my buddy. You don't use any odd method, you just write `assert x == y` and if it fails it tells you that it fails, and what the operands were:

        def test_py():
            a = geta()
            b = getb()
    >       assert a == b
    E       assert 7 == 467
if you add an assertion message, it just gets added to the output:

        def test_py():
            a = geta()
            b = getb()
    >       assert a == b, "assertion message"
    E       AssertionError: assertion message
    E       assert 99 == 343


It also tends to show local variables and function arguments. It also has --pdb.

This makes debugging without --pdb pretty easy in most cases: http://i.imgur.com/IqRzKnG.png

pytest gets a lot of stuff right, though it also has many faults (like almost any piece of software).


pytest is great, but a just a bit too much magic for me. And I really don't like it when you pass a user defined failure message, you lose all this nice introspection.

With unittest, longMessage = True + my own failure message with the info I want gets me where I need to be. That being said, it is more work/boilerplate.

https://docs.python.org/2/library/unittest.html#unittest.Tes...

Python 3 has subtest, which gives you built in parameterized testing. I have no idea why this was not backported to 2.7, so you must used pytest or nose_parameterized or ddt.

https://docs.python.org/3/library/unittest.html#unittest.Tes...


> pytest is great, but a just a bit too much magic for me.

There isn't much magic there, just an import hook which rewrites the assertion during compilation.

> And I really don't like it when you pass a user defined failure message, you lose all this nice introspection.

Er… you don't, that's what the second snippet shows: if you have a custom assertion message, it just prints the message alongside everything else…


> Er… you don't, that's what the second snippet shows: if you have a custom assertion message, it just prints the message alongside everything else…

Is that a new addition to pytest? I do seem to remember the user defined message stomping over whatever pytest would normally spit out.


That seems to have been fixed in version 2.6.2 released in September 2014[0]:

> Implement issue549: user-provided assertion messages now no longer replace the py.test introspection message but are shown in addition to them.

[0] https://docs.pytest.org/en/latest/changelog.html#id228


cool, thanks for clearing that up for me.


This is because Python makes it excessively easy to analyze call stack. It may be the case for many other VM-based runtimes, like Ruby or JVM, but it's hardly feasible in Go.


> This is because Python makes it excessively easy to analyze call stack.

Wrong. Pytest uses AST rewriting to inject debugging information, introspection has not been the default mechanism for years[0], and was removed entirely in pytest 3.0 (released mid-2016).

> It may be the case for many other VM-based runtimes, like Ruby or JVM, but it's hardly feasible in Go.

Wrong again, see above, as long as you can statically inspect a function body and rewrite an assertion statement you can do what pytest does.

[0] I think rewriting was made the default as soon as it landed back in 2011 but am not actually certain


Wow... I am using Django's stock test runner and that makes me jealous. I've wished I could just write python asserts and have it print useful information.

Of course, this technique only works in dynamic languages. In Go I use testify assert and that is good enough for me.


> Of course, this technique only works in dynamic languages.

Actually, pytest does that by rewriting the AST[0], basically it rewrites the `assert` statement into a more complex form which extracts all the info it needs upon failure. In a statically typed language, you could do that with a compilation hook and some codegen.

There used to be a "reinterpret" mode where it would walk the stack frame and try to understand the expression involved at runtime, that was removed in Pytest 3.0, now the choices are assert-rewriting and plain assert (without any introspection).

[0] http://pybites.blogspot.be/2011/07/behind-scenes-of-pytests-...


> Of course, this technique only works in dynamic language

It can work in any language that supports reflection, dynamic or not.


Or macros, or just direct language (i.e. compiler) support.


Just switch to pytest + pytest-django. It can run Django tests.


Slightly related:

The lack of an easy to use debugger is a huge issue for me when working with go.

Coming from PHP, I spend 90% of my time in the debugger running my code line by lines (with xdebug coupled to phpstorm).

A debugger is especially useful when working with other's people code and large softwares. For example, contributing to large Go projects like Kubernetes can be daunting due to the very steep learning curve. This curve would be greatly reduced if we had a good reliable go debugger, which doesn't seem to be the case at the moment. (I'm aware of delve, but last time I looked, it was still fairly complicated to have it work).



How long ago was that? 'cause it seems pretty easy to get running now, even on Windows.


Delve only works on Linux, OSX, and Windows. Not at all on *BSD.

∙ Delve works "ok-ish" on Linux. Sometimes breakpoints don't fire (for me), but generally it's mostly usable.

∙ Windows and OSX both have issues with (at least) cgo code.

∙ The lack of BSD support is just because no-one's contributed the native bits yet:

    https://github.com/derekparker/delve/tree/master/pkg/proc/native
So, it'll probably get done at some point in the future.

As an example of the problems, a basic search on the Jetbrains issue tracker for Gogland (which includes Delve) shows many, many, many bugs filed.

  https://youtrack.jetbrains.com/issues/GO?q=debugger
eg breakpoints not firing, variables being shown outside their scope, variable names being incorrect, debugging session just hanging (etc)

That being said, the problems are getting looked at and fixed, both by the Delve dev's, the Jetbrains people for things where Gogland can help, and in Go itself when problems originate there (incomplete DWARF info on some OS's for example). Go 1.9 (due for release any day now) helps with the variable scope problems.

Still, it's a long way from "just works" to the level C developers are used to. Though it's improving. Personally, I really wish the "just works" was already here. :D


Have you looked at Delve? I haven't personally tried it, but it works well with VS Code for line by line debugging.

I've strangely found success without even worrying about line by line debugging. Typically, unit tests and the occasional printf debug has done well enough for me.


there is a debugger for Go

https://github.com/derekparker/delve

you can step through things and print stuff out


How is the debug experience in elixir compared to go ?


Erlang/Elixir debugging is world-class. You can directly instrument functions running in production with fairly little overhead.

I highly suggest reading "Erlang in Anger" by ferd/MononQC. It (and his recon library) are indispensable tools for people running Erlang or Elixir in prod.


How on earth is it world class if you can't do line by line or step debugging?


You can? The Erlang debugger has been there since at least R10....


I have to admit, although I use domain specific debuggers sometimes (gdb, debuggers of IDEs), I mostly debug via print-statements, as it just works in most scenarios.

Am I missing out? AFAIK step-back debuggers are still more research (I think Elm has something like this), but otherwise I just want to have system snapshots and there print($foobar) works very well for me across languages.


Elm used to have a crazy time-traveling debugger in elm-reactor[0], but that's been replaced by session-event-export[1].

This is really, really easy to do well when every function is pure and every data structure is immutable, and you use an MVC/Event-sourced/FRP architecture for (more-or-less) all control flow. Of course, Elm enforces all of these things.

I've written / worked on other systems (e.g. accounting / transaction pipelines) in other, less safe languages and implementing a shoddy version of this is really straightforward and immensely useful.

That said, with pure functions and immutable data structures, you don't really need a debugger for the same reasons, or indeed nearly as often, as in less safe languages.

[0]: http://debug.elm-lang.org/ [1]: http://elm-lang.org/blog/the-perfect-bug-report


Yeah, you're missing out. :)

Print statement style debugging can (with effort) get a lot of stuff figured out.

But :), it's often fairly slow to go through that iteration cycle with non-trivial bugs, compared to using a debugger.

With a debugger, you start from the same place in your code you'd put your print statements. But instead of modifying the code to spit out values, the debugger will pass control of the running program to you. You can then go forwards through your program, seeing how the variable values change, which branches are taken, etc.

It'll get you to the same end result as the print statements, but faster.

Also, for complicated/hairy/weird bugs, being able to follow the actual execution can be enlightening which print statements don't really do.


:)

Some time ago I did more Java and used Eclipse heavily and its debugger was really nice, so I share your opinion that you get nice features.

However if you are working on a project (at least that has been my experience so far) that does a lot of multiprocessing (let's say a web service with a processing scheduler in the background calling into processes implemented by different languages) and you may have bugs on several layers using different programming languages, I like the fact that I can use the same concept for all of them and write some custom "debug.log" that I can later analyze (obviously good logging in the first place can help a lot).

But I may check them again: Do modern debuggers work across languages (assuming available source), let's say web-request -> Python -> FFI -> C++?

Previously such scenarios have been non-trivial to debug (although there are some pretty advanced multithreaded code analyzers now), but maybe it has changed for the better.


> But I may check them again: Do modern debuggers work across languages (assuming available source), let's say web-request -> Python -> FFI -> C++?

Gah... no idea. I'm still doing mostly doing single language stuff. :D

The kind of request path you're mentioning there... yeah, I'd probably go back to using print statements too, as I'd have no idea how to hand off from one language to another in some kind of meta-language-debugger. Which would actually be nifty if it exists.


They do for Java-> C++, .NET -> C++ and Swift->Objective-C->C++.


I don't think it's meaningful to compare.

Erlang/Elixir work with immutable values, so when you call a function, assuming it's side-effect free, you always get the same result.

Add to that the fact that there are no global variables (outside of table-like things like ETS), and that you can jump on the running system in an Erlang/Elixir shell and call any exported function and immediately see the results, debugging becomes much, much easier. I can honestly say that I have not needed a stepping-style symbolic debugger since 2008.

And when you find and fix a bug, you can copy the fixed code file (.beam file) to the server, start a remote shell, and do this:

    > l(my_module).
and the new code is running, with no perceptible perturbation to the system.

There are also other tools like dbg that let you monitor calls to specific module/functions based on pattern matches. The representation of these pattern matches is powerful but not so pretty - but it gets the job done.

Nothing's a panacea, and Erlang/Elixir undoubtedly have their warts, but for the Erlang VM's use case - soft real-time, extremely robust, highly concurrent distributed systems - I believe they are peerless.

One of the warts may be that the ecosystem doesn't have a library for every possible scenario, but that's improving rapidly, and in many cases, some of these are trivial to write. On the plus side, the quality of code is generally pretty good.

It is very challenging to describe what a massive level of confidence a hybrid-functional, battle-tested language and environment like this affords a system designer.

So debugging in this environment is very different and hard to compare directly to a line-by-line stepping debugger. Even if such a tool may be available (and things change so quickly these days, I probably missed that), it's easier to do without it.

It's so difficult to convey the experience of using this ecosystem, one cannot do it justice in a HN comment. T be fair, there are so many mature tools in more heavily used ecosystems like (I suppose) Java, that some may give up on Erlang/Elixir prematurely if used to that level of tooling.


Elixir does an amazing job of showing you why a test failed, but as for debugging, up until the latest release, 1.5, there wasn't really a debugger, it had pry, which lets you play around in the environment at the point where pry is called, but you can't step though code. 1.5 introduced a more useful debugging experience, but you can't step line by line, you can only step from breakpoint to breakpoint as far as I'm aware, but I haven't played much with it yet. It's getting there though! I should note that I haven't really lamented the lack of a good debugger, and I came from .NET where I had one of the best debuggers available.


How is the performance of Elixer vs Go? I’ve done some web stuff with Go and I like the performance. Elixir looks interesting. Wanted a reason to give it a try.


They are not paragonable.

Anyhow there are different reason to learn a new languange if ir change the way you think.

For me those languanges were

1. C learn to code

2. Python learn that there are more way to write code than C

3. Clojure keep everything as simple as possible

4. Elixir stuff fail, find a way to deal with it

5. Rust get as much help as possible as soon as possible from the tools that you use (namely the compiler.)

Now in whatever languange I write I bring with me those experiences. So I search to solve the problem in the simplest possible way keeping an eye on the reliability and I let my tools do as much as possible of all the heavy lifting...

All this just to say to give elixir a run that will make you a better r developer :)

Now my code in any languange


Here's a link to benchmarks of different languages including Elixir. https://github.com/kostya/benchmarks


Erlang's graphical debugger works with Elixir since OTP-19 (last year). See this page after the section on Pry:

http://blog.plataformatec.com.br/2016/04/debugging-technique...

It supports stepping, etc.


Great. 1.5 which was just released really helps with pattern matching failures and gives suggestions. I really like testing with elixir.


This is why I love Elixir. It comes with tests as a first-class citizen. Colors! Line numbers! Visual grep! DEVELOPER UX. Check this out:

http://i.imgur.com/qDPE9Pc.png


Came out of private mode on my phone to upvote this.

One key point I'd like to add though is that you must _always_ run git diff before committing anything. There's been enough times where that has saved me from erraneously committing logging statements that are absolutely unnecessary post bug fix.


You can also use a different function for temporary debug prints, and a git pre-commit hook that fails if this function is invoked.

Pros: Easier to insure that your collaborators follow process; your CI server can run it too.

Con: This doesn't catch other issues that a manual `git diff` review will also catch.


Getting into the habit of always adding via `git add -p` is useful to stop this from happening.


Yea commiting by hunks is also super useful. I find a lot of engineers only have a cursory understanding of a lot of git. Rebase vs merge throws a lot of people off and not a lot of people know about git bisect which is just incredibly useful. I think some of it is that people like the graphical nature of things like sourcetree which while offering pretty interfaces don't fully flesh out git. Give me the command line any day.


It's better to do the big commit and then refactor it. Otherwise you won't have tested whether the hunks depend on each other, which means history littered with broken commits.


"Golang drops GOPATH" - I am waiting for this headline.

The biggest hurdle with Golang and working on different unrelated projects that are in their own GIT repo. Golang forces you to have one single path were all the code should reside - GOPATH. Name me one other language that has such a restrictive requirement. Why not offer a config?? Beside that I like everything else about Go a lot.


> The biggest hurdle with Golang and working on different unrelated projects that are in their own GIT repo. Golang forces you to have one single path were all the code should reside - GOPATH.

No one forces you to have one single path where your code should reside. It's just an environment variable, so you can have a GOPATH set per project if you wish. It could be your git root.

> Why not offer a config??

That's what they did, or what am I missing?


Not really. :)

These are the Go .bashrc lines in my Linux dev desktop:

    export GOPATH=${HOME}/go:${HOME}/git_repos
    export GOBIN=${HOME}/git_repos/bin
    export PATH=$PATH:/usr/local/go/bin:${HOME}/go/bin:${HOME}/git_repos/bin
Note the two different paths in GOPATH?

∙ $HOME/go is where "go get foo" puts stuff

∙ $HOME/git_repos is where stuff I'm developing goes (eg $HOME/git_repos/src/github.com/sqlitebrowser/dbhub.io)

The separation means I can backup stuff I'm working on, and exclude the general downloaded Go code from backups as that would just be a waste of space.

Most people do seem to use just one path in GOPATH though, and I remember also doing things that way when first starting out. Not sure why, maybe it's due to some incorrect assumptions given by the Go learning material commonly read when starting out?


Good idea, but it doesn't work for me.

I can't understand the GOPATH restriction, I want to put the Go source code where I want. Think about the following projects (Win/Linux doesn't matter, I use both)

One GOPATH doesn't work when you have several projects in different structures e.g.

* D:\Projects\ABC\repo\server\go\src\

* D:\Projects\XYZ\git\backend\go\src\

* T:\svn\go\src\


Rename Projects to "src" and set GOPATH to D:/.


Yeah, I remember it was a pita for me to change my directory layout habits to work ok with Go. Maybe it'll become more flexible in future. :)


The GOPATH system blows for the learning curve, but it's good for a lot of other reasons, like being very simple to understand and reason about. I don't really enjoy the way Node.js and Python handle package management personally, because it can get complicated. But I've wondered why someone hadn't wired up Go with an npm style package manager, since it would totally be doable.

The Go team does seem to care about the problem though, and they at least made a sane default Go path of $HOME/go, so you can go get right away.


In my project I use "env GOPATH=$PWD go ..." in the makefile, that way it all goes in the current directory. There's probably some good reason this is a bad idea, but go is kinda funky anyway so I make up my own best practices :-)


Have a look at virtualgo

https://github.com/getstream/vg


> As far as I can tell you can’t step through tests in the debugger

Actually, you can, if your IDE lets you. Eg. In Intellij if you're using the Go plugin, you will see little green arrow in the margins of your tests. Set a breakpoint and click the little arrow. The new Gogland IDE has this too.


> Too many people have a bad habit of including “go” in their repository name, presumably to not confuse people with non-go version of the code.

Note that this is actually a perfectly reasonable thing to do. If I had two clients for some particular API, one written for Node and one for Go, it absolutely makes sense to name them "node-whatever.git" and "go-whatever.git".

Part of what makes naming things hard is needing to come up with a more "creative" name for every little module, especially once you throw in the constraint that apparently eight characters is too long and four characters is just right.


It might be necessary to resolve naming conflicts but it's not good.

In Go, package name should be the same as repo name.

Most cases that I've seen are not including "go" in repo name to resolve naming conflict but "just because", possibly cargo-culting others that did it.


What if those people or organisations have a lot of repositories and they want to use a naming convention that reflects that maybe, just maybe, only a subset of their code is written in Go?

I recognise that you don't speak for the entire Go community, but frankly this kind of over-prescriptive preference presented as the only possible way forward is oddly common in what often seems more like a church than a programming language ecosystem.

Go is just a tool, like any other. If I happen to use git for versioning that's my choice, and I (and everybody else) can and should name and arrange repositories as is convenient.

There is no "in Go" to be had here; merely "in my project as makes sense for me".


> What if those people or organisations have a lot of repositories and they want to use a naming convention that reflects that maybe, just maybe, only a subset of their code is written in Go?

I'm not sure I understand how that's an argument in itself for using "go-" or "-go" (or some other version) in a package name. It results in a lack of clarity in how to reference the package: the hypothetical "github.com/stripe/stripe-go" import path probably (and almost certainly) introduces the "stripe" package name, but the the discordance between what you write as an import statement and by what name the package is referenced is unconventional and strange.

There are certainly times when it makes sense. And I agree with the idea that an organization, in most cases, shouldn't come up with a unique name for the Go implementation of, say, an API wrapper, just to satisfy Go convention. But generally speaking the convention should be followed not simply because it's prescribed but because it's sensible to avoid that disconnect between the import statement and the package name.


If you must then you must.

I'm not sure if you actually disagree with me.

Like I said, most of the time I see "go" in project name, it's not because the owner of the repo was in the hypothetical situation you describe.

So unless you think it's ok to add "go" to the name of the repo willy-nilly there's no disagreement.


> In Go, package name should be the same as repo name.

First of all, it's entirely possible that "go" is in the repo name, but beyond that, I regularly see packages (and very popular, mainstream packages) where the package name is not the repo name. The one that comes to mind is github.com/mattn/go-sqlite3 which provides the `sqlite3` package.


Every package dependency should be installed in /usr/lib by the admin or else vendored in our repo using its canonical import path. It's a mistake for "go get" not to do this by default, because the compiler shouldn't even be aware of the name of the repo we got the code from.


The compiler doesn't care. Even for the build tool it doesn't really matter. The one thing that cares about repositories is "go get", a small convenience wrapper around git/hg/svn.


> possibly cargo-culting others that did it.

Agreed, I don't see the point of mentioning the language for compiled, stand-alone programs and standard, C-compatible libraries. The resulting binary is the same (well, of the same nature at least) whichever language it was compiled from.

It only has a use for programs and libraries in interpreted (in the widest sense of the word) languages, which can only be used together with libraries in the same language, or from programs in the same language, or with a specific external runtime or virtual machine.


> However, win32 doesn’t have anything to help you layout things on the screen. Manual positioning is painful.

Ooooh, yes, I remember that. My first experiences with GUI programming was a tiny little of Perl/Tk, then Gtk 2 in Perl, Python, and Ruby.

And then I started working as programmer, doing maintenance on an line-of-business application suite written in C/Win32, and I was shocked people would actually put up with this. Fortunately, the overwhelming majority of my work dealt with the deepest guts of those programs, and I only needed to deal with Win32 directly on a few occasions.


I've started writing some Win32 code for fun and like you I found it a whole different experience from GTK and such. It really is an API rather than a toolkit.

Take scrolling. Scroll bars are provided when requested, but all the interactions have to be implement by the application. There's no 'scroll view' with standard behaviours. This means Microsoft can't easily add new scrolling behaviours as we see with the messy situation around high precision trackpads and touch.

Compare this to Cocoa with its auto layout, data binding and even document lifecycle management.

On the other hand, with Win32 I rarely find myself fighting the toolkit. I'm in control.

Backwards comatibility is also amazing. With my little project I'm targeting Windows 3.1 through Windows 10 with a single binary! I expected most of my effort to go towards supporting old OSes, but in reality I spend more time on modern features like proper per-monitor DPI suport, the gazillion different scrolling behaviours and (conditionally loaded) GDI+ and Direct2D implementations.

Another delightful aspect, although again it comes the beforementioned price, is the speed. My little utility starts instantly and won't skip a beat when you frantically shake the resize grip. Compare that to even the simplest UWP XAML app.


> Another delightful aspect, although again it comes the beforementioned price, is the speed.

That is true, with only a little care, one can write insanely fast and responsive UIs in Win32.


It's funny you mention that. I grew up with the Windows API first and then moved to Tcl/To, and then Swing. I remember wishing I could just manually position things instead of packing into horizontal and vertical boxes. Maybe it is Stockholm syndrome with the Windows API for me.


In my experience, generally, when I have work with some piece of technology that works differently from what I am used to, it is highly frustrating. All the habits that normally would make things easier for me suddenly are working against me.


Which is why already on the Win16 days only masochists would program in straight Win16, let alone when Win32 arrived.

The sane of us were already using OWL (TP, C++), MFC(C++), VB, VCL (C++, Delphi), Visual Fox Pro (xBase).

And the reason why on Windows world C was mostly relevant for kernel and drivers programming, with other languages taking over actual application development.


> only masochists would program in straight Win16, let alone when Win32 arrived

Also, electrical engineers. ;-)

At least I learned a lot about C. And I learned that friendly coworkers make up for a lot of stuff that would drive me insane otherwise.


> "Go doesn’t support xor (^) and or (|) operator on bool. As it turns out, they are not neccessary. x ^ y is the same as x != y. a = a | b can be written as: if !a && b { a = true }"

Well, you don't need subtraction, division or multiplication either (or any number more than 1!), but they're certainly nice to have. Go has some nice qualities, but its choice of omissions continues to fascinate me.


You can put (some of?) your fascination to rest, Go has ||. https://play.golang.org/p/_GKbk_GUAe


Sounds very uneventful... A few porting bugs and language differences but not a lot.

At least it sounds like a great way to learn about flexbox internals.


This was my take too. Some reinforcement about the utility of tests. 4.5k is a relatively small amount of code (particularly in C or similar)


> As far as I can tell you can’t step through tests in the debugger

Really? In Visual Studio Code this is as simple as:

1) Set a breakpoint in your test

2) Hit the "Debug Test" button

See http://wopr.norad.org/~stefan/delve.png for this in action. Look how great this looks. And how you can see all your variables in the upper left section. How you can expand structs and inspect them.

In my experience this works really really well.


Delve and the VSC integration is great. Just note that it's historically been quite buggy and unstable. Worse, for a long time it wasn't practically possible to use Delve on macOS due to changes introduced in 10.12 [1] [2]. It should be better now, though.

[1] https://github.com/derekparker/delve/issues/645

[2] https://github.com/aaronhackney/delve_on_mac/blob/master/REA...


This is what really struck me: "The API is still a bit awkward by Go standards. Too many methods, not enough direct access to variables" Does he really mean that proper go code has direct access to fields rather than encapsulating them???


I would phrase it like that: most languages (C++/Java/.NET) have unhealthy obsession with creating unnecessary setters/getters, paying for it with increased compile time, code bloat, slower code.

In Java/C# land refactoring tools even give one-click access to "create me some setters/getters".

This is not to say that abstracting access with functions has no place, but if all they do is get/set the value, they are unnecessary.


C# and Python (for examples) don't use getters and setters, they use properties which provide the same interface as direct access variables when the need arises for more complexity, client code doesn't need to know about it. Languages without this feature needs to make everything getter/setter if they don't want to break the API when something needs additional logic.


And indeed Go code has its fair share of (otherwise) unnecessary getters because of this.


Python does have properties, which work via decorators @property (which decorates a getter) and @foo.setter.


He literally just said that


So, let's suppose you don't create getters or setters, because all that function does is get/set the value.

Six months later, you discover that you, in fact, do need to do something besides getting/setting the value.

If you created a getter/setter, you could just change its code. If you, instead, had raw access to the field, you now need to update every single instance of client code that called your getter/setter.

Most of the time, your compiler/VM can optimize the getter/setter call away, and a bunch of trivial get/set logic isn't exactly the worst source of cognitive overhead.


There are infinite possible futures but only one now.

Write the best possible code needed today. Anything else is premature abstraction.

I do get the "what if" temptation, I just think it should be actively resisted.


See [YAGNI](http://wiki.c2.com/?YouArentGonnaNeedIt).

A helpful (to me) metaphor is that you're paying for an option. (In the finance sense, not the type system sense.) Sometimes it's worth buying an option, sometimes it's not, but don't think it's free.


A more simple analogy: it's an insurance.

It's certainly wise to subscribe an insurance for your house, but for every single drinking glass you own...?

If it's a tea set you inherited from your great-grand-mother, when you break it the insurance won't un-break it. If your property suddenly needs to be more than a property, chances are that this change will break the LSP or trigger a larger redesign anyway.


This not one of those instances though. The cost of having getters/setters is essentially zero, the potential downside can be big.


Ok, reality time: out of all the getters/setters you've ever written, how many of them have you ever needed to modify? And for those modifications, how long would it have been to refactor your code base to go from public field access to publi method access ?


> paying for it with increased compile time, code bloat, slower code.

Auto-properties in C# are one line of code, same as fields, and the getter/setter methods should always be inlined which turn them into field accesses anyway. So I'm not sure how that would affect compile times or lead to slower code in anything but some pathological use case. Or DEBUG, but at that point all performance bets are off.

(Technically Microsoft can get worse performance on properties vs. fields when using NGen - that's why they have TargetedPatchingOptOutAttribute - but that doesn't apply to regular, non-Framework assemblies.)

As for code bloat `public int SomeValue { get; private set; } = 6;` encapsulates a publically read-only/privately mutable property with a default value quite concisely...


(smacks forehead)

Wait, wait, since when can you put the default assignment right there on the same line? Go ahead, say since always... I dare you.

Edit: Apparently this came in C# 6.0 (July, 2015). Ok, now I don't feel so bad. :-)

On a separate note, C# might not sexy or even that popular, but I really enjoy coding in it, and the continuing evolution of the syntax is a joy.


It really is a joy to program in. I fought it off for the longest time (I like regular ole C, python, dabbled in Java long ago), but finally dipped my toes in and tried C# a month ago... While the language as a whole is a bit verbose, LINQ is incredible, and the breadth of the standard library is awesome.


Instead of a field, you get a field and two methods. It's more IL, more metadata, etc. Maybe when the JIT is all done it's eliminated, but it'll cost more to JIT.

Though I doubt this makes a ton of real world impact, performance wise. Stripping names would do more to shrink the .Net code.


> Instead of a field, you get a field and two methods

And if you wanted a publically read-only/privately mutable value without properties you'd have to write ... a field and one method. Possibly two methods if you wanted some shared validation code everywhere you assign the field.

Like I said, in practice, it's the same. If you have some case where three vs. two members makes a difference you're in pathological case territory.


Souns like get/set in swift for me. Does it have computed properties too?


    public double Area
    {
        get { return Width * Height; }

        set
        {
            Width = Math.Sqrt(value);
            Height = Width;
        }
    }
You can also use expression bodied members (that's the => syntax) with get/set. There's a shorthand if you only need to define the getter (i.e. read-only):

    public double Area => Width * Height;


Swift has the same syntax too except the last line.

Have they introducted willSet and didSet too?

Neat stuff, i may try the language out in a future project on windows.


There's no direct equivalent to willSet or didSet. The closest equivalent (aside from adding code before/after your own get/set code) might be INotifyPropertyChanged [1]/INotifyPropertyChanging [2]? But they are more for external observers and add a lot of boilerplate to your code. It's possible to manage the boilerplate, however [3].

[1] https://msdn.microsoft.com/en-us/library/system.componentmod...

[2] https://msdn.microsoft.com/en-us/library/system.componentmod...

[3] https://github.com/Fody/PropertyChanged


Yes. If you have a struct, getters/setters are somewhat pointless, since member visibility control is only package level (protected/public depending on variable name capitalization).

For interfaces, you can only invoke methods, so getters/setters are required.


Yup, getters/setters in POJOs drive me up the wall. So much simpler in languages that have proper const/mutability qualifiers.


> proper const/mutability qualifiers

I really like Go, but on the const/mutability front, it does no better than C, unfortunately.


so..not Go :P


Oh yeah, definitely not :)


Encapsulation isn't _that_ useful.

In Go it has real runtime cost as unlike C/C++ (you have an optimizing compiler) [1] , and in Java/C# you have the JIT which will optimize out your method calls over enough time.

A lot of FP languages are perfectly fine building Abstractions without getters/setters. But in OOP land people believe this is a requirement. 2/4 OOP _principles_ encapsulation and inheritance have aged like dirty sweat socks in a hot gym locker.

[1] https://klaig.blogspot.be/2012/09/the-go-language-is-faster-...


Take a look at the `time.Time` struct. It has a couple fields. What are the trade offs of making those fields public? (I contend that encapsulation is supremely useful, and the time package is a good exemplar.)

Note that Go's compiler does do function inlining, so it might be possible to encapsulate your data without runtime costs. With that said, you certainly don't have the flexibility that you have in C/C++/Rust.

Of course I agree that blindly adding getters/setters everywhere is bad juju. But to say encapsulation isn't useful is a step too far. Instead of thinking in terms of getters/setters, I'd rather think in terms of invariants on your data. If you have invariants you want to protect, then encapsulation is one possible tool you can use to protect them.


He didn't say encapsulation is not useful. He said it's not that useful. In other words, blindly encapsulating everything because that's supposed to be the right way is overkill.

Unless you're building an API.


> He didn't say encapsulation is not useful. He said it's not that useful.

So look at my comment as a refinement on that idea?


This is because most FP languages embrace immutability, so setters are absent by design, and getters are not needed. Any computation goes to proper functions, not "getters". Absence of virtual methods also helps: there is no point in having a getter (or a setter) if nobody would override it.


>Does he really mean that proper go code has direct access to fields rather than encapsulating them???

Yeah, so?


    Go is so close to C. Wouldn’t it be great if there was a program that could take C code and turn it into Go code?
    
    There are few attempts to do that:
    
    https://github.com/rsc/c2go
    https://github.com/elliotchance/c2go
    https://github.com/cznic/ccgo
    elliotchance/c2go seems to be the most promising.
    
    I didn’t try any of them as neither seems to be usable yet.
Actually, we used github.com/rsc/c2go to port our Go compilers and linkers from C to Go. It was 100% mechanical translation. No manual steps needed to achieve correctness.

github.com/cznic/ccgo was used to port sqlite to Go. It passes all the (very comprehensive) sqlite test suite.

They work very well. The code produced is not very idiomatic Go, but it's not some monstrous impenetrable computer generated code either. It's quite similar to the original C code and it's readable and refactorable enough. After we converted the compiler from C to Go, we spent a lot of time to make the code more idiomatic.


>github.com/cznic/ccgo was used to port sqlite to Go.

Where is the result of this? Is it publicly available?



>`a | b` can be written as: `if !a && b { a = true }`

`a || b` ...

The repository does have multiple cases of `a || b` and none of the `a = true` kind, so perhaps the author realized it after writing this blog post.


This one tripped me up too. I'm guessing he meant `a |= b`.


Of course he meant that. My point was that `if !a && b { a = true; }` is a silly way to write `a = a || b;`


It's right on the line wrap, but it actually says `a = a | b`. At least right now, not sure if they edited it.


> As far as I can tell you can’t step through tests in the debugger

Maybe I am missing something, but that is not correct. You can debug tests the same way as you debug any other code. Even works with the mentioned/used Go extension for VS Code.


It's quite possible that it's just my laziness in figuring out how to do it.

All I know is that hitting 'F5' in VSCode builds the binary and runs it under the debugger.

How would I debug the equivalent of `go test` in VSCode?


I have no idea in VSCode, but with JetBrains Gogland, debugging tests is stupidly easy. Simply add the breakpoint where you want it, left click on the play button in the line of the test declaration, click debug in the contextual menu, and off you go.


too bad sometimes it doesn't resolve pointers correctly


In Go test files, there's now a codelens annotation above every Test function with "run test" and "debug test" commands. Just click "debug test" to launch a debugger against that test.

See https://twitter.com/ramyanexus/status/860190148239081472

If you want to configure F5 to launch tests instead of your main entry point, you can do that too in your launch.json file.


There's a C++ library for Win32 (and other platforms) GUI programming with built-in layout engine, wxWidgets. It's a bit old, but works quite good, large DPI aware etc. Unlike Qt, it aims to be slim, provides native look and feel for all controls on all platforms, and avoids creating its own leaky abstractions. Necessary workarounds for old versions of Windows are included too.

Probably it would be easier to create Go language binding for wxWidgets rather than reimplementing all that stuff from scratch. wxWidgets already has Python, Perl and Ruby bindings, and besides Windows supports Mac (Carbon and Cocoa) and Linux (GTK, X11 and OpenMotif).


There are already wrapper libraries in Go for it. I'd say more relevant to the article is the fact that wxBoxSizer wxFlexGridSizer are reportedly similar in capability to flexbox.


My only problem with porting from A to B is how to keep B updated when A changes. Manual step destroys it all.


Sweet, Windows GUI in Go! Glad I put this off this long...

PS. That evergreen/un-dated content tho!


Looks like they're using walk and win for the gui.

https://github.com/kjk/walk https://github.com/kjk/win


Not naming it Yogo was a missed opportunity.


Today I learned that Go does not support the ternary operator. that's sad :(


Gah.. I hate the ternary operator.

Not that it's necessarily a bad thing in and of itself, but every time I've come across it in code, I've ultimately had to yank it out because I've needed to support an additional possibility. I don't get what value it really supplies, and it seems like people are really fond of using it in the wrong places.


I don't get what value it really supplies, and it seems like people are really fond of using it in the wrong places.

It's a conditional expression , not a statement, and particularly helps simplify things like

    if(foo)
        someVeryVeryLongExpressionEvaluatingToAnLvalue = someShortRvalue;
    else
        someVeryVeryLongExpressionEvaluatingToAnLvalue = otherShortRvalue
into

    someVeryVeryLongExpressionEvaluatingToAnLvalue =
        foo ? someShortRvalue : otherShortRvalue;
I suppose to really "get" what the ternary is useful for, you have to have some experience with a more functional programming language, focusing on the flow of the data itself. There, multiply-nested ternaries are really the norm for what you'd otherwise call "control flow" in non-functional languages, and structures like this are common:

   value = isAtrue ? A_value :
           isBtrue ? B_value :
           isCtrue ? C_value :
                     D_value ;
(Does not work in PHP, but every other language with a ternary will interpret that as a compact alternative to the far more cumbersome if/else --- especially if coding standards dictate braces on their own line.)


I don't like the ternary operator ?, because it's not obvious:

xyz = foo ? bar : baz;

What does it mean? Which is the condition? which is the true, which is the false value? It's not obvious.

In Python it is much more clearer:

xyz = bar if foo else baz


I honestly struggle to understand how a traditional ternary like this isn't obvious. The bit before the question mark is the condition (er, like a question, which ends with a question mark), then the other two bits are for true and false in the order you would expect.

I will freely admit I try and avoid nested ternaries, which I personally find hard to read.


I never really understood the distaste for it, or how it’s non-obvious. It seems like the people I’ve talked to object to the punctuation-based syntax more than the semantics, because it exactly mirrors conditional statements, even when nested. (Well, except in PHP, but that’s a bug in the grammar.)

    if (a) { b } else   if (c) { d } else { e }
        a  ? b   :          c  ? d   :      e

    if (a) { b } else { if (c) { d } else { e } }
        a  ? b   :    (     c  ? d   :      e   )
The question mark goes after the thing you’re asking about, like in English. The colon to separate the true and false branches is totally arbitrary, I grant you. I’d prefer to have the if…else notation available as an expression, but the statement-expression distinction hasn’t quite died yet.

Personally, I don’t care for Python’s version with the condition and true branch swapped, because I read it like invalid Forth, expecting “condition IF true-branch ELSE false-branch THEN”. :P


The ternary operator can be nice for conciseness and to ensure a variable has been assigned to when the language doesn't check that for you e.g.

    var x = condition ? 1 : 2;
vs

    var x;
    if (condition)
      x = 1 
    else 
      x = 2
I miss being able to write code like this in languages like OCaml:

    let x =
      if (condition) then
        // more lines of code
        1
      else 
        2


Heh your OCaml example is why I adore Nim, and any other languages that "expression all the things!" -- even modern JavaScript is decent on this front with 'ramda' and my favourite helper library @unction: https://github.com/krainboltgreene/unction.js/blob/core/READ...

Between those two, consise readable functional JS code based entirely around expressions is simple to achieve. It's genuinely made me happy to be back in JS land


In C it's useful as it allows you to still const a variable when assigning a value conditionally.

  const int x = a ? 1 : 2;


I managed to go about 15 years without using the ternary operator. Pretty much the only time it's really good at what it does is for assignments, and I've started using it a bit myself.

However, the old discomforts come up pretty frequently. I find people slipping functions into both the right hand and left hand side. And once you do that, you really should push it into a new function. Especially if you have any plans for testing.

Most compilers are pretty good at inlining things like that, and at any rate there are almost always bigger performance problems than function call overhead. In fact, one of my big tricks is that a function that consists of a weird but fast algorithm is generally tolerated by your coworkers better than just dropping it into the middle of a block of code. And knowing they could easily revert the change (because it's only one commit and one function body!) if it starts to annoy them also helps.

tl;dr: Just like comments in the middle of a function reveal a missing function, a ternary operator probably means the same.


Or bitwise OR and XOR operators, according to the article? That's a bit hard to believe, no pun intended.


Bitwise OR and XOR are supported on ints. It doesn't work on bools, but there are equivalents: https://play.golang.org/p/GCAFwx03pi


Bitwise OR and XOR on bools. Bitwise XOR is just !=, so it's useless. Bitwise OR isn't identical to any other operators, but writing code which specifically relies on side-effects of a function on the RHS of an OR does not strike me as the sort of thing that the Go designers would approve of.


Bitwise OR is equivalent to addition, but IMHO it makes more sense to omit support for addition on bools than to omit support for the classical boolean operators supported by C++. If for no other reason than to make existing C++ code easier to port.


Addition on bools is XOR.


Not by C++'s convention. It may be compiler-dependent, but all of the compilers I have within easy reach treat addition of bools as an 'or' operation.

    bool a = true;
    bool b = true;
    bool c = a + b;
This leaves c set to true. Basically it gets promoted to int, turned into a value of 2, and demoted back to bool.

Either way it's highly nonstandard not to implement a bitwise-or operation on a boolean type. It's a violation of the "principle of least surprise" as well as a portability hazard. No upside that I can imagine.


I have been writing C for a while and never saw anyone add bools with +. Why do this when there is completely unambiguous || ?

I get suspicious of being fast and loose with conversions between int and bool. This is implicitly required when working with operators like &&, ||, ==, etc., and working with pre-C99 bools and library typedefs. But assigning anything other than 1 or 0 is asking for problems. There is a whole class of bugs that shows up in security bugs involving assigning someone's machine-word-sized Boolean expression into someone else's byte sized bool, losing bits, and flipping from true to false.

I sometimes feel there is a ^^ operator that should be there and is missing, in the same way that we have && and || corresponding to & and |. I have occasionally faked it with a macro:

    #define XOR(A,B) (((A) ? 1 : 0) ^ ((B) ? 1 : 0))
The ? here eliminates the problems from using larger != 1 quantities as true, as above.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: