22 ms·
Go's power is in emergent behavior
- pedrocr 12y agoThe second one just looks like a hack. You've defined a useless function to make an interface not implementable. It would look cleaner with a keyword in the interface definition.
- jdiez17 12y agoFirst of all: I agree. There are many things in Go that feel like a hack. For example, it is common (and idiomatic) to use a map with empty values when you want to use a set (unordered and unique values), because the map's keys are a set. I personally dislike this, and some aspects of Go are like this. However this blog post is about how good core language design leads to unplanned patterns that are useful, like HandlerFunc and the private interface hack.
- Iftheshoefits 12y agoOne person's "unplanned patterns that are useful" is another's "this is a potentially dangerous hack that could lead to all manner of headaches in any non-trivial program with even just a few hundred lines of code, let alone thousands or millions."
- politician 12y agoThe sweet spot for Go is in the hundreds to a couple of thousand lines of code. Unix philosophy and all that. Leave the monolithic MLOC monstrosities to Java.
- bsdetector 12y agoPeople write MLOC monstrosities in Java because they can. You get some boring financial topic and some sub-par programmers and they'll write as much garbage as the language can possibly sustain. These things are a testament to how safe and simple Java is as a language. Not having massive crappy code bases is a negative sign in terms of how reliable and easy to understand a language is.
- midas007 12y agoYes, it's best to use the smallest tool (orthogonal language and libraries) for the job because it's less to test and maintain, and more likely to be correct.
- colin_mccabe 12y agoNone of the things you mentioned are "potentially dangerous hacks." Using maps as sets is common in many languages and there's nothing wrong with it. Adding a guard method to an interface is pretty clear and it works well, as well.
- rectangletangle 12y agoThat's funny, that's how sets in early versions of CPython were implemented.
- zaphar 12y agoThe function doesn't have to be useless. He uses a small and simple example to illustrate it. However the more common example of this is an interface with public piece your consumers can interact with and a private piece that only you can interact with.
- seanmcdirmid 12y ago> However the more common example of this is an interface with public piece your consumers can interact with and a private piece that only you can interact with. Also known as...a class. Honestly, this is why classes exist: they are able to support both public and private interfaces, while interfaces and type classes are only able to support public interfaces. The closest we get to this essential aspect of OO programming in the H & M world is via existential types.
- austinz 12y agoI'm not sure I understand why implicit interfaces are better than explicitly declared interfaces. (This is an honest question.) I do iOS stuff occasionally, and in that world one of the main purposes of interfaces (in Objective-C, protocols) is to explicitly declare that some entity adheres to a contract - it promises to implement some pre-agreed-upon functionality that other entities can use. What do implicit interfaces buy you? Better static type checking? Less typing? /* Also, from the blog: > Nowhere in this code did we ever declare a relationship between MyType and Answer. "Answer" isn't in the example. Was this something from an earlier draft? */
- spydum 12y agoIt seemed to me clearly a autocorrect from Awesome to Answer.
- latch 12y agoWith implicit interfaces you're not tied to a specific interface, but rather the method(s). If you have a type that implements Close() error, your type can now be used anywhere (include other people's code) which accepts an io.Closer. Add a MarshalText() (text []byte, err error) function and it can now be used anywhere that expects a encoding.TextMarshaler. Furthermore, your type can be used in someone's else code which has defined its own MarshalTextCloser interface. I believe this is what the OP means by emergent. You wrote a type that has two methods, within your own package called "goku.Sayan", and it accidentally satisfies the interface "picard.MarshalTextCloser" which you'll never know about. "Sayan" was never built to satisfy _any_ interface. You just needed a Close and MarshalText function, but none the less, "Sayan" satisfies 3 distinct interface (which could be represented by an unlimited number of names) I realise accidental sounds dangerous, but the only real risk is that the methods don't do what you think they should. But I don't see how that's different from explicit interfaces; in both cases the intent is only implied by the name. Overall, as the OP says, it's quite powerful when dealing with simple 1 or 2 function interfaces. I think most Go developers see this naturally emerge in their own code: they favor small interfaces specifically for this type of re-use.
- skywhopper 12y agoSo Go has duck-typing, and semantic capitalization, just like many other languages.
- ryanobjc 12y agofor me, the built in goroutines is what moves the needle. The rest of the stuff is very nice, but built-in concurrency is pretty nice. Yes I know about Erlang, no I won't touch Erlang again.
- findjashua 12y agoElixir?
- midas007 12y agoElixir is neat, but it takes dynamic languages much farther away from static type checking. Static compilation and compilation speed make Go very attractive for large projects.
- rdtsc 12y agoErlang has dialyzer type checker. Depending on how well users refine their types that can produce very good results. Combine that with property checker (PropEr or QuickCheck) + Erlang's default process isolation and fault tolerance, and I think it can make for a more reliable system. I think I'll take that into production over just having a statically typed program. (Just going off my gut feeling and experience). http://learnyousomeerlang.com/dialyzer http://learnyousomeerlang.com/dialyzer ( not sure if Elixir supports it though, but it seems so: http://elixir-lang.org/docs/stable/Kernel.Typespec.html http://elixir-lang.org/docs/stable/Kernel.Typespec.html )
- midas007 12y agoYeap thanks, have used both. It's just optional, and therefore doesn't get used much on open source projects. So the average quality of code suffers. Whereas something like Go where extra import are hard errors, making best-practices mandatory keeps code to certain standard and it's zero work to setup.
- coolsunglasses 12y agoThere are nicer ways to do structural typing. Here's just one example: I have a post here of how you can partially define structure to be filled in by the consumer of the API: http://bitemyapp.com/posts/2014-04-11-aeson-and-user-created-types.html http://bitemyapp.com/posts/2014-04-11-aeson-and-user-created... Can't do this without polymorphism and higher-kinded types.
- biscarch 12y agoI'm using this approach for a library that is currently in development. It works very well.
- myg204 12y agoInteresting to see surprise usages like that for such a small/simple language like Go; makes you wonder about the amounts of trickeries available in more complex languages.
- philsnow 12y agothe purpose is to not be able to use the interface outside the module, right? why wouldn't you just use an interface type with a lowercase name, which wouldn't be visible outside the module?
- bslatkin 12y agoYou want to be able to use the interface outside the module, but not allow anyone else to implement that interface.
- Laremere 12y agoThe purpose was to not allow defining types which satisfy the interface outside the package. In this case they still want others to be able to use the values of the interface. It's an uncommon thing to need to do, but this example shows that you can do it.
- asuffield 12y agoWelcome to the world of languages with type systems that don't suck. It's going to be a fun ride, and if you think this is cool, you're going to be really excited by the other languages we've got in here. It continually amazes me that people are willing to put up with Java.
- midas007 12y agoThe Oracle JVM is pretty versatile and has been beaten on in production on an extremely large scale, as much as M$ CLR. Java as a language though was a primary response to over-correct and over-optimize for secure, correct, safe code based on the history of C's shortcomings. There's are many other lessons that have been learned since. Java is really hard to beat apart from specialized formal methods verifiers (coq, CVC4), strongly-typed functional languages Haskell and similar derivatives for embedded industrial systems. If you're involved in safety critical systems, you should be using the simplest and easiest to understand formal methods tools as possible. If something's too esoteric, fewer people will be able to double-check the work. For wider participation, it's a tradeoff to use one of the more popular languages that lack correctness aspects because of the absence of a learning curve.
- yohanatan 12y agoJVM != Java so not sure why that point belongs in here.
- zak_mc_kracken 12y agoIf you are putting Go in the category of languages with a type system that doesn't suck, you probably need to study more languages. Go has a few nice things going for it but its type system was clearly designed by people who stopped paying attention to type theory and compiler design in the late 90's.
- yepguy 12y agoGo's type system stays out of the way and adds some useful amount of safety. That's more than I can say of any mainstream language. No reason to tear it down just because it doesn't go further.
- shoo 12y agoFor the second example, the non-implementable public interface, is there a way to allow implementations when testing? naively, if i define a non-implementable public interface as described in the article inside `foo.go` and put some test code next to it in `foo_test.go` that attempts to implement the interface with a special test version, then as we might expect, that doesn't work. if i put test code inside `foo.go` itself, `go test` doesn't appear to collect the test. (i have no experience with go, apologies if i am missing something very obvious)
- helper 12y agoYour example of `foo.go` and `foo_test.go` would actually work the way you want. In go, tests are in the same namespace as the code they are testing so they have access to private members.
- shoo 12y agoAh, thank you. Where i was going wrong was declaring `foo_test.go` to be a separate package from `foo.go`. When i write `package foo` at the head of both `foo.go` and `foo_test.go` this works fine.
- mamcx 12y agoAs asked in http://programmers.stackexchange.com/questions/234900/go-like-interfaces-multi-methods-make-sense http://programmers.stackexchange.com/questions/234900/go-lik... I wonder if make sense to merge the julia multi-methods + GO interfaces.
- MaysonL 12y agoThe trouble with implicit interfaces is maintainability: suppose Awesome gets a func PerformAwesomeness() added to it: what then happens to the code that thinks myType is Awesome? Not too bad when it's all in one source file, but get a few megabytes of code spread through a few thousand source files, and you've got a formula for chaos and heartburn.
- thrownaway2424 12y agoNot sure what you are getting at. If you add something to the interface of Awesome, then any call site which passes a thing not having a PerformAwesomeness() method will fail to compile.
- danieldk 12y agoUnless you rely on introspection, which happens a lot when your language does not support generics.
- NateDad 12y agoNo, it really doesn't happen a lot. If it does, you're doing it wrong.
- danieldk 12y agoIf you don't have parametric polymorphism, you only have two options to implement new containers: a new implementation for each type or using interface{}. The first leads to a lot of duplicate code, the latter requires you to use introspection to use safely. So, it does happen a lot, or you are making duplicate code.
- NateDad 12y agoOr you just don't need polymorphic containers that often (other than the ones that go already gives you).
- 12y ago
- mjburgess 12y agoI dont know, these feel to me like very simple features ("functions are objects", "functions take a final-type") .. nothing really "wow". The wow-factor is, I guess, that Go can actually do it.. which seems to me a little masochistic. ("Wow my underpowered lang. isnt so bad!").
- eridius 12y agoThe first example is not emergent behavior. And it has nothing at all to do with the implicit nature of interfaces. It is in fact a pretty straightforward consequence of the fact that functions are first-class types. Because they're types, you can newtype them, and you can implement interfaces on the newtype. This should work in any language that has interfaces and first-class functions. For reference, here's the exact same thing in Rust: // Define trait Foo, this is the equivalent of an interface pub trait Foo { fn foo(&self, x: int) -> int; } // Define a newtype called FooFunc pub struct FooFunc(fn(x:int) -> int); // Explicitly implement the trait on FooFunc impl Foo for FooFunc { fn foo(&self, x: int) -> int { let &FooFunc(f) = self; // unwrap the newtype f(x) } } // This is a method that uses a Foo, analogous to http.Handle() pub fn doFoo<T: Foo>(f: T) { println!("foo(42) = {}", f.foo(42)); } // Here's our function that we want to wrap fn MyFoo(x: int) -> int { x+1 } fn main() { // Call the function, just like http.Handle using http.HandlerFunc doFoo(FooFunc(MyFoo)); } And just like Go, this ends up being free at runtime.
- bslatkin 12y agoBut you had to explicitly define the relationship: "impl Foo for FooFunc"
- dignati 12y agoWhich many would prefer because it's implicit documentation. Never knowing what interfaces a go type implements sometimes bugged me. Functions, defined in interfaces often look different in style and paradigm so sometimes they stand out and made me wonder why they would implement such functionallity in this way, not knowing that the definition comes from a hidden interface. It also takes away the ability to quickly look up all die functionality because I would have to know the functions names and what interface they are defined in. Imagine a interface changes and you would have to find all the types you have to change.
- seabee 12y ago
- twic 12y ago> That's it: a tiny adaptor that makes a bare function satisfy an interface. How is that possible? No, why is it necessary? In Java 8: public interface Handler { void serveHTTP(ResponseWriter w, Request r); } public class MyApplication { public static void myHandler(ResponseWriter w, Request r) { w.write("An even easier webserver?"); } public static void main(String... args) { Http.handle("/", MyApplication::myHandler); } } No adaptor needed! Or even: public class MyApplication { public static void main(String... args) { Http.handle("/", (w, r) -> w.write("An even easier webserver?")); } } And please bear in mind that this is Java, the slowest-moving and least sophisticated of contemporary languages! Why does Go require so much more boilerplate than Java?
- dvirsky 12y agoIt's just a design decision of the http library, that its router by default accepts an interface and not a function - so internally you need this little adapter to make the function comply with that interface. I also loved this little trick when I noticed it. But if the HTTP library would directly accept a function, you could then just pass a function without that trick. Go doesn't mandate you use interfaces for callbacks (like Java did until 1.8), you can also define an API that accepts functions directly, provided they have a certain signature. Also, the standard library hides this little trick from you, and just exposes the HandleFunc method, that lets you register plain functions as handlers. as in this example: http://golang.org/pkg/net/http/#ListenAndServe http://golang.org/pkg/net/http/#ListenAndServe
- tomp 12y agoIf I understand the Java 8 example correctly, the Http.handle accepts an interface as well (the Handler interface with a single method). Java compiler is smart enough to automatically convert functions into objects satisfying interfaces with exactly one method, while Go compiler isn't.
- dvirsky 12y agoYeah, well, knowing the Go philosophy, it makes sense not to have the compiler solve ambiguities like that, especially since a function can have its own methods.
- NateDad 12y agoOne of the nicest things about Go interfaces is that they encourage SMALL interfaces. In explicit interface implementations, you end up throwing everything including the kitchen sink in the interface, because you might need it somewhere the type is used. In Go, interfaces are small, and focused more on the function rather than the type. The function defines the methods it needs. So you can have a hundred small interfaces with one or two methods each, and a small handful of types that implement some percentage of those interfaces. Small interfaces makes your functions a lot more flexible. If all you need is a Read() method and a Close() method, instead of a huge number of potentially type-specific methods, then your method that takes a ReadCloser can be used by a lot more types. You can do that in languages with explicitly implemented interfaces, but it means you need to write down that your type implements these 40 interfaces... and that's a hassle that isn't really needed or useful.
- lnanek2 12y agoHonestly, I feel the version where he defines the struct as usual is clearer, simpler, and more maintainable. Other programmers will understand it immediately without thought. Allowing weird fancy ways like he raves about to save a couple lines of code is really a drawback in enterprise software development.