Monday, January 13, 2014
There are people who like to bad-mouth Java as being verbose and tout dynamically typed languages. I disagree with much of what such people say, as I find type declarations a valuable form of compiler validated documentation, especially in an unfamiliar codebase. At a previous job, I had to work on some Python code that I had to learn. The guy who wrote it had been laid off and didn't write it for understandability, having described the code as "dense" on at least one occasion. This guy is one of those who bad-mouths Java for being verbose. Anyhow, when working on the code, I found that the lack of type declarations made it much more difficult to find the code that was being called. I also had to work on some unfamiliar Ruby code, and the same applies. My next job was initially working on an unfamiliar codebase of Java and C++ (for Android), and finding the code was much easier, which was good, since it was tracking down numerous bugs that turned out to be race conditions in using code primarily written for iOS.
Tuesday, April 24, 2012
I started playing with Google App Engine for Go, and immediately encountered failures due to how I like to structure code in packages. The go-app-builder parser rejects any imports containing double dots (..) outright, instead of checking if the import path actually attempts to escape the root of the app.
One structure I was trying to use was:
One structure I was trying to use was:
./app
./app/app.go
./module
./module/interface.go
./module/implementation1
./module/implementation1/implementation1.go
./module/implementation2
./module/implementation2/implementation2.go
./app/app.go
has something like:
package app import "../module" import "../module/implementation1" var instance module.Interface = implementation1.New()and
./module/interface.go
has something like:
package module type Interface interface { // omitted }and
./module/implementation1/implementation1.go
has something like:
package implementation1 import "../../module" func New() module.Interface { // omitted }I could change
./module
to ./app/module
, which make one part of it work. It would also make code structure ridiculous, since, if I had another package, ./module2
, that imports ../module1
, then I'd have to structure it as ./app/module2/module.
And if I had a third module that also imports ../module
, then I'd need ./app/module2/module3/module
or ./app/module3/module2/module
, which is the case of ./module/implementation1
and ./module/implementation2
. And it gets worse with more packages. Perhaps it's expected that AppEngine code should all be lumped into a single package, which is horrible.
Monday, April 2, 2012
After playing with Go (golang), I think the biggest flaw in its
syntax is using the period (.) for the package qualifier, which
is not distinct from field selectors and method calls. It's
not horrible and not worth changing, but I still consider it a
minor flaw. Here's an illustrative example:
Java has the same issue, but since packages qualifiers in Java
tend to be things like java.util.something or org.apache.something,
they aren't likely to collide with identifiers, and, in practice,
the result is there are compile errors about unknown packages when
using undeclared variables. In Go, package qualifiers are much
more likely to collide with useful names.
If one could go back in time to change it, some alternatives could
be ::, \, or #. Other possibilities include ~, !, @, $, or ?.
syntax is using the period (.) for the package qualifier, which
is not distinct from field selectors and method calls. It's
not horrible and not worth changing, but I still consider it a
minor flaw. Here's an illustrative example:
package main
import "os"
type a struct {
Args int
}
func main() {
println(os.Args) // os is a package
os := a{4}
println(os.Args) // os is a variable
}
Java has the same issue, but since packages qualifiers in Java
tend to be things like java.util.something or org.apache.something,
they aren't likely to collide with identifiers, and, in practice,
the result is there are compile errors about unknown packages when
using undeclared variables. In Go, package qualifiers are much
more likely to collide with useful names.
If one could go back in time to change it, some alternatives could
be ::, \, or #. Other possibilities include ~, !, @, $, or ?.
Monday, March 19, 2012
Some thoughts after using Go for a couple of days:
- I like
gofmt
. - Having used Java for so long, I got tripped up by the passing by value of structs and had to change the parameters to pointers.
- I also got tripped by the
:=
declaration shadowing variables:
func pop(n int, list []int) ([]int, error) {
if n > len(list) {
return list, errors.New("error")
}
return list[n:], nil
}
func multipop(counts []int, list []int) ([]int, error) {
for _, n := range counts {
list, err := pop(n, list) // Oops, a new variable named list
if err != nil {
return list, err
}
}
return list, nil
} - I like goroutines + a channel better than
java.util.Iterator
and Python gemerators, but not as much as lazy lists in Haskell for producer/consumer code. containers/list
looks very awkward compared tojava.util.List
, especially with Java generics. Generics are almost essential for useful containers, so Go inelegantly has the built-in functionappend
and the built-inmap
data structure.
Monday, March 12, 2012
I've been thinking about playing with Go for a while, as it seems worth knowing, so I decided to install the compiler. That was a bad experience. I didn't want to install Mercurial to get the sources, but there was tar file available for download of the most recent weekly snapshot. There didn't seem to be a more stable version available. So I downloaded it and built it. It failed on some ipv6 tests, so I commented those out. Then, it went into some infinite loop or something is another test, so I killed it after about 15 minutes in that one test, and downloaded and installed a pre-built binary package instead. That was a mistake. Somehow, after installing it, /etc was empty except for /etc/paths.d/go. Apparently, on a Macintosh, /etc is supposed to be a symlink to /private/etc, and the go install screwed that up. And I couldn't even run sudo or su to fix it, since /etc/pam.d was gone. I had to reboot into single user mode to fix it. After all of that, the binaries still didn't work due to some dynamic link error. Probably because I'm using an older version of the operating system. In the end, I figured that I could still use the build from the sources, even if the tests didn't run, and was able to build a hello world program.
Here are some of my first impressions. Go seems to me to primarily aimed at what C++ (and Java and C#) is used for, as well as being designed to appeal in particular to those who prefer using Python (and Ruby and Javascript). I like that it has static typing. To me, the syntax is just another not particularly elegant syntax. The concurrency model of goroutines and channels seems similar to Control.Concurrent.forkIO and Control.Concurrent.Chan in Haskell, but it also has shared memory and explicit locks like Java does, though that is discouraged in favor of using channels in idiomatic Go. Go does not have software transactional memory. Overall, it feels driven by pragmatism over elegance. I think I'd prefer using Go over C++ and Python, possibly in time over Java, but not over Haskell for most things I'd do.
Here are some of my first impressions. Go seems to me to primarily aimed at what C++ (and Java and C#) is used for, as well as being designed to appeal in particular to those who prefer using Python (and Ruby and Javascript). I like that it has static typing. To me, the syntax is just another not particularly elegant syntax. The concurrency model of goroutines and channels seems similar to Control.Concurrent.forkIO and Control.Concurrent.Chan in Haskell, but it also has shared memory and explicit locks like Java does, though that is discouraged in favor of using channels in idiomatic Go. Go does not have software transactional memory. Overall, it feels driven by pragmatism over elegance. I think I'd prefer using Go over C++ and Python, possibly in time over Java, but not over Haskell for most things I'd do.
Monday, February 27, 2012
My workflow is generally
git checkout -b topic
- Work on code, committing at various times.
- Run automated tests. Go back to working on code if a test fails.
git rebase -i master
to squash related commits.git checkout master
git pull
git rebase master topic
- Fix merge conflicts
- Run automated tests. Go back to working on code if a test fails.
git checkout master
git merge topic
git branch -d topic
git push
If this fails, go back togit pull
, except working in the master branch rather than the topic branch.
git rebase -i
, I'd open a new window and do the rebasing in vi, so I generally skipped that step and just had a bunch of commits with identical log messages sometimes interleaved with other commits. But then I figured that was just dumb and changed my .emacs file for the first time in over 5 years, adding(setenv "EDITOR" "/usr/bin/emacsclient")
(server-start)
Monday, May 23, 2011
I was writing some Haskell code where I was constructing a value with lots of record fields, but only cared about one of them initially.
But, omitting the irrelevant fields from the initializer causes a big "Fields not initialised" warning.
So, I figured that, instead of writing out
which was verbose, or
which resulted in the big warning, I could use
which did eliminate the warning.
However,
resulted in an exception. I had expected it to be equivalent to
but it's not. Since types can have multiple constructors, it's actually equivalent to
according to section 3.16.3 in the Haskell report.
data Type = Type { field1 :: Type1, field2 :: Type2 }
But, omitting the irrelevant fields from the initializer causes a big "Fields not initialised" warning.
So, I figured that, instead of writing out
Type { field1 = value, field2 = undefined }
which was verbose, or
Type { field1 = value }
which resulted in the big warning, I could use
undefined { field1 = value }
which did eliminate the warning.
However,
field1 (undefined { field1 = value })
resulted in an exception. I had expected it to be equivalent to
field1 (Type { field1 = value, field2 = field2 undefined })
but it's not. Since types can have multiple constructors, it's actually equivalent to
field1 (case undefined of { Type { field2 = field2 } -> Type { field1 = value, field2 = field2 })
according to section 3.16.3 in the Haskell report.
Subscribe to:
Posts (Atom)