A Linux Turducken

2009-01-30 3:35 PM PST

~/.screenrc+a

source ~/.screenrc
escape ^aa # my .screenrc sets ^]]

~/bin/tiling3

#!/bin/bash
STY= SHELL=/bin/bash screen -c ~/.screenrc+a

~/bin/tiling2

#!/bin/bash
SHELL=~/bin/tiling3 dvtm

~/bin/tiling

#!/bin/bash
SHELL=~/bin/tiling2 screen -RaAd dvtm

Huh?

In case you didn't follow, that creates screen whose windows are dvtms, whose panels are screens (because dvtm doesn't keep a scrollback history). A poor attempt to emulate my tiled window manager in text mode. This is in no way good; I just figured it's been a while since I've poisoned anybody's brain.

Not Between Jobs

2008-05-03 12:26 AM PDT

Too lazy to right this in haiku. I caused confusion, it seems. I wasn't being euphemistic in saying I was "between jobs." I am now jobbed. Jobbing? Jobsome.

Undo Close Tab in Vim

2008-04-14 10:26 PM PST

Note: Whether the event below is BufUnload, BufHidden, BufDelete, or BufWipeout depends on what you have set for hidden and bufhidden:

augroup UndoClose
  au!
  au BufUnload * let s:last_buffer_hidden = expand("<afile>")
augroup END
command! UndoClose exec "tabe " . s:last_buffer_hidden

A Dream

2008-04-13 02:38 AM PST

So, we lived in a small house. My parents were going out somewhere. (Brendan was either out of the house, or going with them, I can't remember.) I needed to stay behind to take care of Brendan's pet hampster and miniature (like, 3" tall) human. So, I took their boxes out of the closet. The hampster didn't mind being kept in tight spaces, but I felt bad about it, so I opened its box up to let it get some fresh air, run around a bit. It responded a little; it seemed a bit happier. I did the same with the human – he bolted.

Now here's the sad part – the hampster really looked up to the miniature human, and so he bolted, too. I looked around the house for them, but they were nowhere to be found. When my parents got home, they were upset – for some reason (I don't remember), with the hampster gone, we might lose the house. (I dunno; maybe he held the title.) You see, we lived in a small three room shanty under a circus tent. A successful corporate mogul had already bought the circus, and just needed our land so he could, I don't know, do something nefarious.

There must be more stuff I'm not remembering, because at this point my mom is convinced that we need to go to the kinds of places where they audition child stars. Wait – maybe the pets weren't Brendan's, but my little sister's, and she ran after them, and got nabbed by the mogul? I dunno. Any way, we went to Disneyland – apparently, we lived nearby. (Oddly, it looked more like the inside of an Opera house. Note to director: the hallways were seal brown.) My mom looked and looked, and I followed (not knowing what she was looking for). She eventually stopped and asked where Magic Mountain was. A crowd of Disney employees gathered to help. They told her that Magic Mountain was on the east coast. Disappointed, she asked for any suggestions on where an aspiring child star might go.

Meanwhile, a rather attractive employee with a swimmer's build was eyeing me (and I reciprocated). When they were done helping my mom, they all left, but before doing so, she said, "You know, I find you rather attractive," rather nonchalantly, as if I should have expected it. I said, "Wait! I need some information. You can't just say that and walk away." She ripped of a blank check (like, template blank – no name, address, account number), and wrote on it, and told me, "I'll be at the Shamu's Sham, 9, 11, and 3." I looked down at the check she handed me, and it said "Shamoo's Sham \ 9, 11, 3." Thinking this was some club or something, I didn't understand, and asked "What? What day?" She just repeated herself and walked away.

I look forward to dreaming Part 2.

Employment: Haiku

2008-02-24 05:00 AM EST

Between employers
Find myself without a badge
I feel powerless.

Also, do we use the verb to wield with any object other than axe?

parse.y is teh suck

2007-11-15 12:00 PM EST

puts loop {
  "#{b}"
  ids.each do
  end
}

The above code doesn't parse in Ruby. I bolded various terms, above. If you remove any single one of those, it parses. Also, if you change the do...end to a {...} it parses.

You might say, “But that's sucky code,”, to which I have two responses:

  1. The fact that it's this odd combination of things is BAD. It hints that you can't take two (newline-terminated) valid Ruby expressions, catenate them, and expect the result to parse. What is this, Python?
  2. The actual code was, in fact, legitimate. I made it a contrivance in order to reduce its syntactic complexity. Replace loop with Benchmark.measure, "#{b}" with f = open("yada/#{yadayada}"), and ids with @processes, and, oh yeah, fill in the inner block.

I slayed the Postfix dragon

2007-10-03 11:29 PM EDT

I had a heck of a time getting Postfix set up on my Mac. Issues included:

In the process, I learned more about Postfix, sendmail, SMTP, tcpdump, and syslog than I really cared to. At least I can darcs send now. :)

Oh, also, I installed Netscape Communicator 4.8 at work today. I feel dirrrty.

Video game idea

2007-09-21 6:35 AM EDT

I just had an idea for a video game. Not as good as my never-to-be-implemented branching bemani-shmup, but with potential: Software Development Shop.

The gist:

Of course, this is just a seedling of ideas -- much more could be added. And the point values / distributions would have to be tweaked, of course. I think, however, it provides value in two ways:

  1. It's a serious game, that can help us learn about and improve our software practices. *cough*
  2. It's unique. I can only think of one other game that manages to provide this sort of non-stop, action-packed, heart-pounding tedium.

Update: It's pointed out to me (by my brother) that this has already been done before. Oops!

For what it's worth...

2007-07-11 1:46 AM EDT

EDT? EST? Whatever. I'm not done with the Haskell HTTP adventure, but I just wanted to write up a couple of brief stories of success with the Y combinator (aka the least fixpoint):

Implementation in Haskell, of course, is trivial: y f = f (y f). (I cheated and looked that up in Prelude.hs, though.) I'm still not to the point of understanding its lambda-calculus variant.

Clean-up time

2007-06-28 11:39 PM EST

Just posting some backlog today -- no time to do new work. This is from five days ago.

Okay, the code works, but it's uglier than French Stewart:

module Main where
import System.Environment
import Network.HTTP
import Network.URI
import Data.Maybe
import Text.XML.HaXml.Parse
import Text.XML.HaXml.Types
import Text.XML.HaXml.Xtract.Parse
import Text.XML.HaXml.Pretty

base_url = "http://webservices.amazon.com/onca/xml?" ++
           "Service=AWSECommerceService" ++
           "&SubscriptionId=your_id_here" ++
           "&Operation=ItemLookup" ++
           "&ResponseGroup=SalesRank,Small" ++
           "&ItemId="

amazonUrlFor isbn = base_url ++ isbn

-- Usage: resp <- simpleGet uriString
simpleGet :: String -> IO Response
simpleGet uriString =
  let Just uri = parseURI uriString in
  let req = Request uri GET [] "" in do
  result <- simpleHTTP req
  case result of
    Left _ -> error "Connection error!"
    Right resp -> return resp

-- Usage: resp <- fetchAmazonData isbn
-- fetchAmazonData "0977616630"
fetchAmazonData :: String -> IO String
fetchAmazonData isbn = do
  resp <- simpleGet $ amazonUrlFor isbn
  case rspCode resp of
    (2,0,0) -> return $ rspBody resp
    _ -> error "Bad status code!"

-- Usage (title, rank) = parseAmazonXml xml
parseAmazonXml :: String -> (String, String)
parseAmazonXml xml =
  let Document _ _ root _ = xmlParse "amazon xml" xml
      cont = CElem root
      rank = show $ content $ xtract "//SalesRank/-" cont !! 0
      title = show $ content $ xtract "//Title/-" cont !! 0 in
  (title, rank)

main = do
  args <- getArgs
  xml <- fetchAmazonData $ head args
  print $ parseAmazonXml xml

Okay, simple thing – remove the textual duplication in parseAmazonXml:

xtractFirstText path cont =
  show $ content $ xtract path cont !! 0

parseAmazonXml :: String -> (String, String)
parseAmazonXml xml =
  let Document _ _ root _ = xmlParse "amazon xml" xml
      cont = CElem root
      rank = xtractFirstText "//SalesRank/-" cont
      title = xtractFirstText "//Title/-" cont in
  (title, rank)

I can do a little "better":

xtractFirstText cont path =
  show $ content $ xtract path cont !! 0

parseAmazonXml :: String -> (String, String)
parseAmazonXml xml =
  let Document _ _ root _ = xmlParse "amazon xml" xml
      [title, rank] = map $ xtractFirstText (CElem root) $ ["//Title/-", "//SalesRank/-"] in
  (title, rank)

Though I don't like how there's no compiler guarantee that the map will return a list of two. And, well, ghci complains:

ranks.hs:48:60:
    Couldn't match expected type `Char' against inferred type `[Char]'
    In the expression: "//SalesRank/-"
    In the second argument of `($)', namely
        `["//SalesRank/-", "//Title/-"]'
    In the second argument of `($)', namely
        `(xtractFirstText (CElem root)) $ ["//SalesRank/-", "//Title/-"]'

I switch out my over-zealous use of ($) for parentheses, and it works. Don't forget, kids, ($) is right-associative. Next, I inline one of the lines in main:

main = do
  args <- getArgs
  fetchAmazonData $ head args >>= print . parseAmazonXml

Haskell complains again:

      Expected type: Char -> b
      Inferred type: String -> (String, String)
    In the second argument of `(.)', namely `parseAmazonXml'

Why is GHC wanting to pass a Char to parseAmazonXml? It should be getting the String from fetchAmazonData. Oh, yeah, binding rules again. See, args is a String, which is just an alias for [Char], and lists are also Monads, so the >>= works on them. It's actually a bit of a synonym for map:

*Main> [1,2,3] >>= return . (2*)
[2,4,6]

Anyways... let's finish out the inlining of main...

main = getArgs >>= fetchAmazonData . head >>= print . parseAmazonXml

Didn't really save a lot of characters there, but it was fun. Now, I really don't like those yucky case statements, but I don't know of a good alternative. I can, again, tell the compiler that I don't care about the error state:

simpleGet uriString = do
  let req = Request (fromJust $ parseURI uriString) GET [] ""
  result <- simpleHTTP req
  let Right resp = result
  return resp

I also went ahead and inlined the fromJust bit. Still, I don't like having to use pattern-matching just to extract the Response, and I'm betting Monads can help. I hit the internet.

Okay, well, I found no Monadism – best I found was the either function. Works like so:

*Main> let doStuff = either (2*) (3*)
*Main> doStuff $ Left 2
4
*Main> doStuff $ Right 2
6

Okay, now I just need to make the the first argument to either be a function that takes the left value and raises an exception. Well, this is cool:

*Main> :t error
error :: [Char] -> a

error takes a String and returns something. Anything. Whatever type you want it to be. Since it's never actually going to return, you can just put it in whatever context you want. It can even be a function:

*Main> error "bad" 1 2 3 4 5 6 7
*** Exception: bad

See? It compiled! Now I can turn the line:

  let Right resp = result

into:

  let resp = either (error "GET failed") id result

Doesn't seem like much of an improvement, but now this means I can say:

simpleGet uriString = do
  let req = Request (fromJust $ parseURI uriString) GET [] ""
  simpleHTTP req >>= either (error "GET failed") return

Okay, beddie bye. More cleanup to be done, and maybe some decent error-handling! Oh, and I read that good Haskell programmers specify the individual functions they're importing, not just the modules as a whole. Oh, well.

You know what? The little bit of reading I'm doing on the Haskell-Cafe archives is really pushing the small function paradigm, so let's extract a tiny one:

getRequest uriString =
  Request (fromJust $ parseURI uriString) GET [] ""

simpleGet uriString =
  simpleHTTP (getRequest uriString) >>= either (error "GET failed") return

Okay, this time for real. 'Night. Thinking about coupling and type signatures...

XPath

2007-06-25 9:10 PM EST

So, last time we got the XML from Amazon, though I realized I never actually demonstrated it. Here's a transcript:

*Main> fetchAmazonData "0977616630"
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><ItemLookupResponse ...><SalesRank>630</SalesRank><ItemAttributes><Author>Dave Thomas</Author><Author>David Hansson</Author><Author>Leon Breedt</Author><Author>Mike Clark</Author><Author>James Duncan Davidson</Author><Author>Justin Gehtland</Author><Author>Andreas Schwarz</Author><Manufacturer>Pragmatic Bookshelf</Manufacturer><ProductGroup>Book</ProductGroup><Title>Agile Web Development with Rails (Pragmatic Programmers)</Title></ItemAttributes></Item></Items></ItemLookupResponse>"

Okay, so we've pulled back the XML from Amazon. Now let's get busy with the parsing. Google turned up two top contenders: HaXml and and HXT. I started out looking at HXT, 'cause:

Well, it turns out I'm not yet qualified to use arrows. Maybe it's just my fault for trying to reproduce everything in the REPL before I code it. On the other hand, HaXml deals simply in functions – gimme a String, and I'll return a Document. Here's a simple HaXML example, using the XPath-like:

Prelude> let xml = Text.XML.HaXml.Parse.xmlParse "filename" "<doc><bob><apple/><banana/></bob><french/></doc>"
Loading package haskell98 ... linking ... done.
Loading package HaXml-1.13.2 ... linking ... done.
Prelude> let Text.XML.HaXml.Types.Document prolog entities root misc = xml
Prelude> let content = Text.XML.HaXml.Types.CElem root
Prelude> map Text.XML.HaXml.Pretty.content $ Text.XML.HaXml.Xtract.Parse.xtract "//bob/*[1]" content
[<banana/>]

I'm not sure why I have to go through all that trouble to extract the root element and wrap it in a Content – I'm sure there's a saner way, but I'm just happy I got something.

Sad. It seems all the examples on the 'net use Text.XML.HaXml.Wrappers.processXmlWith, which looks like it was optimized for plugging straight into main and making stupid commandline utilities. This thing takes a CFilter and then returns an IO that magically does all the work. Okay, well, on with the show. When I'm done, I'll look into HXT again.

Well, this does what I want:

-- Usage (title, rank) = parseAmazonXml xml
parseAmazonXml :: String -> (String, String)
parseAmazonXml xml =
  let Document _ _ root _ = xmlParse "amazon xml" xml
      cont = CElem root
      rank = show $ content $ xtract "//SalesRank/-" cont !! 0
      title = show $ content $ xtract "//Title/-" cont !! 0 in
  (title, rank)

Note that xtract returns a list, so I !!0 to get the first one (this will fail miserably if none is found). Tying it all together:

main = do
  args <- getArgs
  xml <- fetchAmazonData $ head args
  print $ parseAmazonXml xml

And rename the module to Main to runhaskell will work:

$ runhaskell ranks.hs 0977616630
("Agile Web Development with Rails (Pragmatic Programmers)","705")

Et voilà! Oh yeah, I skipped the block of import statements:

module Main where
import System.Environment
import Network.HTTP
import Network.URI
import Data.Maybe
import Text.XML.HaXml.Parse
import Text.XML.HaXml.Types
import Text.XML.HaXml.Xtract.Parse
import Text.XML.HaXml.Pretty

Boo, import statements. How dare you make me remember all your names. Next time, I start trying to clean this up. Who knows what lies ahead?

GETting some Content

2007-06-24 9:45 PM EST

This transcript is barely edited. I'm trying to map my experiences with a simple task. It may turn out that this is completely unreadable, so, you know, at your own peril and whatnot. If, at the end of it, I get something decent, I may correlate it into some final, useful form. Oh, and no promise that I maintain this regularly, or finish, or anything. First, notes from last Wednesday, the 20th:

First, I write a Haskell module – ranks in ranks.hs:

module ranks is

fetchTitleAndRank isbn = ("Agile Web Development with Rails", 1234)

A couple of things to note:

I take the above and run it via runhaskell ranks.hs – this should produce no output, because I haven't defined a main function on a Main module. Instead, it gives me parse errors. I run it with runhugs ranks.hs, since Hugs gives better error messages.

I discover that ranks should be uppercase, and that is doesn't exist. It has a problem with the function definition, but I can't figure it out from the vague error message. I go check out the docs at haskell.org to see what I did wrong. I'm a little bit insane in that I jump straight to the Haskell 98 Report (pretty much the language spec).

Aha! module Ranks where. Sigh. Fixing that and running with runhaskell I get Failed to load interface for `Main'. Well, yeah. For now I'll just go into interactive mode. hugs or ghci? I choose ghci for now.

$ ghci ranks.hs
   ___         ___ _
  / _ \ /\  /\/ __(_)
 / /_\// /_/ / /  | |      GHC Interactive, version 6.6.1, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base ... linking ... done.
[1 of 1] Compiling Ranks            ( ranks.hs, interpreted )
Ok, modules loaded: Ranks.
*Main> :t fetchTitleAndRank 
fetchTitleAndRank :: (Num t1) => t -> ([Char], t1)
*Main> 
I asked the interpreter to print the type of fetchTitleAndRank for me. Like ML, Haskell is a statically-typed programming language, but it doesn't put the burden on you – it infers the type. The above says, "Where t1 is some Number-type, this function takes some value of type t (I don't care what), and retuns a tuple containing a String and value of type t1." (Num is a type class. It's too soon for you, my young one.)

Now that we know about typing, I can explain currying just a smidge. Let's look at the type of a function that takes two parameters:

*Ranks> :t (+)
(+) :: Num a => a -> a -> a

(That is, the infix plus operator. I wrap it in parentheses when I want to refer to it, rather than have it actually add things.) Notice that the syntax for its type isn't a, a -> a, but a -> a -> a. This symmetry has meaning. The above says "This function takes a number and returns a function that takes a number and returns a number." Seriously. Example:

Prelude> :t (2 +)
(2 +) :: (Num t) => t -> t
Prelude> let f = (2 +)
Prelude> f 5
7

That's built in to every function you ever use or write. That's why functions have fixed arity. (You may say, "How pointless!" but others will respond, "No, points-free!" har har...) Okay, enough jibbajabba, let's write the URL generator:

base_url = "http://webservices.amazon.com/onca/xml?" ++
           "Service=AWSECommerceService" ++
           "&SubscriptionId=your_id_here" ++
           "&Operation=ItemLookup" ++
           "&ResponseGroup=SalesRank,Small" ++
           "&ItemId="

amazonUrlFor isbn = base_url ++ isbn

(base_url can't be uppercase – that's reserved for modules, types, constructors, type classes and the like.) Note there's no difference between assigning a value to a variable and assigning a definition to a function. How cool!

Okay, I want HTTP access, and that's not built into the Haskell Prelude. I click 'Standard Libraries' from the Haskell home page and am taken to a URL with 'ghc' in it. Odd... are these compiler-specific? Well, paging down takes me to a section on compatibility notes. Okay.

I find a Network library, but nothing that has knowledge of HTTP. Okay, time to trawl the internet... Ha! Google takes me back to haskell.org. I install it using cabal-install, Haskell's alpha package manager.

Network.HTTP.simpleHTTP takes a Request and returns an IO (Result Response). I'll figure out how to get the XML out of the IO (Result Response) later – now let's figure out how to build a Request. Looking at the spec says a Request is made up of a URI, RequestMethod, list of Headers, and a String for the body.

I click the URI hyperlink to check out the docs for Network.URI and discover the parseURI function. A few minutes of playing around gets me:

*Ranks> :mo + Network.URI
*Ranks Network.URI> parseURI "http://hello"
Just http://hello
*Ranks Network.URI> parseURI $ amazonUrlFor "some_isbn"
Just http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&...&ItemId=some_isbn
(The $ operator does absolutely nothing. Literally, its definition is f $ x = f x, courtesy of Haskell's pattern matching. However, its ultra-low precedence changes the associativity of the above expression, serving as a substitute for parentheses. Without it, Haskell would've passed two arguments to parseURI, the first being the amazonUrlFor function.) RequestMethod is easy – GET (no arguments) is one of its constructors. I'll just pass an empty list of Headers and an empty body, so let's put this together:
fetchTitleAndRank :: String -> IO (Maybe Response)
fetchTitleAndRank isbn = do
  let parse = parseURI $ amazonUrlFor isbn
  case parse of
    Nothing -> return Nothing
    Just uri -> do
      let req = Request uri GET [] ""
      result <- simpleHTTP req
      case result of
        Left _ -> return Nothing
        Right resp ->
          return $ Just resp

I'm leaving out, here, the efforts I went through to get the above correct. That's not because it's okay to gloss over that – simply because I wouldn't know how to transcribe it. Mostly, it's just me fumbling over syntax. I'll also add that the type signature isn't necessary, but it often makes the errors make more sense.

And, woah, that's ugly. I can shorten the code quite a bit by simply raising an exception when the code doesn't follow the happy path:

fetchTitleAndRank :: String -> IO Response
fetchTitleAndRank isbn = do
  let Just uri = parseURI $ amazonUrlFor isbn
  let req = Request uri GET [] ""
  result <- simpleHTTP req
  case result of
    Left _ -> error "Connection error!"
    Right resp -> return resp
Filling that out:
-- Usage: resp <- simpleGet uriString
simpleGet :: String -> IO Response
simpleGet uriString =
  let Just uri = parseURI uriString in
  let req = Request uri GET [] "" in do
  result <- simpleHTTP req
  case result of
    Left _ -> error "Connection error!"
    Right resp -> return resp

-- Usage: resp <- fetchAmazonData isbn
-- fetchAmazonData "0977616630"
fetchAmazonData :: String -> IO String
fetchAmazonData isbn = do
  resp <- simpleGet $ amazonUrlFor isbn
  case rspCode resp of
    (2,0,0) -> return $ rspBody resp
    _ -> error "Bad status code!"

I don't like the fact that I took out all the error checking code, or that it takes two functions and eighteen lines to GET, but it's late. Tune in next time, for an exciting romp through the world of XML parsing, and more fumbling.

Installing Haskelly Stuff

2007-06-21 1:50 AM EST

Won is South Korean currency.

I was going to write a Haskell translation of Dave Thomas's A First Erlang Program, but then I got bogged down in two things:

Here's what I do (glossing over all the pain I encountered figuring these simple steps out, owing mostly to some really stupid errors):

port install ghc darcs <wait>
darcs get --partial http://darcs.haskell.org/packages/Cabal
pushd Cabal; make && sudo make install; popd
darcs get --partial http://darcs.haskell.org/packages/cabal-install/
pushd cabal-install; runghc Setup.lhs configure && runghc Setup.lhs build && sudo runghc Setup.lhs install; popd
cabal-install update
cabal-install install <name-of-library>

You can see the list of libs over at Hackage.

Take it from me, there's nothing like a job well done, except the quiet enveloping darkness of the bottom of a bottle of Jim Beam after a job done any way at all.
– Stephen Colbert

a week late, a won short

2007-05-29 7:56 AM EST

I wrote this last week, when it was current. I think it's still relevant, however:

Chris and I work on a database-backed Rails app that is a portal for a shload of documents. Chris just propositioned to me the idea of moving our document store – the webapp's raison d'ĂȘtre– out of the RDBMS and into an alternate datastore, such as an XML DB or a plain, flat file access.

I yelled at him for suggesting that we add XML to our system, and then agreed with the idea of looking at alternate datastores for those things whose access profile differs greatly from that to which relational databases are optimized. That is to say, our app consists primarily of a search engine and various 'latest' feeds. Why not use a bona-fide IR engine for the former and memcached (or something even simpler) for the latter? Heck, I'm even open to investigating BDB for the rest of our app, which pretty much doesn't rely on any relational algebra besides PK and FK lookups. (I wasn't nearly this lucid at 9pm in the parking lot of FGM, but these were the concepts vaguely forming in my head.)

Chris and I used to be the only members of the delivery team, in Scrum parlance. We'd bring up new ideas all the time, and in varying degrees of wacktitude, and we'd discuss the pros and cons. We'd disagree on some things, but having similar philosophies and backgrounds, we could speak the same language. (For example, we are both software development generalists, we are both neophiles, and we are both strong proponents of agile practices.) The result was an app that was always changing for the better.

Since then, the team has grown. It is decorated with many new faces, with varying philosophies and backgrounds. If Chris had brought that idea up in a larger setting, the conversation would have gone something like this:

Chris: I really think we should look into moving our documents out of the database.
DBA: What, are you a fucking idiot?

If Chris pursued the line of thought any further, it would result in massive shock waves throughout the team. Specialists worry that the technology decision will snuff them out of a job, or worse, require them to learn. Political-minded folks anger at the idea of pursuing a concept that causes any bit of stir. The change-averse would be frustrated that such a decision would introduce unnecessary risk. The result is that it is impossible to reach consensus on the minorest of things, and that technical problem-solving in the team is a blend of skunkworks vigilanteism and absolute deadlock.

Oh, and it's ridiculously demotivating.

</rant>