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:
  • ./app
  • ./app/app.go
  • ./module
  • ./module/interface.go
  • ./module/implementation1
  • ./module/implementation1/implementation1.go
  • ./module/implementation2
  • ./module/implementation2/implementation2.go
where ./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:

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 to java.util.List, especially with Java generics. Generics are almost essential for useful containers, so Go inelegantly has the built-in function append and the built-in map 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.

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 to git pull, except working in the master branch rather than the topic branch.
And I do this from a shell within emacs. For the longest time, if I really wanted to do the 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.

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.