top | item 5125699

It is ridiculously easy to refactor Go

149 points| bslatkin | 13 years ago |onebigfluke.com | reply

88 comments

order
[+] dons|13 years ago|reply
Welcome to bounded polymorphism, circa 1989. A young Wadler would be pleased.

An aside, it is not "duck typing" if it involves a static type system.

[+] voidlogic|13 years ago|reply
Thanks right, technically Go supports a structural type system* which is a similar to, but different than duck typing. But the Go community often refers to Go as being duck-typed due as that term is more familiar to people coming from languages like Python.

* http://en.wikipedia.org/wiki/Structural_type_system

[+] cwzwarich|13 years ago|reply
Bounded polymorphism was old news even by 1989. Boring imperative languages like CLU and Ada had it.
[+] MichaelGG|13 years ago|reply
Can you clarify why it can't be duck typing if it's static? For instance, in F# you can declare a function that can take any type that provides a specific method name and signature. You can pass any object to such a function as long as it has that method - there's no other similarities required in the types you pass to it. How is that not "duck typing"?
[+] scott_s|13 years ago|reply
I think you could actually do this in C++, but not with runtime polymorphism. I think you should be able to define extractBody and extractTextBody as template functions. If the signatures (return type, name and parameters) of the functions of the interface are identical - and it looks like they are - then this would work as a C++ template function. Behind the scenes, the compiler would generate two different kinds of functions for you, one for each of the types you called it with.

(Please note I'm not saying this would be better, just pointing out that it's possible.)

[+] bslatkin|13 years ago|reply
Yes, it's possible to use C++ templates to accomplish this. But you'd still miss other features because the types of Header and MIMEHeader are not actually unified. Go would let you statically type-check at compile time, but also understand those types are the same (as "emailHeader") at runtime, so you could do this:

x := ... // Header, MIMEHeader, or whatever you want v, ok := x.(emailHeader) if !ok { fmt.Println("It's not compatible with Get(string) -> string") }

So, it's a nice mix of static and dynamic typing.

(ps: http://golang.org/ref/spec#Type_assertions)

[+] plorkyeran|13 years ago|reply
For runtime polymorphism you'd have to define a wrapper of some sort, which in this specific case would be completely trivial since it's only one method. In more complex cases it gets pretty awful compared to the Go version, but it's not impossible (although AFAIK if the methods you care about have different names in the different implementations you'll need a wrapper even in Go).
[+] pdhborges|13 years ago|reply
And he doesn't even have to define an interface.
[+] VeejayRampay|13 years ago|reply
Great article. As a side note, you might want to consider reformatting the blog post using syntax highlighting to make the code more readable and obvious. Thanks for posting though.
[+] zenocon|13 years ago|reply
This was a polite way of saying that the blog is pretty darn unreadable. It really does hurt my eyes. Content sounded interesting, though.
[+] shurcooL|13 years ago|reply
Go has really grown on me in the last few months, it's becoming my favourite language, coming from C++.
[+] Tekker|13 years ago|reply
I've been working in C/C++ for over 20 years now. I started playing with Go about two months ago, burnt through the tutorial in 2 afternoons, and was hooked. It's very easy coming from a C background, it has constructs I'm familiar with, but also picks out the best parts of Python-ish SmallTalk-ish languages, while still being compiled.

I'm looking forward to when I can write Android apps in it, instead of mucking around with Java.

[+] jbrackett|13 years ago|reply
I went into the article thinking it was going to be about some way of renaming functions or something. Probably just my limited internal definition of the word refactor. After reading I would say Go has a cool built in way of using the adapter pattern. More interesting than renaming functions.
[+] eknkc|13 years ago|reply
Actually, Go makes this somewhat easy too. Check this: http://blog.golang.org/2013/01/go-fmt-your-code.html It's possible to do context aware source code substitutions. Works great. (Not that it's something new or groundbreaking though, just relevant to your expectation)
[+] pkulak|13 years ago|reply
I thought it was going to be about go-fmt, which is really cool.
[+] mahmud|13 years ago|reply
Folks, recursive data-strcutures are CS101. Processing them is also a trivial, template-filling exercise. First you specify the (recursive) datatype, consisting of the simple "leaf" elements, along with the complex forms. Then you write your program consisting of a series of mutually recursive functions.

What matters is that once you specify the data type, the code that processes it follows immediately from the grammar.

[+] adam-a|13 years ago|reply
I think you're slightly missing the point. It's not the use of recursive functions or data structures that is interesting. It's that, in Go, you can add a new type which covers externally defined types, and those types are now instances of the new type automatically. In other languages, to unify two types like this with a common interface, you would probably have to define a wrapper class which proxies the calls to the real objects. Even though the methods for the two classes have matching type signatures and do the same thing already.
[+] chipsy|13 years ago|reply
Haxe has a similar (but not quite identical) bag of tricks which act as a "gradient" of stronger/weaker couplings.

You have to specify that an implementation can use a specific interface, but you may overload an implementation with multiple interfaces. http://haxe.org/manual/2_types#interfaces

You can use structure types instead of classes and rely on structural subtyping to match them. http://haxe.org/manual/2_types#structural-subtyping

And you can ask for dynamic type behavior wherever the code needs it. http://haxe.org/ref/dynamic

[+] zcrar70|13 years ago|reply
Typescript allows you to do this also. It's extremely convenient.
[+] skrebbel|13 years ago|reply
I don't completely seee the point - in more established OO languages you'd make two one-line wrapper classes that both implement Get() and be done with it. Not stuck at all.. Extra indirection, yes, but is your performance going to block on parsing mime mails?
[+] Djehngo|13 years ago|reply
I assumed you would do the same thing as was done in the article.

Create an interface and have a function that takes implementations of that interface as a parameter. The difference would be that in Java or C# you would need to explicitly mark Header and MIMEHeader as implementing emailHeader

I don't think much will come of discussing what is/isn't possible in a language given that they are all Turing complete. Usually posts like this are used for discussing what's easier or cleaner in a language.

For what it's worth I think having dynamic interfaces is cool, but I can only really think of one place I have actually relied on that capability.

Mostly the "dynamic"ness is nice for decoupling code. You can have a package(A) containing a function that takes an interface a parameter, a package(B) containing an implementation of the interface in A and a package (C) which passes the implementation in B to the function in A.

Given this situation C needs to import A and B but neither A nor B need to know about each-other.

[+] 6ren|13 years ago|reply
To be fair, you can do this in java by adding to Header and MIMEHeader "implements emailHeader".

Structuring typing seems simpler. Are there any downsides? (apart from two types having an identical method signature by coincidence)

[+] LeafStorm|13 years ago|reply
> To be fair, you can do this in java by adding to Header and MIMEHeader "implements emailHeader".

Not if you aren't the author of the package. (You could modify your local copy, but requiring a fork is always a pain.) Without the ability to modify the sources, you would have to create custom subclasses of Header and MimeHeader which implement that interface, and find some way to make the library generate your custom subclasses instead of the defaults.

[+] alangpierce|13 years ago|reply
One potential downside is that it's a lot harder or more computationally expensive to figure out class/interface relationships in IDEs or other tools. (I haven't used Go, though, so I'm not sure what kinds of tools are available for it.)

Generally, "what classes implement this interface?" is a question I need to ask quite a bit when working with Java in Eclipse. The simple way in Go (looking through all classes) would generate a bunch of false positives in a large enough codebase, and the more precise way (looking through all implicit casts everywhere to find all classes that are implicitly casted to the interface) is probably pretty slow or memory intensive and could potentially have false negatives (e.g. implementing classes that haven't been hooked up yet, and uses that aren't contained in your source tree).

There are other Eclipse operations that rely on this that would also be a lot harder to implement (in the same way they're implemented in Eclipse):

-"Find callers" on a class method includes calls to that method on superclasses and superinterfaces.

-"Rename method" automatically renames all methods with that name that are reachable through a path of implementer/interface relationships.

[+] Uchikoma|13 years ago|reply
I wished for years for Java to determine "implements interface" by method checking not the way it does it currently.

And while Scala has "structural typing" I'd also wish it would implement it the way Go does not with explicit methods.

[+] knodi|13 years ago|reply
I hope go 1.1 will be ready soon. A lot of goog stuff in it.
[+] signa11|13 years ago|reply
care to elaborate ?
[+] Ramonaxvh|13 years ago|reply
Go has been out for a while, it's really cool to see it finally starting to gain traction.
[+] martinced|13 years ago|reply
Refactoring is probably the one area where good IDEs shine the most.

I was expecting to see some advanced Emacs / vim or whatever texteditors or IDE support for refactoring Go code.

Not just manual extraction of common behavior into a tinier interface.

But I'm more and more impressed by Go and may give it a try one of these days.

[+] est|13 years ago|reply
I doubt it.

How could anyone refactor something like this in Python

getattr(myobj, 'myprop', None)

Where mypropis dynamically generated?