SimplyScala is awesome for trying out some quick Scala tricks. However, you can go one step further, and execute Scala one-liners in your Firefox location bar!
1. Create a bookmark to URL http://www.simplyscala.com/interp?code=%s
2. Associate a keyword to the bookmark. Mine is "scala", you can try something shorter like "s" or ">".
3. Now when you type "scala <expression>", the expression will be sent to SimlpyScala and evaluated. For instance, "scala List(1,2,3).reverse"
Why does this work? Whenever you have a keyword associated with a bookmark Firefox expands "%s" in the URL to the text following the keyword. Check this Lifehacker article for details.
The nice thing is that your bindings are kept in your session, so you can use variable names defined in previous invocations. There are inconveniences, for instance you cannot easily preview your history and it's hard to review inputs which are more than a couple of lines long.
I also find that in combination with URL-shortening services it's an acceptable alternative to using code snippet sharing sites like gist.github.com or paste.pocoo.org. The code is actually part of the URL, which makes sense for shorter code snippets. You don't have syntax highlighting, but you have the evaluated result readily available.
Saturday, September 26, 2009
Wednesday, September 16, 2009
3 things you didn't know Scala pattern matching can(not) do
Since I started learning Scala, pattern matching has become a favorite feature. But powerful as it is, pattern matching has some limitations. On the other hand, there are some unexpected ways to use pattern matching.
You already knew you can initialize multiple variables using the tuple syntax:
I want to do the same with closure parameters, though. For example, to swap the elements of 2-tuples in a list:
Still, using numbered slots for tuples doesn't seem as readable. It would be great if we could easily assign variables with meaningful names to document the purpose of the elements of the tuple. Parameters are already packed in a tuple, so why can't I do this:
Unfortunately this is not legal syntax. Of course, I can explicitly bind the tuple to a temporary variable and then decompose it:
But this seems too verbose for Scala. Fortunately, there's a trick which can help. Pattern matches are actually instances of partial functions! The closures used for map, filter, etc. are functions, too. This means we can use a pattern match to bind variables to the closure parameters:
Decomposing lists in pattern matching is another power feature of Scala. You can match the first element of a list:
The underscore-star (_*) symbol serves as a placeholder for the rest of the elements of the list. You can also do this:
Which mimicks construction of the list using the cons operator. So what if I want to match on the last element instead of the first?
Hey, this seems like it's working! Not so fast, though:
In fact, in Scala 2.8 this syntax will trigger an error message: "_* may only come last"
How about using the alternative notation?
Unfortunately Scala expects the placeholder to be an element, not a list. The triple colon expects a list, will it work?
Nope, this isn't even recognized by the compiler. While you're wondering why there is a ::: operator, but it's unknown to Scala in pattern matching, remember that ::, which is used in pattern matching, is actually a class. Furthermore, the syntax "a :: b" is in fact a more readable expression of "::(a, b)", which in turn is a short form of "call unapply on the matching expression and check if it decomposes to a and b".
And this, in short, is how the Scala black magic, called "extractors", works. But can we also use it to match on the last element of a list? Sure! Just define an object (let's call it "::>") and define its unapply method. The method expects a list and must return a tuple of the "init" part of the list and the "last".
Since you've heard of structural types, didn't you wish you could do this to find out whether a class has a certain method?
Unfortunately, here again we are fooled in thinking method checking happens when matching the class. It's not:
The reason for this is that the class type is erased to AnyRef. Unfortunately, there's no clean workaround for this- yet. This looks positively ugly:
Do not despair, though- there's a chance that this feature is going to land in Scala sooner or later.
Fake multiple assignment of tuples
You already knew you can initialize multiple variables using the tuple syntax:
val (a, b) = (1, 2)
I want to do the same with closure parameters, though. For example, to swap the elements of 2-tuples in a list:
List(1 -> "one") map {
t => (t._2, t._1)
}
Still, using numbered slots for tuples doesn't seem as readable. It would be great if we could easily assign variables with meaningful names to document the purpose of the elements of the tuple. Parameters are already packed in a tuple, so why can't I do this:
List(1 -> "one") map {
val (num, str) = _; (str, num)
}
Unfortunately this is not legal syntax. Of course, I can explicitly bind the tuple to a temporary variable and then decompose it:
List(1 -> "one") map {
t => val (num, str) = t; (str, num)
}
But this seems too verbose for Scala. Fortunately, there's a trick which can help. Pattern matches are actually instances of partial functions! The closures used for map, filter, etc. are functions, too. This means we can use a pattern match to bind variables to the closure parameters:
List(1 -> "one") map {
case (num, str) => (str, num)
}
Match the last element of a list
Decomposing lists in pattern matching is another power feature of Scala. You can match the first element of a list:
List(1, 2, 3) match {
case List(1, _*) => "yeah!"
}
The underscore-star (_*) symbol serves as a placeholder for the rest of the elements of the list. You can also do this:
List(1, 2, 3) match {
case 1 :: _ => "hurray!"
}
Which mimicks construction of the list using the cons operator. So what if I want to match on the last element instead of the first?
(1 to 9).toList match {
case List(_*, 8, 9) => "sure"
}
Hey, this seems like it's working! Not so fast, though:
(1 to 9).toList match {
case List(_*, 18) => "yeah, right"
}
In fact, in Scala 2.8 this syntax will trigger an error message: "_* may only come last"
How about using the alternative notation?
(1 to 9).toList match {
case _ :: 9 :: Nil=> "no way"
}
Unfortunately Scala expects the placeholder to be an element, not a list. The triple colon expects a list, will it work?
(1 to 9).toList match {
case _ ::: 9 :: Nil => "absolutely not"
}
Nope, this isn't even recognized by the compiler. While you're wondering why there is a ::: operator, but it's unknown to Scala in pattern matching, remember that ::, which is used in pattern matching, is actually a class. Furthermore, the syntax "a :: b" is in fact a more readable expression of "::(a, b)", which in turn is a short form of "call unapply on the matching expression and check if it decomposes to a and b".
And this, in short, is how the Scala black magic, called "extractors", works. But can we also use it to match on the last element of a list? Sure! Just define an object (let's call it "::>") and define its unapply method. The method expects a list and must return a tuple of the "init" part of the list and the "last".
object ::> {def unapply[A] (l: List[A]) = Some( (l.init, l.last) )}
List(1, 2, 3) match {
case _ ::> last => println(last)
}
(1 to 9).toList match {
case List(1, 2, 3, 4, 5, 6, 7, 8) ::> 9 => "woah!"
}
(1 to 9).toList match {
case List(1, 2, 3, 4, 5, 6, 7) ::> 8 ::> 9 => "w00t!"
}
Match structural types
Since you've heard of structural types, didn't you wish you could do this to find out whether a class has a certain method?
List(1) match {
case t: {def length: Int} => "cool!"
}
Unfortunately, here again we are fooled in thinking method checking happens when matching the class. It's not:
List(1) match {
case t: {def aoeu: Int} => "really?"
}
The reason for this is that the class type is erased to AnyRef. Unfortunately, there's no clean workaround for this- yet. This looks positively ugly:
List(1) match {
case t: {def aoeu: Int}
if t.getClass.getMethods.exists{_.getName == "aoeu"} => "not really!"
}
Do not despair, though- there's a chance that this feature is going to land in Scala sooner or later.
Tuesday, June 23, 2009
The ultimate Twitter client
...is a Feed reader!
OK, not quite, but after some thinking about the top features the ideal Twitter client should have, I found feed readers have most. For the minor drawback of not being able to post messages, you get:
But some of these "features" require you to import the feed of every single one of your users. Noone in their right mind would do that manually, but there's a way to generate a list of feeds in the form of an OPML file (which many readers can import). First of all, get a list of your friends. Then apply the following XSL stylesheet:
This contains only the most essential elements of a feed, and the name of each feed is the ID, not the name, but Gooogle Reader imports the feed successfully and some readers can rename the feed from information provided in the RSS/Atom format.
For an OPML of your friends' favorites, replace "statuses/user_timeline" with favorites.
OK, not quite, but after some thinking about the top features the ideal Twitter client should have, I found feed readers have most. For the minor drawback of not being able to post messages, you get:
- Marking messages as read
- Fixed replies
- Favorites of friends
- Tracking
- No follow/unfollow counting
Marking mails as read is absolutely critical for email clients, since you can easily see at any time which messages are new. There's enough cognitive load on your brain already, it doesn't need to rescan again and again messages you've already seen. Paradoxically, many Twitter clients don't have this feature, and there are far more messages in a typical twitter timeline than in a typical email inbox.
If you know what #fixreplies means, you know that some folks prefer to see the replies of their friends to people who are not also their friends. This is an excellent way to discover new people to follow and manages to capture a lot more interesting conversations. Well, if you track the individual feeds of your friends (more on that later), all of their replies are there.
Yeah, most Twitter clients let you see your own favorites, but that's backwards. Of course, you already know which tweets you've marked as favorites! What you really want to know is what others, mostly your friends have marked as favorites. You could see favorites' timelines of individual users, but who would want to check all of their friends' favorites manually? There's a real need to track an aggregated list of messages your friends recommend. The whole flood of retweet (aka "RT") messages are a response to this need. Retweeting is not a solution though, as it pollutes Twitter and especially search results with tweets with duplicate content. Ever searched for Scala on the day The Register published the article that Twitter is "dumping" Ruby for Scala (ignore the fact Twitter was not really dumping Ruby)?
Remember the days of old when Twitter had Instant Messaging integration? And you could track messages from any user by a keyword? It couldn't scale, of course. Then there was Twitterspy, which provided identical functionality, but it fell down under the weight of popularity too. Well, you can use Twitter search. And you can create an atom feed out of your Twitter search. It does not have the real-time responsiveness of IM, but I'd take that over nothing.
This is actually a feature. People have complained before that followers' count should go, since it serves no purpose other than trophy collecting and is no measure of the usefulness of someone's tweets.
There's also Qwitter. It's a service, which shows you when someone quits, and the message after they quit. If you thought for a moment this is good, think again. Rarely ever someone decides you're not worth following after a single tweet. The corresponding reaction could be either to become too careful about what you tweet (you become too boring) or demanding an explanation from the qwitter (lame, but I heard some are doing it). Noone will chastise me if I unsubscribe from their blog because I have no time to read it, why take Twitter personally?
But some of these "features" require you to import the feed of every single one of your users. Noone in their right mind would do that manually, but there's a way to generate a list of feeds in the form of an OPML file (which many readers can import). First of all, get a list of your friends. Then apply the following XSL stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="ISO-8859-1" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<opml>
<body>
<outline title="twitter">
<xsl:for-each select="/ids/id">
<outline>
<xsl:attribute name="title"><xsl:value-of select="."/></xsl:attribute>
<xsl:attribute name="xmlUrl">http://twitter.com/statuses/user_timeline/<xsl:value-of select="."/>.atom</xsl:attribute>
</outline>
</xsl:for-each>
</outline>
</body>
</opml>
</xsl:template>
</xsl:stylesheet>
This contains only the most essential elements of a feed, and the name of each feed is the ID, not the name, but Gooogle Reader imports the feed successfully and some readers can rename the feed from information provided in the RSS/Atom format.
For an OPML of your friends' favorites, replace "statuses/user_timeline" with favorites.
Monday, February 9, 2009
3 Scala gotchas
It's been over a year since I bought my "Programming In Scala" book by Martin Odersky. Since then, a lot has changed. There are now 3 more Scala books in the works: a "Programming Scala" by Venkat Subramaniam (Pragmatic Bookshelf), "Beginning Scala" by David Pollak (Apress) and another "Programming Scala" by Alex Payne (of Twitter fame) and Dean Wampler (by O'Reilly). Scala is the key actor in a couple of new software projects (pun intended), including one at Apache (ESME, to which yours truly is a committer; ok, I have no shame).
There's no perfect language, though, and you can't say you know a language until you know its warts. So here's my share of traps, which wasted a lot of my time- I certainly hope you discover them before you waste yours. Of course, these don't mean the language is ill-designed; if only minor corner cases like these can be discovered, this would suggest that a lot of thought has been put in the language.
You copy & paste a simple example and compile it. Great, everything works and you're happy. So now you read about companion objects and decide to use your main method so you can test the companion class. You start with a dummy and (if you're cautious), try to compile right away:
Good, it compiles. Running it, though, results in a NoSuchMethodException for main. Huh? Well, there's a main method only in singleton objects, not in companion objects.
Here, you want to match on the contents of a variable. It works as expected: in the above example a MatchError is thrown, as expected. So suddenly you decide that you want to rename the variable...
Boom! Now pattern matching matches things it shouldn't! Actually, it matches everything you throw at it. What has happened? Well, Scala checks the name in order to decide if it's an existing constant (capitalized first letter) or a new variable name it should bind (lower-case first letter). So in the last example a new variable called "fileExtension" was created, which shadows the existing variable name.
No surprises here- one method prints a String, the other returns one. So then you suddenly decide that you want only one method to this (contrived) and merge these two:
...and the output is not what is expected: only "test1" is printed. What's wrong here? In Scala, if you omit the equals sign after the method declaration and before the method body, the result is always Unit (same as void in Java).
There's no perfect language, though, and you can't say you know a language until you know its warts. So here's my share of traps, which wasted a lot of my time- I certainly hope you discover them before you waste yours. Of course, these don't mean the language is ill-designed; if only minor corner cases like these can be discovered, this would suggest that a lot of thought has been put in the language.
Main method in companion objects
object Test {
def main(args: Array[String]) {
println("Running!")
}
}
You copy & paste a simple example and compile it. Great, everything works and you're happy. So now you read about companion objects and decide to use your main method so you can test the companion class. You start with a dummy and (if you're cautious), try to compile right away:
class Test {
}
Good, it compiles. Running it, though, results in a NoSuchMethodException for main. Huh? Well, there's a main method only in singleton objects, not in companion objects.
Variables and constants in pattern matching
val Extension = "xml"
"json" match {
case Extension => "Wrong!"
}
Here, you want to match on the contents of a variable. It works as expected: in the above example a MatchError is thrown, as expected. So suddenly you decide that you want to rename the variable...
val fileExtension = "xml"
"json" match {
case fileExtension => "Wrong!"
}
Boom! Now pattern matching matches things it shouldn't! Actually, it matches everything you throw at it. What has happened? Well, Scala checks the name in order to decide if it's an existing constant (capitalized first letter) or a new variable name it should bind (lower-case first letter). So in the last example a new variable called "fileExtension" was created, which shadows the existing variable name.
Always returning Unit from methods
def test1 {
println("test1")
}
def test2 = {
"test"
}
No surprises here- one method prints a String, the other returns one. So then you suddenly decide that you want only one method to this (contrived) and merge these two:
def test {
println("test1")
"test2"
}
println(test)
...and the output is not what is expected: only "test1" is printed. What's wrong here? In Scala, if you omit the equals sign after the method declaration and before the method body, the result is always Unit (same as void in Java).
Wednesday, January 28, 2009
Scala is static- is Scala dynamic?
Despite being statically typed, Scala is not an inflexible language. Smarter folks than me have commented on extending the behavior of classes dynamically using implicits.
I want to focus on another feature of Scala which allows for dynamic behavior. In concurrent programming with actors, the default handling involves pattern matching on the messages received. Pattern matching and actor concurrency have been borrowed from Erlang- a dynamic language by nature- and have been one of the key differentiating concepts compared to languages I've learned before.
When calling a method on a class, you have to know the method signature at compile-time. The types of the parameters you pass must be defined (even through an implicit type conversion). But when you pass a message to an actor (something of an asynchronous method call), you don't need to care what type of class the actor is or what type of messages it can process- at least at compile time:
There's an unassuming case denoted by the underscore character (_). This is the so-called catch-all case, the moral equivalent of Ruby's method_missing. As you can see, you can send any object to the actor, and all undefined classes can be handled dynamically by this placeholder case.
Of course, the spirit of Scala doesn't encourage handling patterns using the catch-all method. You can send messages to actors only through a typed scala.actors.Channel:
Once you use this Int-typed Channel, you can only send Int messages to it (unless there are any implicit conversions). For instance this would be caught by the compiler and fail with an error message:
The Channel is constructed using an actor as an argument. This actor can then match by the scala.actors.! class (yes, it's the name of the class) and check which messages have arrived through which channels.
This took awhile to discover, since the best documentation is in the source itself. If there are any other (and better) idioms for using scala.actors.Channel, I'd be happy to learn them!
I want to focus on another feature of Scala which allows for dynamic behavior. In concurrent programming with actors, the default handling involves pattern matching on the messages received. Pattern matching and actor concurrency have been borrowed from Erlang- a dynamic language by nature- and have been one of the key differentiating concepts compared to languages I've learned before.
When calling a method on a class, you have to know the method signature at compile-time. The types of the parameters you pass must be defined (even through an implicit type conversion). But when you pass a message to an actor (something of an asynchronous method call), you don't need to care what type of class the actor is or what type of messages it can process- at least at compile time:
import scala.actors._
import scala.actors.Actor._
val a = actor {
loop {
react {
case i: Int => println("Got an integer")
case o: AnyRef => println(o.getClass)
}
}
}
a ! 2
a ! 2.8
a ! "what is this?"
There's an unassuming case denoted by the underscore character (_). This is the so-called catch-all case, the moral equivalent of Ruby's method_missing. As you can see, you can send any object to the actor, and all undefined classes can be handled dynamically by this placeholder case.
Of course, the spirit of Scala doesn't encourage handling patterns using the catch-all method. You can send messages to actors only through a typed scala.actors.Channel:
import scala.actors._
import scala.actors.Actor._
val c = new Channel[Int](actor {
loop {
react {
case _ ! _ => println("test")
}
}
})
c ! 2
Once you use this Int-typed Channel, you can only send Int messages to it (unless there are any implicit conversions). For instance this would be caught by the compiler and fail with an error message:
c ! "2":10: error: type mismatch;
found : java.lang.String("2")
required: Int
c ! "2"
^
The Channel is constructed using an actor as an argument. This actor can then match by the scala.actors.! class (yes, it's the name of the class) and check which messages have arrived through which channels.
This took awhile to discover, since the best documentation is in the source itself. If there are any other (and better) idioms for using scala.actors.Channel, I'd be happy to learn them!
Wednesday, January 21, 2009
Apply search to following
There are still some things that don't make sense in search.twitter.com. For instance, it would be perfectly logical to search in just your followees' timelines. Why, TwitterSpy is already doing it. You could, of course, generate a huge query with "from:" and every possible person you're following, but search.twitter.com restricts the length of the query. Good luck if you have more than 10 friends. Uh, are there people like this after 1 month of using Twitter?
You could also write a console script, except that you don't want to type your password and reauthenticate once per follower. So doing it from the browser might be a better fit for a quick & dirty approach. This way your session would be reused.
Here's an educational bookmarklet. You can paste it in the URL bar of Firefox (didn't bother with other browsers) when you've opened twitter.com (won't work if window has a link open under another domain). And you can bookmark it- you thought it would be called a bookmarklet for nothing?
This will open one search window per followee- so don't try this if you have loads of'em. You will hit a limit in Firefox' default config, and you can increase it using the property:
Still, you will hit a hardware/hardcoded limit if you're not careful with how many people you're following. I told you this only has educational value, right?
You could also write a console script, except that you don't want to type your password and reauthenticate once per follower. So doing it from the browser might be a better fit for a quick & dirty approach. This way your session would be reused.
Here's an educational bookmarklet. You can paste it in the URL bar of Firefox (didn't bother with other browsers) when you've opened twitter.com (won't work if window has a link open under another domain). And you can bookmark it- you thought it would be called a bookmarklet for nothing?
javascript:query = prompt("Twitter search:");
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "http://twitter.com/statuses/friends.json");
xmlhttp.onload=function (){
list=eval(xmlhttp.responseText);
for (i=0; i<list.length; i++)
window.open("http://search.twitter.com/search?q="
+ query + "+from:" + list[i].screen_name)
};
xmlhttp.send(null)
This will open one search window per followee- so don't try this if you have loads of'em. You will hit a limit in Firefox' default config, and you can increase it using the property:
browser.tabs.maxOpenBeforeWarn
Still, you will hit a hardware/hardcoded limit if you're not careful with how many people you're following. I told you this only has educational value, right?
Friday, January 9, 2009
SliTaz: smaller than you think, more useful than you think
I started using SliTaz on a USB stick half an year ago just as a workaround to make my laptop with crashed hard-disk usable and try to rescue some of the disk contents. Eventually I ended up keeping the distro as my regular OS. There's a lot to like about it.
First of all, it's unbelievably small- 25 MB. Yes, in times of Vista, there are graphical OS's and software distros that small. Yes, they cheat, but with style- the OS is uncompressed using lzma, one of the most efficient compression algorithms. And it has an amazing amount of software, too- Openbox provides a slick GUI and even graphical effects like transparency, Geany is a powerful programmer's editor, Firefox is there, a mail client, torrent client, CD/DVD burner, PDF viewer, mplayer, even Sudoku. I like the choice of lightweight, but stylish programs. Finally, there's a system information applet, which gives you just what you need, and a Debian-like package manager- with a GUI.
The lack of an office suite is one of the key decisions in SliTaz. These days it is not uncommon to rely on Google Docs as a no-frills office suite. And getting rid of one of the bulkiest pieces of software has a lot of unexpected consequences. The OS is loaded in RAM, which makes it exceptionally fast and responsive, and eliminates a lot of reading/writing to the USB stick, which prolongs its life.
Why is SliTaz significant? Small is fashionable again due to the resurgent interest in mobile devices and ultra-mini PCs. Especially the Asus EEE PC and similar devices would be a perfect fit.
In this age of mobility it becomes increasingly important to have a stable environment so you can maximize your productivity. It becomes annoying if you have to set up a new PC every couple of weeks or switch between two different environments. By carrying around a USB stick with your OS, you have not only your documents and portable programs with you- the window manager is the same, you can use the familiar key bindings, the shortcuts and menus are the same. It feels like you're on the same machine.
Finally, you can regain this lost feeling that you understand what's going on with your OS. You keep it simple, you have just what you need and use everyday, and when you need a rarely used package, just install it from the repository. It won't be there when you reboot (unless you explicitly write RAM to the USB), and security-wise, I like the idea- it's as if you update your software for security vulnerabilities when you need it. It's also harder for malware to set itself up to start on reboot.
But why did I choose this specific distro? I used DamnSmallLinux for a while, and it was really cool for awhile, but showing its age- most of the software is hopelessly old- kernel 2.4 and... only last year switched to Firefox 2? No, that's not a typo- in times when Firefox 3 is stable, lagging one major version behind seems backwards. If you want to be small, get Firefox 3, use Google Docs, get rid of the office suite.
More importantly, in DSL there was a 50 MB core of applications, which could not be easily modified and customized. That makes it harder to upgrade as well. In SliTaz you can change everything- remove or upgrade to your heart's content. I can remove more programs, making SliTaz even slimmer.
The only nitpick I have is that a lot of the configuration utilities assume you have certain base programs. For instance, I wanted to have mrxvt instead of xterm, and get rid of xterm. Eventually wrote a small wrapper for xterm which invokes mrxvt.
First of all, it's unbelievably small- 25 MB. Yes, in times of Vista, there are graphical OS's and software distros that small. Yes, they cheat, but with style- the OS is uncompressed using lzma, one of the most efficient compression algorithms. And it has an amazing amount of software, too- Openbox provides a slick GUI and even graphical effects like transparency, Geany is a powerful programmer's editor, Firefox is there, a mail client, torrent client, CD/DVD burner, PDF viewer, mplayer, even Sudoku. I like the choice of lightweight, but stylish programs. Finally, there's a system information applet, which gives you just what you need, and a Debian-like package manager- with a GUI.
The lack of an office suite is one of the key decisions in SliTaz. These days it is not uncommon to rely on Google Docs as a no-frills office suite. And getting rid of one of the bulkiest pieces of software has a lot of unexpected consequences. The OS is loaded in RAM, which makes it exceptionally fast and responsive, and eliminates a lot of reading/writing to the USB stick, which prolongs its life.
Why is SliTaz significant? Small is fashionable again due to the resurgent interest in mobile devices and ultra-mini PCs. Especially the Asus EEE PC and similar devices would be a perfect fit.
In this age of mobility it becomes increasingly important to have a stable environment so you can maximize your productivity. It becomes annoying if you have to set up a new PC every couple of weeks or switch between two different environments. By carrying around a USB stick with your OS, you have not only your documents and portable programs with you- the window manager is the same, you can use the familiar key bindings, the shortcuts and menus are the same. It feels like you're on the same machine.
Finally, you can regain this lost feeling that you understand what's going on with your OS. You keep it simple, you have just what you need and use everyday, and when you need a rarely used package, just install it from the repository. It won't be there when you reboot (unless you explicitly write RAM to the USB), and security-wise, I like the idea- it's as if you update your software for security vulnerabilities when you need it. It's also harder for malware to set itself up to start on reboot.
But why did I choose this specific distro? I used DamnSmallLinux for a while, and it was really cool for awhile, but showing its age- most of the software is hopelessly old- kernel 2.4 and... only last year switched to Firefox 2? No, that's not a typo- in times when Firefox 3 is stable, lagging one major version behind seems backwards. If you want to be small, get Firefox 3, use Google Docs, get rid of the office suite.
More importantly, in DSL there was a 50 MB core of applications, which could not be easily modified and customized. That makes it harder to upgrade as well. In SliTaz you can change everything- remove or upgrade to your heart's content. I can remove more programs, making SliTaz even slimmer.
The only nitpick I have is that a lot of the configuration utilities assume you have certain base programs. For instance, I wanted to have mrxvt instead of xterm, and get rid of xterm. Eventually wrote a small wrapper for xterm which invokes mrxvt.
#!/bin/sh
while [ $# -gt 0 ]; do
if [ $1 == "-fa" ]; then
ARGS="$ARGS -fn"
elif [ $1 == "-fs" ]; then
shift
elif [ $1 == "-e" ]; then
ARGS="$ARGS -e sh -c '"
while [ $# -gt 0 ]; do
shift
ARGS="$ARGS $1"
done
ARGS="$ARGS'"
break
else
ARGS="$ARGS \"$1\""
fi
shift
done
eval exec mrxvt $ARGS
Subscribe to:
Posts (Atom)