Thursday, May 19, 2011

Java local methods

Local functions are a neat paradigm in most dynamic languages like Python and JavaScript:
def recursiveList(path):
def collect(path, result):
# this is a function inside another function
result.append(path)
if os.path.isdir(path):
for child in os.listdir(path):
collect(os.path.join(path, child), result)
result = []
collect(path, result)
return result
One big design win of local functions is namespace cleanliness. The function collect is not visible anywhere but inside of recursiveList.

Unfortunately, C/C++ has no way of doing this that I know of. Java gets really close to local functions with the ability to use anonymous classes. An anonymous class is especially useful for implementing an interface with a single method. Here's an example:
public static void pipeStreamsAsynchronously(final InputStream in, final OutputStream out) {
Thread thread = new Thread(new Runnable() {
// Runnable is an interface with just a void run() method.
public void run() {
try {
while (true) {
// this call to read() can block
int b = in.read();
if (b == -1)
return;
out.write(b);
}
} catch (IOException e) {
}
}
});
thread.start();
}
It occurred to me only today that anonymous classes can be used just like local functions by using initializers. Initializers are segments of code that run at the beginning of (almost) every constructor. You declare them like this:
public class LinePrinter {

private final String newline;

{
// this is an initializer
if (System.getProperty("os.name").startsWith("Windows"))
newline = "\r\n";
else
newline = "\n";
// NOTE: this is the wrong way to get the platform's line terminator.
}

private final PrintStream out;
public LinePrinter() {
// initializer has run by now
out = System.out;
}
public LinePrinter(PrintStream out) {
// initializer has run by now
this.out = out;
}
public void print(String line) {
out.print(line);
out.print(newline);
}
}
You can also put a static before the { to make it a static initializer which runs once when the class is first accessed at runtime. Static and non-static initializers are the mechanisms java compilers use behind the scenes to evaluate initial assignments for fields with an assignment built into their declarations:
public class MyClass {
// the getProperty() call runs in an implicit static initializer.
public static final String NEWLINE = System.getProperty("line.separator");

// the length() call runs in an implicit non-static initializer.
private final int newlineLength = NEWLINE.length();
}
In general, explicit non-static initializers are not particularly useful since they offer code reuse only in the case non-converging constructor overloads. The proper way to implement the above LinePrinter class would be have the parameterless constructor call this(System.out); and have the other constructor do the newline initialization logic. The example is only to demonstrate when initializers run.

Here's how you can use anonymous classes and initializers together to make local functions in Java:
public static ArrayList<File> recursiveList(final File path) {
final ArrayList<File> result = new ArrayList<File>();
new Object() {
private void collect(File path, ArrayList<File> result) {
result.add(path);
File[] children = path.listFiles();
if (children != null)
for (File child : children)
collect(child, result);
}
{
// this is the initializer
collect(path, result);
}
};
return result;
}

Thursday, July 29, 2010

wtf, msys!

in an msys shell (from running C:\msys\1.0\msys.bat):
$ python -c "import sys;print(sys.argv)" "C:/A"
['-c', 'C:/A']

$ python -c "import sys;print(sys.argv)" "C:/A C:/A"
['-c', 'C;C:\\msys\\1.0\\A C;a:\\']
Why would you do that! The solution is to use backslashes:
$ python -c "import sys;print(sys.argv)" "C:\A C:\A"
['-c', 'C:\\A C:\\A']
But again, wtf!!

Friday, April 16, 2010

Git Fail

$ git add file-that-is-1.4GB
fatal: Out of memory? mmap failed: Cannot allocate memory
Is that it? Git just can't handle files that are that big?

Yep. That's it. Here are other people who have found the same thing:

http://blahg.josefsipek.net/?p=219
http://ubuntuforums.org/archive/index.php/t-634946.html

I guess this is one more way in which SVN is better than Git. I still like Git, but it's by no means better than SVN in every way.

Why does "suppress" have 2 'p's?

Forgive me for thinking that "supress" was correct spelling. All the rules about English spelling I know say that that word should have only one 'p'. Look at the word "supremacy". That has only one 'p'. The etymology of "suppress" is sub + premere [wiktionary]. That doesn't even have 'p's at all, let alone two of them. You know what, I'm just going to keep spelling it "supress".

Friday, April 9, 2010

MSYS Make and Windows Paths

Why can I not find any documentation about how Windows absolute paths are interpreted in MSYS make?
C:\TEMP\tmp2.txt: C:\TEMP\tmp.txt
touch C:\\TEMP\\tmp2.txt
This works just fine in MSYS make. Cygwin make can't handle all those colons. Why doesn't MSYS document how it handles paths anywhere?

Tuesday, April 6, 2010

Remote Desktop Connections suck at event queuing

Microsoft Remote Desktop Connections are great when the network is working. When there's a brief lag in the connection, events are queued. Great. The problem is that keyboard events and mouse events are in DIFFERENT QUEUES!! The network just lagged for me, so I clicked in a location in a text editor and typed a word. When the lag passed, my events went through, but the word was typed at the old cursor location, and then the mouse click went through and jumped the cursor to the new place. Would it really have been that hard to buffer all input in the same queue?? Come on!

Monday, April 5, 2010

Why Git is like a Microsoft product

That's right, I just said Git is like it's written by Microsoft. Or rather designed by. It works, which is awesome, but its design features one serious drawback of which I find Microsoft products to be the most prominent source. The drawback is namespace pollution.

In Windows, you're not allowed to name a file "CON". Why? Because of namespace pollution. There are 25 reserved names that show up in the context of file names, but that aren't really file names because they're special.

This writes "hello" into a file called "conn":
echo hello >conn
This doesn't create a file at all. I have no idea what it does:
echo hello >con
The only difference is that "con" (case insensitive) is a special name. I'm calling this namespace pollution because there are a set of names (file names) and a set of names (reserved names) that are mixed together.

In Git it's a lot clearer what I mean by namespace pollution.
git checkout thing
What does this do? Can't tell. It might revert a file called "thing" to it's state at the last commit. It might switch the entire working directory to another branch called "thing". Which of these happens depends on whether there's a file called "thing" or a branch called "thing". If both exist, I'm sure one of them happens, but I don't know which without doing an experiment or reading through documentation. I'm not going to do that. Why can't there be different syntax for the two? Like
git switchtobranch thing
and
git revertfile thing
I'm going to say it. SVN's syntax is much more understandable.