GitHub, ‘git’ and the forking fallacy
GitHub is a pretty sweet system. One of the best aspects of web 2.0 has been the focus on simple, straightforward web app usability/utility. GitHub is a great example of some of web 2.0′s best attributes: a really interesting model coupled with straightforward usability and just a touch of AJAX. I never would have tried Git without GitHub.
Git is pretty interesting, too. Turns out that git is effective at encouraging social involvement in coding. The Turbinado project has been forked a few times… ‘Forked’?! Wait! That’s what happens when projects are in deep trouble, right?!
Maybe so, but not necessarily in git. Since git is a decentralized revision control system (like our beloved darcs), ‘forking’ is roughly equivalent to ‘checking out’. A ‘fork’ is a good thing, so Rails’ fork count (398) is heroic.
Thinking About Haskell*: You Know Lazy Evaluation; You Just Don’t Know It
[Post updated to reflect comments. ...too much late night typing...] Lazy evaluation is a very novel aspect of Haskell. Turns out that it’s not that difficult to think about.
A very common example of lazy-ish evaluation is ‘&&’ operators used in lots of languages (using C#):
if ( (obj != null) && (obj.someMethod() == somethingElse) ) { // do something } |
A HAML parser for Haskell
HAML‘s lovely. As I’ve been working with Turbinado, I’ve been having some issues with HSP. HSP is an insanely impressive piece of software, but its error messages can be a bit unclear. So I started playing around a bit with HAML. Got me wondering “How easy would be to write a HAML parser in Haskell?”
So I tried. Here’s a first-pass, to-be-updated [and somewhat incomplete] HAML parser for Haskell. Not all of the features are implemented, but it’s a start. It generates HTML bits suitable for compilation by GHC.
Input
f = content page = #content .left.column %h2 Welcome to our site! %p = print_information %p = print_inline .right.column = render [abba = ding, ding = abba] dinger |
Output
f = (stringToHtml "content") page = ((tag "div"![strAttr "id" "content"]) ((tag "div"![strAttr "class" "left column"]) ((tag "h2") (stringToHtml "Welcome to our site!") ) +++ ((tag "p") (stringToHtml print_information) ) +++ ((tag "p") (stringToHtml print_inline) ) +++ ((tag "div"![strAttr "class" "right column"]) (stringToHtml render) ) +++ ((tag "div"![strAttr "abba" "ding", strAttr "ding" "abba"]) (stringToHtml "dinger") ) ) ) |
le Code
module Main where import Text.ParserCombinators.Parsec import Text.ParserCombinators.Parsec.Language import Text.ParserCombinators.Parsec.Pos import qualified Text.ParserCombinators.Parsec.Token as T import Data.Char import Data.List import Data.Maybe import System.IO.Unsafe main = do s <- getContents case (parse mainParser "stdin" s) of Left err -> putStrLn "Error: " >> print err Right hs -> putStrLn hs -- Try to parse HAML, otherwise re-output raw lines mainParser = do whiteSpace ls <- many1 (hamlCode <|> tilEOL) return $ unlines ls -- -- * HAML lexer -- hamlLexer = T.makeTokenParser emptyDef whiteSpace= T.whiteSpace hamlLexer lexeme = T.lexeme hamlLexer symbol = T.symbol hamlLexer natural = T.natural hamlLexer parens = T.parens hamlLexer semi = T.semi hamlLexer squares = T.squares hamlLexer stringLiteral= T.stringLiteral hamlLexer identifier= T.identifier hamlLexer reserved = T.reserved hamlLexer reservedOp= T.reservedOp hamlLexer commaSep1 = T.commaSep1 hamlLexer -- -- * Main HAML parsers -- -- hamlCode is just many identifiers (e.g. 'func a b c' followed by '=' followed by a hamlBlock -- func a b c = %somehaml hamlCode = try ( do is <- many1 identifier symbol "=" currentPos <- getPosition x <- manyTill1 (lexeme $ hamlBlock) (notSameIndent currentPos) return $ (concat $ intersperse " " is) ++ " = \n" ++ (concat $ (intersperse (indent currentPos ++ "+++\n") $ filter (not . null) $ x)) ) -- A Block may start with some whitespace, then has a valid bit of data hamlBlock = do currentPos <- getPosition bs <- manyTill1 (pTag <|> pText) (notSameIndent currentPos) return $ intercalate (indent currentPos ++ "+++\n") bs pTag = do currentPos <- getPosition try (do t <- lexeme tagParser ts <- (isInline currentPos >> char '/' >> return []) <|> (hamlBlock) return $ intercalate "\n" $ filter (not . null) $ [ (indent currentPos) ++ "((" ++ (if (null ts) then "i" else "") ++ t ++ ")" , if null ts then [] else ts , (indent currentPos) ++ ")\n"] ) pText = lexeme stringParser notSameIndent p = (eof >> return []) <|> (do innerPos <- getPosition case (sourceColumn p) == (sourceColumn innerPos) of True -> pzero False -> return [] ) -- -- * Various little parsers -- tagParser :: CharParser () String tagParser = do t <- optionMaybe tagParser' i <- optionMaybe idParser c <- optionMaybe (many1 classParser) a <- optionMaybe attributesParser if (isJust t || isJust i || isJust c || isJust a) then do return $ "tag \"" ++ (fromMaybe "div" t) ++ "\"" ++ (if not (isJust i || isJust c || isJust a) then "" else concat $ [ "![" , intercalate ", " $ filter (not . null) [ (maybe "" (\i' -> "strAttr \"id\" \"" ++ i' ++ "\"") i) , (maybe "" (\c' -> "strAttr \"class\" \"" ++ (intercalate " " c') ++ "\"") c) , (maybe "" (\kv -> intercalate ", " $ map (\(k,v) -> "strAttr \"" ++ k ++ "\" \"" ++ v ++ "\"") kv) a) ] , "]"] ) else pzero tagParser' :: CharParser () String tagParser' = do char '%' many1 termChar idParser :: CharParser () String idParser = do char '#' many1 termChar classParser :: CharParser () String classParser = do char '.' many1 termChar attributesParser :: CharParser () [(String, String)] attributesParser = squares (commaSep1 attributeParser) attributeParser :: CharParser () (String, String) attributeParser = do k <- identifier symbol "=" cs <- many1 identifier return (k, intercalate " " cs) stringParser :: CharParser () String stringParser = do currentPos <- getPosition modifier <- optionMaybe (char '=' <|> char '-') whiteSpace c <- alphaNum cs<- tilEOL case modifier of Just '-' -> return $ (indent currentPos) ++ "-" ++ c:cs Just '=' -> return $ (indent currentPos) ++ "(stringToHtml " ++ c:cs ++ ")" Nothing -> return $ (indent currentPos) ++ "(stringToHtml \"" ++ c:cs ++ "\")" -- -- * Utility functions -- isInline p = do p2 <- getPosition case (sourceLine p ) == (sourceLine p2) of True -> return [] False -> pzero isSameIndent p1 p2 = (sourceColumn p1) == (sourceColumn p2) tilEOL = manyTill1 (noneOf "\n") eol eol = newline <|> (eof >> return '\n') termChar = satisfy (\c -> (isAlphaNum c) || (c `elem` termPunctuation) ) termPunctuation = "-_" indent p = take (sourceColumn (p) - 1) (repeat ' ') manyTill1 p e = do ms <- manyTill p e case (null ms) of True -> pzero False -> return ms |
Golly, but I wish that I’d cleaned up the code, but there it is in all of its raw, un-thought-through glory…
ANNOUNCE: Turbinado V0.2 “Still Ugly”
More progress on Turbinado. Look! A whole “+0.1″!
Progress continues on Turbinado. Turbinado can be found at: http://www.turbinado.org The source can be found at: http://github.com/alsonkemp/turbinado/tree/master (see the /App directory for the code for www.turbinado.org) New in V0.2: * A better "Environment" type (rather than using Dynamics) (-> a 50% speed boost? That's unpossible!); * A functional early version of an ORM for PostgreSQL (note: still needs to handle updates; hdbc-postgresql has a bug with sensing nullable columns); * A prettier website; * Licensing -> BSD. Expect V0.3 in the next week or so with: * More view helpers; * An ORM which handles INSERT/UPDATE...; * A little CMS built using the ORM. Future release: * Separate the website out from the framework. For now, they're evolving together, so live together.
ANNOUNCE: Turbinado V0.1
Posted to the Haskell mailing list:
I'd like to announce Turbinado, a very young and raw MVC web framework for Haskell. While the framework doesn't exactly copy Ruby on Rails, it certainly rhymes... It's very early days for Turbinado, but the framework is moving along nicely. There are still issues to be ironed out and architectural details to be decided, so help/contribution would be very much appreciated. Turbinado can be found at: <a rel="nofollow" href="http://www.turbinado.org/" target="_top">http://www.turbinado.org</a> The source can be found at: <a rel="nofollow" href="http://github.com/alsonkemp/turbinado/tree/master" target="_top">http://github.com/alsonkemp/turbinado/tree/master</a> (see the /App directory for the code for www.turbinado.org) Turbinado: * Provides a fast web server (based on HSP; see <a rel="nofollow" href="http://turbinado.org/Home/Performance%29;" target="_top">http://turbinado.org/Home/Performance);</a> * Provides a straightforward organization for your website (courtesy of Rails); * Uses simple HTML-like templating (courtesy of HSX); * Is easily extensible (courtesy of an Environment built out of _Map String Dynamic_, not the most type-safe of beasties; Help!); * Configurable routing (see Config/Routes.hs). Turbinado is currently lacking: * Documentation... * An easy install... * A database ORM based on HDBC (visibly incomplete and ugly in Turbinado/Database/ORM); * Many more HTML helpers; * Controllers for partials (lightweight "controls" ala ASP.NET); * Strong error reporting and handling; * Lots of functionality and plugins; * ... the favorite feature that you want to develop for Turbinado ...
Commentary: “RMS hates cloud computing; says you should too”
See here for a much discussed article on how RMS has said he hates cloud computing. I’m usually pretty on board with what RMS says (after I filter out the uber-geek-bias), but I’m on the fence about this issue.
My counter argument to RMS’s argument is: Damn!, but online services are just too handy to leave aside in order to maintain software development philosophical purity. Would you give up online e-mail, social networks, etc?
Besides, I love having fewer applications installed on my computer. I install and focus on the applications I really, really need and the other needs are served out there in the cloud.
RMS’s comment strikes me as purely academic with little consideration for What Really Works. I hope that online e-mail doesn’t go away, but I’d love to know how to make it more GNUy.
Further evidence that newspapers are in trouble…
BusinessWeek has more column inches on the pressing subject of the decline of newspapers… I wrote a bit about this last week, concluding that we should probably be more worried about what to do with all those big-brained reporters than about what to do with newspapers. Jon Fine focuses a bit on the topic of what to do with the reporters, but (probably courtesy of not looking very hard) I haven’t generally seen much discussion about what to do with the reporters, which are the big asset in the newspaper biz. And if we have had loads of discussion about how to fix the industry, then why are we still writing articles about the fact that the industry is dying?
Pretty Firefox: ‘Chrome Package’
I’m comfortable with Firefox. Stable, loads of developer tools, etc., but Google’s Chrome looks awful nice and is screen space efficient. Fortunately, some lovely fellow has made a Firefox add-on to make Firefox look like Chrome:
This add-on hides the Firefox toolbar (as opposed to Chromifox, which only provides colors and images).
Newspapers are dying… and?
Scoble opines on whether we can re-’kindle’ the newspaper industry:
“For example, the Times‘ software architect Derek Gottfrid showed me his Times Machine, an app that lets you search and read old copies of the paper. I’d pay for that.”
I’m sure that the Times Machine is super cool, but I’m a bit puzzled by these articles. I’m an old man (when measured in Internet years) and I have no idea why/how articles on how the newspaper industry can thrive/survive are relevant to me. Scoble has to fill column inches, but he’s kinda trod this ground before.
There are lots of good [old] articles out there that do a nice job of thinking through the newspapers’ dilemma.
Big Juicy Brains
Seems that what we really care about is how we keep all of those big-brained investigative journalists employed gathering news… And a Times Machine app doesn’t seem to be the solution to that problem. Blogging doesn’t feel right, either. Loads more fun certainly, but blogging usually serves up derivative works, not the original, investigative pieces of the big newspapers.
I’m not strong on the history of the Associated Press (or any of the other news production aggregators), but the Newspaper vs. Blog situation certainly points in the AP’s direction. Newspapers mix advertising and content origination (reporting) together in one big package. They also pay the AP for larger pieces of content that they’re happy to share with all of the other newspapers: international news; local stories which have high national relevance; random science stories; etc. Basically, anything that the paper wants to report on, but doesn’t want to hire the people to do the reporting. The Newspaper v. Blog situation suggests to me a complete decentralization of news production and news distribution, as opposed to the current situation in which newspapers mix the two.
So now we’re in the glorious future in which the AP produces news, legions of bloggers distribute the news and you subscribe to the feeds of 4-5 of your favorite bloggers so that you get both their news selections and their news interpretations. Lovely. So how does the AP make money? Naturally, the bloggers are all making money on advertising. So the AP should get a cut of the advertising revenues, but the AP can’t trust me to accurately report my advertising revenues.
The AP could create a partnership with a large advertising network or two and adopt a Publish-My-Content, you Publish-My-Ads business model. Adopt a rev share (60% to publisher, 40% to AP ?) and let the blogosphere go nuts with their content. (Probably need to hire a brigade or two of lawyers to make examples of a few people who’re abusing the system.) I’ll get the bloggers I love feeding me digested bits of AP content with the particular spin I love. I’m happy to be advertised to and the blogger will probably be happy to share revenue with the paper in order to get access to primo content.
Lots of wrinkles (e.g. how’s copyright affect this idea?) and implementation details (e.g. dude, you should totally use AJAX) left as an exercise to the reader, but it certainly feels like the newspaper industry is going to be decentralized. The sooner the big papers (read: NYTimes, LATimes, WSJ, etc) get out in front of the decentralization the better.
Throw Away Line For Those Writing Hand-Wringing Articles About The Death Of The Newspaper Industry
Won’t someone write an article expressing deep concern about the health of the printing press industry?
Pretty URLs – how-tos & guides
Since we run a membership-fee-based service, we’ve focused on providing security for our inside-the-walled-garden URLs. It’s been important to protect the content generated by our members for our members from being scooped out by unscrupulous operators. That said, we’re probably getting to the point at which we should pretty up our URLs, if only to enhance our member experience and ease with which members can find their content.
In searching around for guidance on how to go about switching to pretty URLs, I’ve found the following helpful:
- A Report on one company’s experience with switching to pretty URLs
- An analysis of that report here.
- A bunch of solid advice on how to think about pretty URLs.
- Nuts & bolts about setting up Apache for URL rewriting.