Decided to take another day on Julia, write something more serious and see how that goes.
There's an uber-juno "IDE" plugin for Atom, which at least turns on syntax highlighting and puts an interactive console in the editor. Yay. It's not capable of linting yet, though it says it is.
So I'm rewriting a simple 1970s-style dungeon crawl game (Chorus:"As if you could make any other kind of game, Mark!" Mark:"I assure you I could, I just choose not to.") as a test of data structures and application programming in Julia. It's a little challenging, but not impossible.
Using Modules
The current directory is not included in the default LOAD_PATH, so you can't import local modules right off. One solution is to put it in your startup:
% mkdir -p ~/.julia/config
% echo '@everywhere push!(LOAD_PATH, pwd())' >>~/.julia/config/startup.jl
All names are in the same namespace. This means your module and a struct or method in it can't have the same names… My current solution has been to pluralize the module, so GridMaps contains a struct GridMap.
The export rules are a little annoying. If you use the @enum macro to make a ton of constants, they aren't exported when you export the enum type; you can either manually export each constant name, or just use ModuleName.ConstantName in other modules. Bleh.
Debugging
Bug #1: The terminator problem is pretty bad in Julia:
% cat Foo.jl
module Foo
for i=1:10
println(i)
#missing end
end #module
% julia Foo.jl
ERROR: LoadError: syntax: incomplete: "module" at /Users/mdh/Code/CodeJulia/Foo.jl:1 requires end
Stacktrace:
[1] include at ./boot.jl:317 [inlined]
[2] include_relative(::Module, ::String) at ./loading.jl:1038
[3] include(::Module, ::String) at ./sysimg.jl:29
[4] exec_options(::Base.JLOptions) at ./client.jl:229
[5] _start() at ./client.jl:421
in expression starting at /Users/mdh/Code/CodeJulia/Foo.jl:1
Good luck finding that missing end
if you have a 1000-line module. C used to be just as bad about semicolons and braces, but modern compilers are pretty good at guessing where you fucked up. Python's whitespace-as-control is brilliant, because you can't ever do that. A passable solution would be each control keyword having its own unique end keyword, but it's too late for that. In the actual bug, I had to comment out half the code, run the module, uncomment and comment the other half, repeat until I isolated it.
Bug #2: Type annotations need to be very generic or left off entirely. As code-as-documentation, I declare a function as parseLine(line::String), and it gives me:
MethodError(Main.Foo.parseLine, ("a",), 0x00000000000061bb)
Well, thanks. Turns out I need to use AbstractString, because a prior function returns an AbstractString and not String. Or I can just leave the typing off, as was my first instinct.
String Concatenation
This is super ugly. Currently I've fallen back on:
sb = Vector()
push!(sb, "part of ")
push!(sb, "a string")
return join(sb, "")
Strings aren't mutable, and there's no StringBuffer/NSMutableString equivalent. The other option is to use an IOBuffer. I haven't done timings yet to see which is faster/uses less memory, I just find pushing to a vector simpler.
Switch
There's no switch statement. I could put functions in a dictionary and dispatch on that, which is not ideal for looping over a bunch of simple values, and requires passing around control flags instead of a simple break or return. Caveman solution is a chain of if/elseif, but I don't like it. Possibly a macro could be written?
Binary
Turns out you can make binaries: Julia apps on the App Store: Building and distributing an application written in Julia
It's an ugly process, that Nathan's working around, but it's a start. This should 100% be in the core libraries, and should have a cross-compiler.
Getting this working is tomorrow's problem, I think.