Friday, October 22, 2010

Java and the Mac

So Apple has quietly announced that it is deprecating its support for Java on the Mac. The wording was (not uncharacteristically for situations like this) a bit vague on the details of what this means. I think two things are definite: 1) Apple will continue to support Java 6 on Snow Leopard until it reaches end of life; which I think happens when the OS after Lion is publicly released. 2) Apple will not port Java 7+ to the Mac. The main unresolved question is whether Java 6 will be supported on Lion. My guess is that it will be offered as an optional download, similar to Rosetta in Snow Leopard. Apple won't make any promises though, as they will want to have the option to drop it if it's too much effort.

As a Mac user who has been working on the Java platform for many years, this is a fairly big deal for me. I think it's an interesting event that provides a chance to examine both Java's position and Apple's plans for the Mac. People in the Mac Java community are speculating about the deeper meaning behind this move, especially when combined with Apple effectively banning Java platform applications from the new Mac App Store. So time for me to add my speculation to the noise.

Is Apple trying to force the use of their platform and tools?

No. True, Apple has made it crystal clear on numerous occasions (the Mac App store requirements are just the latest case) that they want developers targeting their OS platforms to use their development platform (Objective-C, Cocoa, Xcode). But I don't think they are deprecating Java in an attempt to force the issue. After all, Lion will come with Perl, Python and Ruby all pre-installed.

Does Apple want to kill cross-platform desktop apps?

No. I think it's true that, strategically speaking, Apple has no interest at all in cross-platform apps (outside of the browser). But along with announcing the deprecation, the latest Apple Java release also includes brand-new support for installing multiple versions of the Java platform, explicitly mentioning support for third-party versions. It's clear that Apple has no problem at all with Java running on Macs. I think they want Oracle to take it over, but I doubt they bothered to make any sort of deal with Oracle before making this move. Update: as I was drafting this, news came through that Steve Jobs has replied to a question about this, which basically confirms that he thinks Oracle should be responsible for Java on the Mac.

Then why did Apple do this?

The reason isn't that complicated: Apple no longer needs Java. If you make a list of what Steve Jobs sees as the critical objectives for Apple, it becomes immediately obvious that maintaining a Mac port of Java is not helping to advance any of them. Of course, neither does maintaining, say, Apple's port of Python. But Python takes very little effort to port and maintain. The Java port requires a team of engineers permanently dedicated to it. Also, the huge success of iOS has given Apple the confidence that their approach to working with third-party developers is working out great for everyone. The prospect of Java developers and applications abandoning the Mac is no longer remotely scary for them. Apple have decided they'd rather pay the costs of dropping Java than keep maintaining it.

But what are Apple's critical objectives?

The main theme of the "Back to the Mac" event on October 20 was the idea of moving iPad software and hardware features into the Mac, in a way that made sense for the Mac. My theory is that Apple has now oriented its entire strategy around the iPad. I think Steve Jobs probably regards the iPad has the best thing Apple has ever made. This is because the iPad provides the "purest" user experience: you hold it in your hands, rotate it, move it, directly manipulate the information you're working with via touch, and it dedicates itself to the task at hand. Of course, the iPhone has these characteristics too, but if will forever be limited by the (obviously worthwhile) trade-off of fitting in your pocket. The larger screen size of the iPad allows the touch interface to reach its full potential.

Slight diversion: in the recent financial call, Jobs said that 7" was a lousy form-factor for a tablet. You had better believe that he meant it. I will bet that Apple will never release an iPad with a different form factor. Going smaller puts to many constraints on the options for touch UI, so you end up with a bulky iPhone. Going larger would be nice, but it rapidly becomes unwieldy (the fact the iPad can be easily held and moved around is a critical part of the "experience"). The dimensions were chosen to balance the needs of video with those of e-books. Believe me, Apple chose the form-factor it did very carefully.

And yes, something else Jobs likes about the iPad is that Apple has more control over it. While many ascribe this to cynical motives, I believe Jobs honestly sees this as being good for the customer, and part of the reason for the success of iOS devices. Whether this view is correct will, in the end, be decided in the marketplace.

What is the future for the Mac?

So Apple sees the iPad as the future. But while it might be a "pure" experience, there's obviously a large number of important things that the iPad simply cannot do1. So Macs aren't going anywhere, but Jobs wants to make them more iPad-like. In the Mac event, Jobs called out what this meant: curated App Store, full screen apps, app "Launchpad", autosave/resume. What is equally interesting is what this implies they are leaving behind. During Craig Federighi's demo of Lion on the 20th, he showed the use of swipe to switch between a) full screen apps b) dashboard c) "the desktop". Perhaps I'm reading too much into this, but what today constitutes "the Mac Desktop experience" seems, in Lion, to be just another full-screen app you can switch to. A Rosetta-like box to run "the legacy stuff". I think Lion could be Apple's first step in really redesigning the Mac from first principles.

This iPad-like approach also reinforces other trends already underway. The traditional document-centric Mac app is basically dead. Most apps will be one-window, "content-centric", and abstract the filesystem completely. And they'll be made with Apple's tools. In other words, iPhoto '11 is the model. It's going to be fascinating to see how far Apple can take this. There have been people saying, literally for decades, that the fundamental desktop concept of movable, resizable, overlapping windows was the worst idea in the history of user interfaces. Will they be proven right? I have no idea. The best part is, as others have pointed out, Windows cannot easily copy what Mac OS X is doing. Greater distinction between the keyboard/pointer OS platforms will be a great thing, in my opinion.

Where does Java fit in all this?

It doesn't. Regardless of whether you think Apple's plans for the Mac are great or terrible, it's clear that heavy-weight "cross-platform platforms" like Java and Flash, which are controlled by others, could hardly be more useless to Apple. They don't interfere with Apple's plans, but they don't help them either. Java and Flash apps don't use compelling OS X technologies like Core Animation. They're completely clueless when it comes to multitouch. Apple only cares about one "cross-platform platform": the light-weight and mobile-friendly HTML5, which they can also influence.

Will Oracle Provide Java on Mac?

I'm not sure, but I think yes. The answer to this question will also answer many questions about what Oracle really has planned for Java and JavaFX. In my opinion, JavaFX has already lost the cross-platform war to HTML5 (I like JavaFX a lot, and would love Oracle to prove me wrong here). Oracle claims to be committed to developing and promoting JavaFX, so it apparently believes it can turn things around. But there's one thing I'm certain about: JavaFX cannot succeed as a credible platform without first-class support on the Mac. If Oracle is actually serious about JavaFX (which is client-side Java now) then it needs to very soon announce that it will be adding Mac as a fully supported platform, by Java 8 at the latest. Bringing Mac support online in a Java 7 update would be better. If Oracle lets Java on the Mac rot, then the death of client-side Java will be certain and final.

Where did it all go wrong for Java?

A few people have pointed out that just 10 short years ago Steve Jobs was proudly proclaiming that the Mac would be the premier platform for Java, and now after years of disinterest Apple is finally giving up on Java altogether. How times have changed. In 2000, Apple desperately needed apps on the Mac - any apps, and of course developers. Now, it's Sun/Oracle that really needs Java on the Mac, and Apple can afford to be indifferent.

Even if Oracle produces a brilliant implementation of JavaFX for the Mac, things do not look great for Java on the client. I won't make this post even longer by discussing the reasons for client Java's failure, but it's not because Apple didn't give Java a fair chance. Speaking as someone who really believed in and supported the "write once, run anywhere" ideal of Java, the fact has to be faced: the dream is well and truly finished. Where Java has failed, HTML5 has succeeded, primarily by keeping its goals modest and being ruthlessly pragmatic.

1 But I think that we haven't even scratched the surface of what the iPad is capable of. Many of its limitations are "a simple matter of software". I think iOS 4.2 is going to give a big boost to the iPad's utility, as will iOS 5 and beyond. Having said that, there'll always be some jobs that just need a mouse pointer and a keyboard.

Thursday, September 30, 2010

Scala's Missing Monad

I've been writing more and more pure functional code in both Scala and Java recently. An issue I found myself running into quite often is this: say you have a function that sometimes returns no results (e.g. looking up a key in a map). A common way to deal with that in Scala is to return an Option:

Simple enough. But then a typical use of such a function is to map it over all members of a collection:

What I find I often want to do in a situation like this is to only bother with the results if all the applications of foo returned a result. In other words, I want to turn the strings list above into an Option[List[String]], which is Some(List(foo(1).get, foo(2).get, foo(3).get)) if all those applications of foo give values, and None otherwise. I couldn't see any method in the Scala standard library that does what I want.

As a bit of a functional programming n00b, one approach I've found to be useful is to work out the Haskell type signature of the function I want, feed that into Hoogle and see what I get, if anything. In this case, the Haskell type is:

The top Hoogle result for that is the sequence function:

And look, the sequence function works on any monad, not just Maybe/Option. For example, if my foo function had been returning Either[SomeErrorType, String], then the sequence function would give the first error in the list if there was an error, or the list of results if there was no error. So this is a useful function, but as I said, I can't find it in the standard Scala library.

When I thought about how to implement sequence in Scala, I immediately ran into trouble. It is often said that "Scala has monads", and we know that the flatMap method is doing a monadic bind. But this support isn't much more than a convention for method names and type signatures, combined with nice syntax in the form of for expressions. There is no monad type declared anywhere in the standard library. Without a monad type, the sequence function would have to be re-implemented over and over: Option.sequence, Either.sequence, List.sequence, etc.

The good news is that Scala's type system can express the monad type. And the better news is that some really smart people have already done all the work for us in a library called Scalaz. So I had a look, and lo and behold there is the sequence function:

That may look nothing like the Haskell function, but that is mostly a result of how typeclasses are expressed in Scala. Trust me, this is the same function. Also, this Scalaz function is more general than the Haskell function above. It turns out that sequence works for any "traversable" thing (not just lists) containing anything that is an applicative functor (not just monads). Haskell also has the equivalent fully generalised sequence function in the Data.Traversable library.

So what did I learn from this exercise? First that the lack of explicit types for monad, functor and friends in Scala's standard library is a greater problem than I'd expected. Second, if you're writing pure functional Scala code, you should be using Scalaz. Since all Scala programmers should be writing pure functional code, it follows that all Scala programmers should be using Scalaz!

Friday, January 08, 2010

Cocoa vs Haskell

So here's the deal - if you want to write a Mac GUI app, you want to use the Cocoa framework. As a practical system for writing desktop GUI apps, the Cocoa stack simply has no equal anywhere. Part of what makes Cocoa so great is the Objective-C language, which adds a very flexible and dynamic OOP system on top of standard C.

Objective-C turned out to be a great match for creating desktop GUIs. It's obviously a general-purpose language, but for general programming it would be no where near my first choice. My first choice right now would be Haskell, even though I'm still a beginner and I pretty much suck at it. But I get why Haskell is so good (and so much fun).

So if I want to write a Mac GUI app, can I use Haskell? I've had a play with HOC which is a very neat bit of software that bridges Objective-C to Haskell. You can call Haskell from Objective-C and implement Objective-C classes in Haskell. There are some challenges trying to use HOC—it's not exactly straightforward to get it up and running and integrated into your XCode project; and it's under active, but slow, development and doesn't have a stable release. But at a more fundamental level I've come to the conclusion that, while it's very cool, HOC probably isn't worth it.

If you can get HOC working, you'll have a full-blown bridge that lets you use Cocoa APIs from Haskell. But is that actually worthwhile? The things that Haskell excels at are basically useless when using Cocoa, which is completely based around stateful objects being mutated by events and messages. While you could have all your code—including the Cocoa GUI stuff—in Haskell, the GUI code will all be imperative using the IO monad. All the effort just gets you Objective-C with nicer syntax.

So for Mac apps, I think sticking with Objective-C for the GUI code is the best choice. As a language, it sucks compared to Haskell, but it just works very well with the Cocoa APIs. But what about using Haskell to implement the Model and do other computational heavy-lifting under the hood? This turns out to be much simpler than a full Objective-C bridge. GHC of course offers a standardised foreign function interface to C out of the box, and Objective-C is a superset of C. So you can call Haskell from Objective-C code just as you would from plain C, and it works quite naturally.

But it's a bit trickier to build a complete Cocoa application that does this. There is a HaskellWiki page that describes one approach to getting a complete XCode project set up. I tried it out and it works, although things don't integrate very well. You have to compile the Haskell code using ghc outside of the XCode build process, and you have to manually track down all the libWhatever.a files that your Haskell code needs to link against. I've made a simple demonstration project for XCode 3.2 that works for me on OS 10.6: http://github.com/quelgar/HaskellCocoaDemo. This is basically the classic Currency Converter app from Apple's Cocoa tutorial, but it does the currency calculation in Haskell. Here's the rough list of steps I performed, starting from the standard XCode template for a Cocoa Application:
  • In the project build settings:

    • Set the architecture to just "i386". GHC is today only built for 32-bit Intel on Mac
    • Add "-liconv" to "Other Linker Flags". GHC libs need it.
    • Add your GHC include directory to "Header Search Paths". For GHC 6.12.1 on my system, this was "/Library/Frameworks/GHC.framework/Versions/Current/usr/lib/ghc-6.12.1/include/".
    • Add "/Library/Frameworks/GHC.framework/Versions/Current/" to the "Library Search Paths" and mark it recursive. Not sure if this is actually necessary since we end up adding all the libraries to the project manually anyway.

  • The Haskell function that will be called from C is in HaskellConvert.hs, which uses the typical FFI stuff. You compile this with GHC like so: "ghc -c HaskellConvert.hs". This outputs various files, of which the following have to be added to your XCode project:

    • HaskellConvert.o
    • HaskellConvert_stub.o

  • You now have to add all the lib*.a files from GHC that your Haskell code needs to link successfully. The wiki page I linked to above describes one way of doing this. It's not pretty, but it works. Once you know you need a particular .a file, just drag it from the finder to the project, and accept all the defaults in the add file dialog. It will be added to the link stage automatically.
  • Modify main.m to perform the Haskell runtime setup and teardown.
  • In this demo, Haskell is called from Converter.m, so this file needs #import "HaskellConvert_stub", and then just make the call to Haskell.
Probably the biggest downside of this plain C approach vs HOC is that you can't pass Objective-C objects to Haskell (well, you can, but they are not easy to use from Haskell, to say the least). So you have to convert whatever Objective-C data structures you're using in the GUI to plain C types for interfacing with your Haskell APIs. Haskell wrappers for the OS X C APIs for CoreFoundation and the Objective-C runtime might make using Objective-C objects from Haskell feasible without a full language bridge, but I'm not sure about this.

The GHC installer for Mac installs the bulk of GHC under /Library/Frameworks/GHC.framework, but I don't think it actually works as a Mac framework. I could be doing something wrong, but adding it to XCode as a framework didn't work (XCode just said it wasn't found when I tried to build, from memory). It would be great if GHC could be made a fully-functional Mac framework, as we would no longer need to manually set include and lib search paths, or explicitly add every Haskell .a file to the project. But to make this useful, I think we'd need some automated way of building any Cabal package as a framework. I'm not sure if that's feasible.

Tuesday, December 15, 2009

Some simple Scala null tricks

Scala code often needs to deal with null references, unfortunately. Such is the price of Java interoperability. One of my favourite utility methods is:

Scala 2.8 offers this using Option(ref) instead of ?(ref). However, I can't figure out why the Scala 2.8 method accepts non-reference values as well.

Another simple thing that may come in handy is an extractor for non-null values:

What's the advantage of using an extractor? Say you have a collection of references, some of which may be null, and you want to ignore the null references:

Monday, July 06, 2009

The FSF doesn't care if they're lonely

Daniel Jalkut, a leading light of the Mac developer community recently blogged about the GPL and some of consequences of it for collaborative software development. I agree with what he says, and I dislike how the GPL limits collaboration in a number of ways. But I thought it might be useful to go over the background and objectives of the GPL.
There's an important thing to remember when talking about the GPL: the impact, good or bad, on the practicalities of software development have nothing to do with its objectives. The authors of the GPL, the Free Software Foundation just aren't concerned by the kinds of issues that Daniel raises. The fundamental principle of the FSF is that anyone should have complete freedom to study, modify and share all computer software. The end-game of the FSF is that all restrictions that apply to software, such as copyrights and patents, be abolished altogether (in so far as they currently apply to software). In other words, they want any software written by anyone to have no legal protection from examination, modification and redistribution by anyone else. The FSF really wants all software to be in the public domain, without the option of copyright protection.
The GPL is their attempt to bring this about given the reality that copyright of software exists today and shows no signs of going away. The GPL is designed to use the copyright system against itself; the FSF calls this "copyleft". In typical commercial software, the copyright is used to prevent the licensee (user) of the software from doing things with it that the author doesn't like (such as modification or redistribution). The GPL instead prevents the licensee from adding any new restrictions. The end result of putting software under the GPL is basically the same as if copyright protection for that software didn't exist at all.
Now, you can make many arguments pointing out the practical downsides of removing copyright restrictions on software (whether by abolishing software copyright, or via the GPL). Daniel's post is a good example of this. But to the FSF, these considerations are secondary to the principle that any restrictions to what users can do with their software is morally Wrong. You can see an example of this in the GNU Manifesto where they acknowledge that adopting their policies could result in programmers earning less. Their view seems to be that doing the "right thing" can have effects that aren't necessarily desirable, but we just have to live with as best we can. Perhaps a good analogy is free speech, which necessarily increases the risk of children accessing pornographic or violent material.
I try to take a pragmatic "what best serves the common good?" approach to matters, which leads me to agree with the FSF with regard to software patents, but disagree with them on copyright. I think patents, when applied to software, are all harm and no benefits. For copyright, I can see greater upside than downside to the current system. It's true that without software copyrights we would all have greater freedom regarding how we can use our computers, but it's also likely that our computers would be of much less practical use to us. Do professional photographers and artists prefer GIMP or Photoshop?
Just to be clear, arguing over the implications of the GPL on software development practice is a very useful thing to do. Many people who don't care about the political position of the FSF still chose to use the GPL simply because it happens to suit their needs, and there's nothing wrong with that. But having a clear understanding of the costs and benefits is essential.

Monday, June 01, 2009

Project Euler Problem 8

I guess I should warn that these Project Euler posts have spoilers, in case you want to try the problems yourself ☺. My Scala solution to problem 8:

val input = "73167176531330624919225119674426574742355349194934" +
"96983520312774506326239578318016984801869478851843" +
"85861560789112949495459501737958331952853208805511" +
"12540698747158523863050715693290963295227443043557" +
"66896648950445244523161731856403098711121722383113" +
"62229893423380308135336276614282806444486645238749" +
"30358907296290491560440772390713810515859307960866" +
"70172427121883998797908792274921901699720888093776" +
"65727333001053367881220235421809751254540594752243" +
"52584907711670556013604839586446706324415722155397" +
"53697817977846174064955149290862569321978468622482" +
"83972241375657056057490261407972968652414535100474" +
"82166370484403199890008895243450658541227588666881" +
"16427171479924442928230863465674813919123162824586" +
"17866458359124566529476545682848912883142607690042" +
"24219022671055626321111109370544217506941658960408" +
"07198403850962455444362981230987879927244284909188" +
"84580156166097919133875499200524063689912560717606" +
"05886116467109405077541002256983155200055935729725" +
"71636269561882670428252483600823257530420752963450"

val digits = input.map(_.asDigit).toArray

def multiply(index: Int) = digits.slice(index, index + 5)
    .foldLeft(1)(_*_)

val multiples: Stream[Int] = {
    def rec(n: Int): Stream[Int] = Stream.cons(multiply(n),
        if (n > digits.length - 5) Stream.empty else rec(n+1))
    Stream.cons(mult(0), rec(1))
}
println(Iterable.max(multiples))

For convenience, this specifies the input as a string, then uses RichChar.asDigit to create a corresponding array of integers. The multiply function uses left fold to multiply together sequences of five digits. The multiples value is a stream (lazy list) of the multiples of all groups of five consecutive digits from the input. And finally, the Iterable object provides a convenient method to find the maximum value in any Iterable containing Orderable things.

Project Euler in Scala

Project Euler is a bunch of mathematical/programming problems. I've been trying to improve my Scala skills by using it on some of the Euler problems. I'm trying to use functional programming styles as much as possible. My solutions definitely haven't been very optimized, but I am learning, which is the aim.

Euler Problem 1

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.

My solution:

def sum(nums: Iterable[Int]): Int = (0 /: nums)(_ + _)

println(sum((3 until 1000).filter(i => i%3 == 0 || i%5 == 0)))

The 3 until 1000 creates a "range", which is a lazy sequence of numbers from 3 to 999 inclusive. Because it's lazy, you don't get 997 integers sitting around in memory, you only get those integers which pass the filter. The filter itself shows the Scala syntax for a "lambda expression" (anonymous function). The full syntax is (arg1, arg2, ..) => function body, but when you only have one argument (a single Int, in this case) you can leave out the brackets.

The sum function is the operator syntax for a left fold over nums. The _+_ is an even more terse form of a lambda expression that you can use for really simple expressions. The first underscore is replaced by the first argument to the function, and the second is replaced by the second argument.