/* 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? */
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.
The primary benefit that structural interfaces give you, IMO, is less typing. Which has plenty of value, don't get me wrong; it's awesome that Go has successfully reduced the amount of syntactic overhead needed to implement interfaces. But I think the benefits of it shouldn't be overstated.
Go's approach is theoretically more error prone. It relies on names matching in a way that can happen by coincidence. The important part of explicit interfaces here is referencing a common declaration site, that could e.g. give informal requirements in the form of comments. I doubt Go's approach is particularly error prone in practice, though.
type foo = ...
and impl hashable(foo) = ...
impl hashable(int) = ... // yep, adding to a builtin type
This is the way that Myrddin currently handles it.First of all, I agree that the accidental argument is not very convincing (two developers writing a method Launch(), one that launches a football and another that launches nuclear missiles).
However, we lose two important things with implicit interfaces:
- Code readability. It becomes much harder for a human to interpret what types are actually implemented.
- More importantly, it severely limits the tooling available since the compiler has a lot less knowledge about the types you are dealing with. Automatic refactorings are all but impossible with structural typing.
Overall, I really don't see the harm in saying explicitly "My type is called Account and it implements Serializable and Entity" as opposed to me having to guess by reading all its methods and also having to remember which methods are necessary to be an Entity or a Serializable.
public interface FooBar
{
int foo();
int bar();
}
public Afob implements FooBar {...}
refactor FooBar to simply: public interface Foo { int foo() }
public interface Bar { int bar() }
public interface FooBar extends Foo, Bar {}
And keep FooBar the way it was, but now cast it to a Foo (or Bar) when you don't want a FooBar?In Go, they can both declare the same interface or even skip formally declaring it and just agree to implement the same methods. Furthermore, if one of them decides to implement a new method, the other can just copy the method signature and they stay compatible. This makes it a lot easier to coordinate API changes.
That's a feature, not a bug. Say we're using implicit interfaces and have library A that provides an interface Foo and an application B that uses A and passes a Foo to some callback system. Now A gets updated and adds a method to Foo, but existing B binaries still call A with an implementation of the old version of Foo, and things fall apart.
Java's model (and COM's and MS-RPC's) model here is better because it enforces good interface hygiene: incompatible types get incompatible names. Then you can choose whether to support the old name as well as the new name, but you're at least consciously making that choice. D's model worries me because it feels like it's easier to accidentally break things.
This is what defender methods in Java 8 are for.
Let's say I have a function A that depends on concrete type B, and that I only use a subset of B's functionality. Now say I decide, hey, I'd actually really like to be able to swap a different implementation of B at runtime.
With traditional interfaces, I would have to either:
a) define a subset of B's functionality as interface I and change B to implement I, or
b) define a subset of B's functionality as interface I and define a wrapper that implements I in terms of B.
If I choose (a) and many other callers end up doing the same thing to B, then B implements a mess of little micro-interfaces that it really should have no reason to care about. If I choose (b) then the implementation of A is way more complex than it seems like it ought to be.
===
One place this comes up all the time is testing: Object A relies on a subset of the interface exposed by service S, and service S is hard to create in test environment. With Go interfaces, A just defines an interface for the subset of S that it needs, and the test code for A implements that interface. S doesn't need to know anything about it.
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?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
Edit: To clarify I mean if you had a Java X app deployed how hard is it to upgrade to Java 8? One thing I like, maybe naively, about Go is static binaries.
I mean sure, you have to install the new JVM, but if you don't have a system in place for making changes to your servers then you've got bigger problems than which language you're using. And honestly I think the JVM with its classpath approach solves the library problem better than most platforms; upgrading the JVM binary occasionally is no great hardship, and aside from that everything is just jars, dynamic but not getting in the way of each other unless you want them to. What happens when security holes are found in Go libraries, do you have to recompile anything that depends on them?
However, most of the work involved in doing that is paving the road so that version upgrades are faster. I would anticipate that moving from 7 to 8 will be much faster than moving from 6 to 7.
It continually amazes me that people are willing to put up with Java.
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.
It's also a bit like working with first-order propositional logic - well-founded and with its own unique simplicity, but you can't say everything with it and have alternatives.
So yes, I'd say that the type system of Go doesn't suck. It's not very good, and there are so many better things out there (rust, I'm looking at you), but it's a long way from being a disaster.
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.
Go is not in that world. It isn't even in the same star system.
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.
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.
Yes I know about Erlang, no I won't touch Erlang again.
Check it out :)
Curious, what makes you say that?
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. var f = function(){ console.log('called'); };
f.callMe = function(){ this(); };
I wonder if this is a useful pattern in JS. Can't recall ever seeing it in practice.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.
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)
It's an uncommon thing to need to do, but this example shows that you can do it.
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...
Can't do this without polymorphism and higher-kinded types.
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.
I wonder if make sense to merge the julia multi-methods + GO interfaces.