gogrep: Exploring go

I’ve been learning Google’s go language and I put together a fun, trivial implementation of grep (gogrep!) to explore some of the language packages. It’s not complicated; the command is invoked with a regular expression pattern and a file path, and iterates over the file, line by line, attempting to match the pattern. Lines which match are then sent to STDOUT.

Go is an interesting language, and I like a lot of its features.

Having a unified workspace for both writing code and building binaries is quite nice, though I am puzzled at how specific versions of dependencies are specified for projects, as the organization of the workspace doesn’t seem to allow for this.

Method implementation for go objects behaves in a similar manner to extension methods in C#, in that methods are not defined on a specific object, but rather as standalone functions that receive, as their target of invocation, an instance of a particular type of object. These “methods” may then be invoked on any object of that type, much as extension methods in C# may be invoked on objects to which they are not really attached, by a trick of the compiler.

Go is type-safe and uses compile time, static type checking. I rarely find myself declaring types of variables, however, because go also uses type inference to make assumptions about the types in my code. The language also supports a special kind of type-safe duck typing in which objects that conform to declared interfaces implement them automatically. Because of this you can define interfaces for external libraries and the compiler will enforce your interfaces.

Go types don’t use type system inheritance; they use embedding instead. Object embedding reminds me of prototypal inheritance in JavaScript, where an invoked method that does not exist on an instance, but does exist on the instance’s prototype, will be invoked. A key difference between the two, however, is that in JS the target of invocation (the value of “this”) will be the object at the top of the prototypal chain, whereas in go, the target of invocation is an instance of the embedded object not the object embedding it! This is not as unusual as it sounds; it is a way to extend behavior through composition rather than inheritance. (Embedding objects can “override” the methods of their embedded objects, however, providing their own implementations.) Not only does embedding grant objects the behavior of other objects, it also means that objects satisfy the interfaces of their embedded objects as well. Epic.

I’ve read a little about the go concurrency model (Goroutines and channels), and I’m eager to jump in and play around with it but haven’t yet.

Overall I’m very impressed with go as a language. Many articles I’ve read almost exclusively relegate it to system-level development, though I hope it creates a much wider footprint.

Leave a Reply

Your email address will not be published. Required fields are marked *