How to build stuff using autoconf and automake (configure.in, configure.ac, Makefile.am)

Here’s the deal: building stuff that uses autotools isn’t just ./configure && make && make install. I keep forgetting how to use the build system and there’s no page on the net that explains it clearly.

How to

It seems fairly easy:

aclocal
autoconf
autoreconf -vi
automake
./configure
make

Of course, the only source for the above invocation was threads of people failing to build software that ended up experimentally recreating the steps necessary. I may have missed something, so make sure to add info in the comments.

When done with the above list, you can do make install — but really, who does that nowadays? Instead use checkinstall to catch the files being installed into a package that can later be uninstalled cleanly, reinstalled, and so on.

Don’t forget the packages

If you’re getting errors such as:

error: possibly undefined macro: AC_PROG_LIBTOOL

then you might have to install the libtool package.

Seriously, would it hurt GNU to include info for users on how to use autotools? The autoconf manpage doesn’t even mention the aclocal tool. Come on guys, get your heads out of your asses, start being user friendly. Having to fight your computer is so trite.

Developer Mailing List, a paraphrase from Liar's Poker

On braver days you cruised the mailing list to find a senior developer who would take you under his wing, a mentor, better known to us as a rabbi. You also went to the mailing list to learn. Your first impulse was to step into the fray, select a likely teacher, and present yourself for instruction. Unfortunately it wasn’t so easy. First, a newbie by definition had nothing of merit to say. And, second, the mailing list was a minefield of large men on short fuses just waiting to explode if you so much as breathed in their direction. You didn’t just walk up and say hello. Actually that’s not fair. Many, many developers were instinctively polite, and if you said hello they’d just ignore you. But if you happened to step on a mine, then the conversation went something like this:

Me: Hello.
Developer: What fucking rock did you crawl out from under? Hey, Joe, hey, Bob, check out this guy’s suspenders.
Me (reddening): I just wanted to ask you a couple of questions.
Joe: Who the fuck does he think he is?
Developer: Joe, let’s give this guy a little test! How do you declare a program entry point in C?
Me: int main(void).
Developer: Terrific. You get an A. Now I gotta work.
Me: When would you have some time―
Developer: What the fuck do you think this is, a charity? I’m busy.
Me: Can I help in any way?
Developer: Get me a burger. With ketchup.

So I watched my step. There were a million little rules to obey; I knew none of them. Package maintainers, developers, and managers swarmed over the floor, and at first I could not tell them apart. Sure, I knew the basic differences. Package maintainers talked to distro maintainers, developers made code, and managers smoked cigars. But other than that I was lost.

As a newbie, a plebe, a young man lying under all that whale shit, I did what every newbie did: I sidled up to some busy bug without saying a word and became the Invisible Man.

That it was perfectly humiliating was, of course, precisely the point. Sometimes I’d wait for a month before my patch was formally acknowledged; other times, a few hours. Even that seemed like forever. Who is watching me in my current debased condition? I’d wonder. Will I ever recover from such total neglect? Will someone please notice that the Invisible Man has arrived? The contrast between me lurking quietly and the developers’s frenetic debate made the scene particularly unbearable. It underlined my uselessness. But once I’d sidled up, it was difficult to leave without first being officially recognized. To leave was to admit defeat in this peculiar ritual of making myself known.

All the senior people, from the merge master down, stalked the mailing list. It was not a normal project, in which newbies were smiled benevolently upon by middle-aged senior developers because they represented the future of the team. Here, newbies were freeloaders, guilty until proven innocent. With this rap on your head, you were not particularly eager to meet the boss. Sadly you had no choice. The boss was everywhere. He saw you in your red suspenders with gold dollar signs and knew instantly who you were. A cost center.

Even if you shed your red suspenders and adopted protective coloration, you were easily identifiable as a newbie. Newbies were impossibly out of step with the rhythm of the place.

A list of sources of university-level online courses

I have recently been looking at some courses online, and thought, hey, there’s gotta be a list somewhere, right? Apparently, there isn’t. I scoured Hacker News, and I tried searching Reddit (though its topology is so vast it’s daunting). Nothing good turned up.

What this is

The following list is ordered roughly by amount and quality of courses. You first get aggregators, then sites of individual universities, then sites of smaller universities, and finally single courses of interest that I stumbled across. The single courses being here is not the result of some cream of the crop selection, I just came across them while searching for bigger results.

Anyway, I hope you guys have fun using and abusing this resource. I decided to use URLs only, because they’re usually easier to identify than many of the stupid titles the sites on this list have.

Please make sure to comment and tell me if you know any good resources I have missed. Also hang out on irc.freenode.net #codez (Freenode webchat) which is a social place for students, people interested in technology, etc. I hope to get some chat going there related to the courses, this way we can figure out what’s best.

The list

Education revised

I am very happy that it’s possible to reach such amazing content on the web. I remember when I first heard of MIT OCW — I immediately started browsing, unfortunately both my computer and internet connection at the time sucked so much I was unable to use any of it. Nowadays I can wake up and, while getting out of my bed, eating breakfast, packing for the gym, and grooming, all the time watch Susskind talk about general relativity. I can really immerse myself in study if I want to, and this is great. Studying would have been so much easier had I had this resource in the past! I was a bit disappointed that I couldn’t find any good resources with video lecutres in Russian — there are many legendary lectures in mathematics and physics, for example, being given at russian universities. Hopefully time will change that.

Posterous fucking sucks it down yet again.

I have had to remove the http prefix from youtube links, otherwise Posterous would spill its fucking guts trying incorrectly to make a video player out of them. I didn’t get any reply from the help email address, as usual, in contrast to how Posterous firm themselves as user friendly. I only figured out later that I could put in an url without the http prefix and it would get mutilated into an anchor link without getting mutilated by the player misfeature. You suck, Posterous. Any suggestions for a better blog host? I just need markdown and image hosting.. although the image hosting in Posterous is useless, too.

How to fix Skype hogging audio hardware in GNU/Linux Ubuntu 10.04 LTS

TL; DR

Quick fix: run

aoss skype

instead of

skype

and you’re fine.

Skype prefers crappy PulseAudio

OK guys, whether PulseAudio is good or not, the way it works under Ubuntu means it’s a crappy choice. PA Might work perfectly well under Arch or Debian — but my problems were under GNU/Linux Ubuntu 10.04.

The problem with PulseAudio

Currently, I have several audio subsystems here. Having Alsa and PulseAudio at once isn’t that uncommon. However, on my computer if I use PulseAudio then no Alsa application can access the sound outputs or inputs. I haven’t bothered fixing it because with most apps I can fix it by changing the output being used; Skype defaults to PulseAudio if it’s present, ignoring Alsa. Skype was the last holdout, and it was annoying me. All the guides I could find mentioned uninstalling PulseAudio or heavyweight configuration changes to Alsa, but one of them defined a configuration which had PulseAudio and Alsa at the same time; they launched Skype with aoss skype and it worked. The command line included some other crud, but it tipped me off on aoss.

Apparently Alsa supplies the tool aoss which wraps any binary and makes it use Alsa. So, after trying: success!

Don’t use the command line every time

You can edit the Ubuntu menu entry by right-clicking on Applications in the Gnome Panel, and choosing Edit menus. There, under Internet, you can click on the Skype entry (make sure not to uncheck it) and then click the Properties button to the right. This should open a window called Launcher Properties in which you can change the Command field to aoss skype. Now you can launch Skype through the menu, and it works.

I’m not too big on installing corporate malware, but Skype is one of the few things you can’t live without nowadays if you interface with normal humans.

There should be a free fully-p2p solution to this problem that just works, but there isn’t anything prominent yet. I’m not so certain it’d beat Skype unless it had very serious corporation backing.

Steve Jobs

I have today figured out the most amazing thing ever. Head onto the Apple page. Yeah, Steve Jobs is dead, but it gets even better than that. Zoom his face so that it takes up your whole monitor. In case his face is gone, that same photo is used for his new biography (it’s black and white and he’s sorta holding his chin on that one). Now take a piece of paper, book, or an iPad or toilet seat, and cover the left half of his face. You get HappyFriendlySteve. Now do that with the other side. You get AngryEvilSteve. How insane is that?!?!?!!???

It’s true: Apple founder Steve Jobs is his own siamese evil twin, and used this fact to generate the ARDF (Apple Reality Distortion Field). I always knew that guy was two-faced.

Simple tutorial on Twisted Deferreds and how to implement them in Haskell

I have been looking at Twisted’s Deferred class lately and considered how I would do it in Haskell.

Intro to Deferred

If you don’t know Twisted or the Deferred class, here’s all you need to know:

  1. A Deferred is a collection of functions that get called once an event happens (user clicks a mouse, socket gets data, download finishes, etc). The Deferred is a dispatcher. In a normal program we registered a Deferred with an event and forget about it, because that Deferred contains all the logic that will handle the event.

  2. The collection of functions in a Deferred is a list of pairs that looks like this: [(callback, errback)].

  3. When the Deferred gets data from the event, the event is either a result or a failure. Depending on what the event data is, you either call the first callback or the first errback.

  4. Pairs in the list are processed in sequence. You take a pair, call an errback or callback depending on whether you’re holding a result or failure in your hand, and you put that value into the desired function. The other element of the pair gets ignored forever.

  5. Each callback and errback is just a normal function which again returns a result or an error.

  6. You take the next pair and call the callback or errback depending on what your previous pair has returned.

  7. Repeat until you run out of pairs. If you run out of pairs and the last result is an error then you print the contents of the error into the Twisted server’s error log.

  8. You add pairs with the addCallbacks method of Deferred objects.

Very simple. I decided to write it down because all the material I could find on Twisted’s Deferred class was confusing. It talked about exceptions and all that sort of stuff, which is a great complication. Here are some things to remember when running into that:

  1. Deferred is a way to express try … except but no one cares and don’t think about it this way. It is a bad analogy and includes a lot of verbose stupid syntax that makes it difficult to visualize what’s going on, even though it works.

  2. When you take your Deferred called d and do d.addCallback© (notice the missing s), then you just call addCallbacks(callback=c, errback=id) where id is a function that simply returns its argument (also called identity, noop, passthrough, etc).

  3. Similarly, d.addErrback(e) is just d.addCallbacks(callback=id, errback=e).

Similarities to bind

If you use Haskell you have probably noticed that this is very similar to >>=, called the bind operator. A monad in haskell is like a new language with usually about three of the same simple functions defined in different ways. Every monad in Haskell has its own definition of >>= and for what we want it works kinda like this:

f >>= g = take result of f and plug it into g

I have had an idea Deferred could be implemented as a monad. At first I thought that this would be similar to the Maybe monad, because you can skip things. Your function can evaluate to Just x which is a “success” output or it can evaluate to Nothing which is a “failure”. That is, if you have a chain like this:

f1 >>= f2 >>= f3 >>= f4 >>= f5 >>= f6

and f1 and f2 return Just 1 and Just 72, but f3 returns Nothing, then f4 and further functions in the chain will not be executed at all.

However the Deferred dispatcher allows you to resume processing after an error has been recovered from in the errback. This means that we basically have two chains:

f1    f2    f3    f4    f5    f6
e1    e2    e3    e4    e5    e6

Returning an error seems to force you into the bottom row, returning a result forces you into the upper row.

Community to the rescue

I went to the #haskell channel on Freenode and asked around if anyone ever used Twisted. The user wavewave has suggested writing a monad based off Either at first, but we decided that’s not it. Then he wrote it trying to use something like Cont, the CPS monad, but it simplified and it turned out to be exactly just like the State monad. So then he rewrote it all again using State and this is what he got:

import Control.Monad.State

data Result = Success | Failure                                               
             deriving (Show,Eq)                                               

type Twisted a = State Result a

callback1 x 
  | x > 0     = do put Success 
                   return (x+1)
  | otherwise = do put Failure 
                   return (x-1)

errback1 x 
  | x > 2     = do put Success
                   return (x+1)
  | otherwise = do put Failure

callback2 x 
  | x > 3     = do put Success 
                   return (2*x) 
  | otherwise = do put Failure 
                   return (x+3) 
                   return (x-1)
errback2 x
  | x < 1     = do put Success
                   return (2*x)
  | otherwise = do put Failure
                   return x 

action1 x = do 
  s <- get    
  case s of 
    Success -> callback1 x
    Failure -> errback1 x 

action2 x = do 
  s <- get 
  case s of 
    Success -> callback2 x 
    Failure -> errback2 x 


run t = runState t Success

main = do 
  putStrLn "test twisted"                                                     
  let r = run $ do x <- return (-3)   
                   y <- action1 x                                             
                   action2 y                                                  
  print r

It was great and worked. The idea was that our program passes around an implicit boolean parameter which tells us either whether it’s in the state of recovering from an error or the state of normal operation.

I told wavewave that it could use a minor cosmeting improvement because the syntax is very verbose:

wavewave: i think this syntax is still verbose. i think it
    is probably a better idea to have "action" abstracted
 action f e = ..
 cheater : sure.. you can define a convenience function.
 also, inside it i would assume success unless a failure is
    specifically returned. if an errback is being called it is
    assumed the parameter with which it is being called is the error
 cheater : That's possible.
 and if a callback is being called then it is assumed the
    parameter with which it is being called is a success. let me try
    to mark up the code a bit and see what happens

One small step for Man

The resulting code was a bit smaller. I have also changed the callbacks and errbacks in order to illustrate the control flow a bit better.

module Main where
import Control.Monad.State

data Result = Success | Failure
             deriving (Show,Eq)

type Deferred a = State Result a

callback1 x
    | x < 0     = return (x + 10)
    | otherwise = do
        put Failure
        return (x - 1)

errback1 x
    | x > 2     = return (x + 1)
    | otherwise = do
        put Failure
        return (x - 1)

callback2 x
    | x > 0     = return (3 * x)
    | otherwise = do
        put Failure
        return (x + 3)

errback2 x
    | x < 7     = return (2 * x)
    | otherwise = do
        put Failure
        return (x + 7)

action callback errback arg = do
-- just like Twisted's addCallbacks
    s <- get
    put Success
    case s of
        Success -> callback arg
        Failure -> errback arg

run t = runState t Success

main2 = do
  putStrLn "test deferred"
  let r = run $ do x <- return (-3)
                   y <- action callback1 errback1 x
                   action callback2 errback2 y
  print r

main = do
-- the main program
    (putStrLn "test deferred")
    let a = (action callback1 errback1)
    let b = (action callback2 errback2)

    let x = run $ (return (-1)) >>= a >>= b
    -- Passing -1 to a is a Success with value 9; b's callback
    -- accepts it and emits a Success with value 3 * 9 = 27.
    print x

    let y = run $ (return (3)) >>= a >>= b
    -- Passing 3 to a is a Failure with value 2; b's errback
    -- accepts it and emits a Success with value 2 * 2 = 4.
    print y

    let z = run $ (return (9)) >>= a >>= b
    -- Passing 9 to a is a Failure with value 8. b's errback
    -- accepts it and emits a Failure with value 8 + 7 = 15.
    print z

The output of the program is:

test deferred
(27,Success)
(4,Success)
(15,Failure)

You compile the program by putting the source in a file called Main.hs and running ghc Main.hs.

Benefits

There are many benefits:

  1. Save chains if Deferreds and reuse them for different events
  2. Be sure your errbacks and callbacks won’t interfere with eachother due to side-effects
  3. Use lightweight threading — GHC’s threads are on the order of single kilobytes (GHC is the most popular Haskell compiler written by geniuses like Simon Peyton-Jones by use of alien technology)
  4. Extend this pattern to do other things
  5. Pass values around from errback to errback if you want, or extend this monad to prevent this if you want. It’s all in your hands!
  6. Use a language which is much simpler to program and reason about than Python

Small compilation problem you won’t encounter

This is not something you would run into normally: I have had the monads-tf and mtl packages installed, so GHC was complaining that Control.State.Monad was ambiguous — this module was defined in both. Apparently the mtl version is currently more popular than the monads-tf one. The mtl package uses functional dependencies, whereas monads-tf uses type families which are by some seen as a superior way to express the concepts in it. Additionally the monads-tf version breaks compatibilty with old code. To let GHC know which version I want (I used mtl this time), I used the -ignore-package monads-tf command line option for ghc when compiling. You can instead ignore mtl and everything works in the same way.

Using Haskell to easily build interactive text-mode applications with HSCurses, NCurses or Vty

I have recently been musing on writing some more involved programs in Haskell that would require a curses(3) based UI, and decided to check out the available libraries. There’s a deficit in documentation on how to do stuff with them, so I thought I’d share.

Available libraries

There are three notable packages for Haskell:

  1. hscurses which is fairly established and works well. It’s somewhat low-level at times, but then that makes it very similar to how you use curses in C-like languages
  2. ncurses — newer than hscurses and somewhat higher-level. One downside for me was not being able to run it in interpreted mode, it displayed garbage unless I compiled my program and ran the binary
  3. Vty, not as new as Haskell’s ncurses; a complete rewrite in Haskell, without using the ncurses(3) family of libraries. Good for things where curses is not available; does not work on Windows.

HSCurses

Let’s take a look at HSCurses first. There’s a pretty good tutorial for HSCurses in Japanese. You can also get an example package called hscurses-fish-ex which displays a fish tank and is a sort of tech demo; you can find its source under ~/.cabal/packages/hackage.haskell.org/hscurses-fish-ex/.

You can install the package via cabal install hscurses and the tutorial via cabal install hscurses-fish-ex.

If you untar the source for hscurses-fish-ex then you will find everything is in one big file that can be executed as a script with ./hscurses-fish-ex.hs. Good for experimentation; you can edit the file in Vim and after writing do :!./% to execute it. It’s a bit too complicated for a first approach, though. Let’s go back to itchyny’s excellent guide.

You can also learn some tricks from Michael Forster’s post about HSCurses on Haskell Cafe from 2010.

Hello, HSCurses

On his “Programming Mumblings” (“プログラムモグモグ”) blog, itchyny starts with something similar to this:

#!/usr/bin/env runhaskell
import qualified UI.HSCurses.Curses as HSCurses

main :: IO ()
main = do
    HSCurses.initCurses
        -- initCurses :: IO ()
        -- 初期化する
    HSCurses.wAddStr HSCurses.stdScr "Hello, HSCurses"
        -- wAddStr :: Window -> String -> IO ()
        -- 標準の画面に文字を出力
    HSCurses.refresh
        -- refresh :: IO ()
        -- refreshすることが必要
    HSCurses.getCh
        -- getCh :: IO Key
        -- 一文字入力を待つ
    HSCurses.endWin
        -- endWin :: IO ()
        -- Cursesを用いたアプリケーションを
        -- 終了するときは, これが必要

I guess it translates to something like:

#!/usr/bin/env runhaskell
import qualified UI.HSCurses.Curses as HSCurses

main :: IO ()
main = do
-- prints a welcome message
    HSCurses.initCurses
        -- initCurses :: IO ()
        -- starts up the Curses: puts the terminal in
        -- graphical mode, moves the cursor, etc.
    HSCurses.wAddStr HSCurses.stdScr "Hello, HSCurses"
        -- wAddStr :: Window -> String -> IO ()
        -- prints a string :) to the screen in the first
        -- parameter. HSCurses.stdScr is the standard
        -- Window that gets created; you can create more
        -- windows, too, and print to them separately.
    HSCurses.refresh
        -- refresh :: IO ()
        -- refreshes the screen.
    HSCurses.getCh
        -- getCh :: IO Key
        -- waits for input
    HSCurses.endWin
        -- endWin :: IO ()
        -- frees all the resources

I like the minimalism of the examples in that blog post. This one doesn’t even use managed resources for simplicity; if someone aborts before endWin is called they could crash their terminal. The author rectifies that soon using bracket_.

The bracket_ function is similar to Python’s with and takes three functions that it executes:

bracket_ allocate deallocate work

First it runs allocate, then runs the work function, then runs deallocate. The deallocator is also executed when work exits in some abnormal way. You can think of work as the managed context that Python’s with creates.

With bracket_, we would rewrite our code to:

#!/usr/bin/env runhaskell
import qualified UI.HSCurses.Curses as HSCurses
import qualified Control.Exception as Exception

allocate = do
-- allocates resources
    HSCurses.initCurses

deallocate = do
-- frees resources
    HSCurses.endWin

work = do
-- prints a welcome message
    HSCurses.wAddStr HSCurses.stdScr "Hello, HSCurses"
    HSCurses.refresh
    HSCurses.getCh
    return ()

main :: IO ()
main = Exception.bracket_ allocate deallocate work 
-- the main program

Note that now we can also return meaningful values from work which was previously impossible because endWin took that possibility. We get the bonus that our allocator and deallocator can be next to eachother in the source file, making it easier to spot any discrepancies between the two which could result in bugs.

Let’s get our program more modular; how about we display five messages? In order to display a message at a specific position, you can use mvWAddStr:

HSCurses.mvWAddStr HSCurses.stdScr 6 2 "Hello, HSCurses"

The above puts the “H” in “Hello” on the third column of the seventh line. Let’s modularize a bit:

#!/usr/bin/env runhaskell
import qualified UI.HSCurses.Curses as HSCurses
import qualified Control.Exception as Exception
import Control.Monad (forM_)

allocate = do
-- allocates resources
    HSCurses.initCurses

deallocate = do
-- frees resources
    HSCurses.endWin

printHello line col = do
-- prints a welcome message
    HSCurses.mvWAddStr HSCurses.stdScr line col "Hello, HSCurses"

work = do
-- displays welcome messages
    forM_ [0..5] $ \line ->
        printHello line 0
    HSCurses.refresh
    HSCurses.getCh
    return ()

main :: IO ()
main = Exception.bracket_ allocate deallocate work 
-- the main program

Here we are using forM_ to iterate over a List; it’s just like Python’s for … in statement:

forM_ ourList $ \x ->
    myFunc x

is just like Python’s:

for x in our_list:
    my_func(x)

They can be nested, too:

forM_ ourList $ \x ->
    forM_ ourList2 $ \y ->
        myFunc x y

or, in Python:

for x in our_list:
    for y in our_list_2:
        my_func(x, y)

We are using the mvWAddStr function in order to print the strings. You have to be careful with it — if your printing offset is beyond the screen then the program crashes; if the string sticks out past the screen then you get wrapping which messes up your display. It’s an idea to use mvAddCh which doesn’t have the crashing problem; however it still wraps and additionally doesn’t let us specify the window. Let’s write a wrapper for mvWAddStr:

mvWAddStr2 w y x s = do
-- a safe alternative to mvWAddStr
    (rows, cols) <- HSCurses.scrSize
    when ((y >= 0) && (x >= 0) && (y < rows) && (x < cols)) $ do
        let space = cols - x - 1
        let s2 = take space s
        HSCurses.mvWAddStr w y x s2

Getting the off-by-one errors out took some testing; the first version of this that I tried would crash, only exactly if the horizontal offset was equal to the screen width. Here we are using when as an if without the else. The when function comes from Control.Monad.

Let’s apply the new function to printHello:

printHello line col = do
-- prints a welcome message
    mvWAddStr2 HSCurses.stdScr line col "Hello, HSCurses"

Let’s make a list of things

Alright, now let’s try displaying something more meaningful. How about a list of cheeses:

fromages =
    [ "Abondance de Savoie - 1990"
    , "Banon - 2003"
    , "Beaufort - 1968"
    , "Bleu d'Auvergne - 1975"
    , "Bleu de Gex-Haut-Jura - 1977"
    , "Bleu des Causses - 1979"
    , "Bleu du Vercors-Sassenage - 1998"
    , "Brie de Meaux - 1980"
    , "Brie de Melun - 1990"
    , "Brocciu - 1983 (sheep and goats' milk)"
    , "Camembert de Normandie - 1983"
    , "Cantal - 1956"
    , "Chabichou du Poitou - 1990"
    , "Chaource - 1970"
    , "Chevrotin - 2002"
    , "Comté - 1952"
    , "Crottin de Chavignol - 1976"
    , "Époisses - 2004"
    , "Fourme d'Ambert - 1972"
    , "Fourme de Montbrison - 1972"
    , "Gruyère - 2007"
    , "Laguiole - 1961"
    , "Langres - 1991"
    , "Livarot - 1972"
    , "Mâconnais - 2005"
    , "Maroilles - 1976"
    , "Morbier - 2000"
    , "Munster-Géromé - 1969"
    , "Neufchâtel - 1969"
    , "Ossau-Iraty - 1980"
    , "Pélardon des Cevennes - 2000"
    , "Picodon - 1983"
    , "Pont l'Evêque - 1976"
    , "Pouligny-Saint-Pierre - 1972"
    , "Reblochon - 1958"
    , "Rigotte de Condrieu - 2008"
    , "Rocamadour see also Cabecou - 1996"
    , "Roquefort - 1925"
    , "Saint-Nectaire - 1955"
    , "Sainte-Maure de Touraine - 1990"
    , "Salers - 1979"
    , "Selles-sur-Cher - 1975"
    , "Tome des Bauges - 2002"
    , "Vacherin Mont d'Or - 1981"
    , "Valençay - 1998"
    ]

Let’s also add some supporting functions:

getName cheese = cheese
-- returns the name of a cheese.
-- This will become more complicated some day.

printCheese line cheese = do
-- prints out info on a cheese
    mvWAddStr2 HSCurses.stdScr line 0 $ getName cheese

and finally let’s make use of this in the application:

work = do
-- displays cheeses
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese line cheese
    HSCurses.refresh
    HSCurses.getCh
    return ()

Our program now displays cheese. Let’s now make the thing interactive :)

Interactivity

In order to have interactivity we first need the ability to mark where we are. Let’s give the display a cursor:

printCheese line cheese cursor = do
-- prints out info on a cheese
    let indicator = if cursor
        then " > "
        else "   "
    mvWAddStr2 HSCurses.stdScr line 0 $ indicator ++ getName cheese

Additionally, we need to change work:

printCheese line cheese False

Now, let’s make the program respond to keyboard input. It already responds to input by quitting, let’s specify that a bit more. The getCh function returns all sorts of events; we’re only interested in KeyChar events:

work = do
-- displays cheeses
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese line cheese False
    HSCurses.refresh
    kc <- HSCurses.getCh
    case kc of
        HSCurses.KeyChar c -> handleKeyboard c
        _ -> return ()

The idea here is that handleKeyboard will call work again:

handleKeyboard c = case c of
-- handles keyboard input
    'q' -> return ()
    _ -> work

The code quits on Q and continues on anything else. Let’s add the cursor and make it react to input:

in work:

work requestedPosition = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese line cheese (line == position)

in handleKeyboard:

handleKeyboard c position = case c of
-- handles keyboard input
    'q' -> return ()
    _ -> work ((position + 1) `mod` 10)

in main:

main = Exception.bracket_ allocate deallocate (work 0)

Make the cursor actually respond like we want it to:

handleKeyboard c position = case c of
-- handles keyboard input
    'q' -> return ()
    'j' -> work (position + 1)
    'k' -> work (position - 1)
    _ -> work position

Let’s add the ability to scroll. In order to do that we need to be able to display the list with an offset:

handleKeyboard c position offset = case c of
(...)

work requestedPosition offset = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese (line + offset) cheese (line == position)
    HSCurses.refresh
    kc <- HSCurses.getCh
    case kc of
        HSCurses.KeyChar c -> handleKeyboard c position offset
        _ -> return ()

main = Exception.bracket_ allocate deallocate (work 0 0)

Passing (-10) as offset from main showed the list scrolled down — a proof that our scheme works. Let’s make the view scroll with the cursor:

work requestedPosition offset = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    (rows, cols) <- HSCurses.scrSize
    let screenPosition = position + offset
    let offset2 = if screenPosition >= rows
        then offset - (screenPosition - rows + 1)
        else if screenPosition < 0
            then offset - screenPosition
            else offset
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese (line + offset2) cheese (line == position)
    HSCurses.refresh
    kc <- HSCurses.getCh
    case kc of
        HSCurses.KeyChar c -> handleKeyboard c position offset2
        _ -> return ()

This seems to be working, except it doesn’t blank — so we get garbage where the screen isn’t being refreshed. Let’s call a blanker:

HSCurses.wclear HSCurses.stdScr
    let fromages2 = zip [0..] fromages

Works great!

Colors

Let’s make the line the cursor is on colorize. Terminals display text with foreground and background colors. We can use the module UI.HSCurses.CursesHelper to help ourselves:

import qualified UI.HSCurses.CursesHelper as HSCursesHelper

We have to define a new color pair, which is conceptually an object describing the foreground and background color of a glyph on the terminal. We can do it in allocate:

allocate = do
-- allocates resources
    HSCurses.initCurses
    hasColors <- HSCurses.hasColors
    if hasColors
        then do
            HSCurses.startColor
            HSCurses.initPair
                (HSCurses.Pair 1)
                (HSCursesHelper.black)
                (HSCursesHelper.white)
            return ()
        else
            return ()

We are calling hasColors to branch and then are setting a new color pair with initPair. The syntax is:

HSCurses.initPair (HSCurses.Pair pairNum) (foregroundColor) (backgroundColor)

Now to use the new color pair we modify printCheese:

printCheese line cheese cursor = do
-- prints out info on a cheese
    let (indicator, colorPair) = if cursor
        then (" > ", 1)
        else ("   ", 0)
    HSCurses.attrSet HSCurses.attr0 (HSCurses.Pair colorPair)
    mvWAddStr2 HSCurses.stdScr line 0 $ indicator ++ getName cheese

Works great! Let’s make the line span the width of the terminal:

printCheese line cheese cursor = do
-- prints out info on a cheese
    let (indicator, colorPair) = if cursor
        then (" > ", 1)
        else ("   ", 0)
    HSCurses.attrSet HSCurses.attr0 (HSCurses.Pair colorPair)
    (rows, cols) <- HSCurses.scrSize
    mvWAddStr2 HSCurses.stdScr line 0 $ replicate cols ' ' 
    mvWAddStr2 HSCurses.stdScr line 0 $ indicator ++ getName cheese

The program should look like this right now:

#!/usr/bin/env runhaskell
import qualified UI.HSCurses.Curses as HSCurses
import qualified UI.HSCurses.CursesHelper as HSCursesHelper
import qualified Control.Exception as Exception
import Control.Monad (forM_, when)

allocate = do
-- allocates resources
    HSCurses.initCurses
    hasColors <- HSCurses.hasColors
    if hasColors
        then do
            HSCurses.startColor
            HSCurses.initPair
                (HSCurses.Pair 1)
                (HSCursesHelper.black)
                (HSCursesHelper.white)
            return ()
        else
            return ()

deallocate = do
-- frees resources
    HSCurses.endWin

mvWAddStr2 w y x s = do
-- a safe alternative to mvWAddStr
    (rows, cols) <- HSCurses.scrSize
    when ((y >= 0) && (x >= 0) && (y < rows) && (x < cols)) $ do
        let space = cols - x - 1
        let s2 = take space s
        HSCurses.mvWAddStr w y x s2

fromages =
    [ "Abondance de Savoie - 1990"
    , "Banon - 2003"
    , "Beaufort - 1968"
    , "Bleu d'Auvergne - 1975"
    , "Bleu de Gex-Haut-Jura - 1977"
    , "Bleu des Causses - 1979"
    , "Bleu du Vercors-Sassenage - 1998"
    , "Brie de Meaux - 1980"
    , "Brie de Melun - 1990"
    , "Brocciu - 1983 (sheep and goats' milk)"
    , "Camembert de Normandie - 1983"
    , "Cantal - 1956"
    , "Chabichou du Poitou - 1990"
    , "Chaource - 1970"
    , "Chevrotin - 2002"
    , "Comté - 1952"
    , "Crottin de Chavignol - 1976"
    , "Époisses - 2004"
    , "Fourme d'Ambert - 1972"
    , "Fourme de Montbrison - 1972"
    , "Gruyère - 2007"
    , "Laguiole - 1961"
    , "Langres - 1991"
    , "Livarot - 1972"
    , "Mâconnais - 2005"
    , "Maroilles - 1976"
    , "Morbier - 2000"
    , "Munster-Géromé - 1969"
    , "Neufchâtel - 1969"
    , "Ossau-Iraty - 1980"
    , "Pélardon des Cevennes - 2000"
    , "Picodon - 1983"
    , "Pont l'Evêque - 1976"
    , "Pouligny-Saint-Pierre - 1972"
    , "Reblochon - 1958"
    , "Rigotte de Condrieu - 2008"
    , "Rocamadour see also Cabecou - 1996"
    , "Roquefort - 1925"
    , "Saint-Nectaire - 1955"
    , "Sainte-Maure de Touraine - 1990"
    , "Salers - 1979"
    , "Selles-sur-Cher - 1975"
    , "Tome des Bauges - 2002"
    , "Vacherin Mont d'Or - 1981"
    , "Valençay - 1998"
    ]

getName cheese = cheese
-- returns the name of a cheese.
-- This will become more complicated some day.

printCheese line cheese cursor = do
-- prints out info on a cheese
    let (indicator, colorPair) = if cursor
        then (" > ", 1)
        else ("   ", 0)
    HSCurses.attrSet HSCurses.attr0 (HSCurses.Pair colorPair)
    (rows, cols) <- HSCurses.scrSize
    mvWAddStr2 HSCurses.stdScr line 0 $ replicate cols ' ' 
    mvWAddStr2 HSCurses.stdScr line 0 $ indicator ++ getName cheese 

handleKeyboard c position offset = case c of
-- handles keyboard input
    'q' -> return ()
    'j' -> work (position + 1) offset
    'k' -> work (position - 1) offset
    _ -> work position offset

work requestedPosition offset = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    (rows, cols) <- HSCurses.scrSize
    let screenPosition = position + offset
    let offset2 = if screenPosition >= rows
        then offset - (screenPosition - rows + 1)
        else if screenPosition < 0
            then offset - screenPosition
            else offset
    HSCurses.wclear HSCurses.stdScr
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese (line + offset2) cheese (line == position)
    HSCurses.refresh
    kc <- HSCurses.getCh
    case kc of
        HSCurses.KeyChar c -> handleKeyboard c position offset2
        _ -> return ()

main :: IO ()
main = Exception.bracket_ allocate deallocate (work 0 0)
-- the main program

Caveat emptor

There is one problem: the “safe” string printing function we have made is not so safe; it breaks on multibyte characters: they pass through Haskell as a single character, but when HSCurses gets to them it counts them as multiple characters, and prints them as some sort of escape codes. This can crash your application. The alternative is to use mvAddCh instead; this however breaks things such as selecting words on the terminal, and does not let you print to a specific window. The mvwaddch function is not exported by HSCurses, either. I tried compiling the program and that did not help, either. By the virtue of these issues I see HSCurses as broken and not usable in stable programs.

NCurses

In order to install the ncurses package you need to have c2hs installed. Apparently cabal will take care of dependencies on libraries, but will not install new parsers/compilers/etc. You can install c2hs using cabal as well. A bit weird, but ok.

Another issue with NCurses is that it doesn’t seem to work in interpreted mode — if you can get it to run, please tell me how! For me, it displays garbage unless the program using it is compiled.

NCurses uses Data.Text for its strings, which is a better solution than just using Haskell strings. It could matter if you try to store 50 000 000 lines of text in memory.

When writing the HSCurses-based script, I have been doing :! % in Vim in order to execute the script. The % evaluates to the file name. This time, you have to compile it and execute the binary. The new shorthand is :!ghc % && /path/to/binary-name. For example, I am editing this as /tmp/nc.hs, so I execute :!ghc % && /tmp/nc every time I want to run the program’s newest version.

Hello, NCurses

The simplest script in NCurses is similar to the one in HSCurses:

module Main where

import qualified UI.NCurses as NCurses
import qualified Data.Text as Text

main = NCurses.runCurses $ do
-- prints a greeting
    win <- NCurses.defaultWindow
    NCurses.updateWindow win $ do
        NCurses.drawText $ Text.pack "Hello again, NCurses!"
    NCurses.render
    NCurses.getEvent win Nothing

This is very simple. The main function consists of very discernible things:

main = NCurses.runCurses $ do
        -- you need to call runCurses to execute your IO. Additionally,
        -- runCurses acts like somewhat of a resource manager.
-- prints a greeting
    win <- NCurses.defaultWindow
        -- get the default window. Just like HSCurses.
    NCurses.updateWindow win $ do
        -- update a window through the actions passed
        NCurses.drawText $ Text.pack "Hello again, NCurses!"
        -- action to draw text at the current cursor position.
    NCurses.render
        -- update the terminal with new window contents.
    NCurses.getEvent win Nothing
        -- read key (or something else, like a mouse click or event)

Compare with HSCurses:

#!/usr/bin/env runhaskell
import qualified UI.HSCurses.Curses as HSCurses

main :: IO ()
main = do
-- prints a welcome message
    HSCurses.initCurses
        -- initCurses :: IO ()
        -- starts up the Curses: puts the terminal in
        -- graphical mode, moves the cursor, etc.
    HSCurses.wAddStr HSCurses.stdScr "Hello, HSCurses"
        -- wAddStr :: Window -> String -> IO ()
        -- prints a string :) to the screen in the first
        -- parameter. HSCurses.stdScr is the standard
        -- Window that gets created; you can create more
        -- windows, too, and print to them separately.
    HSCurses.refresh
        -- refresh :: IO ()
        -- refreshes the screen.
    HSCurses.getCh
        -- getCh :: IO Key
        -- waits for input
    HSCurses.endWin
        -- endWin :: IO ()
        -- frees all the resources

Those things are pretty much identical in size (if you strip out the explanations). This comparison is, however, irrelevant to actual use, because the HSCurses code above is broken either way — for lack of resource management. NCurses wins here.

We are capturing quite a bit with that getEvent. Let’s make it only capture key presses:

readChar win = do
-- captures keyboard input
    event <- NCurses.getEvent win Nothing
    case event of
        Just (NCurses.EventCharacter _) -> return ()
        _ -> readChar win

main = NCurses.runCurses $ do
-- prints a greeting
    win <- NCurses.defaultWindow
    NCurses.updateWindow win $ do
        NCurses.drawText $ Text.pack "Hello again, NCurses!"
    NCurses.render
    readChar win

No need to separate out into a work like function, since it’s pretty much all of main, but let’s do it for clarity and ease of comparison:

work = do
-- prints a greeting
    win <- NCurses.defaultWindow
    NCurses.updateWindow win $ do
        NCurses.drawText $ Text.pack "Hello again, NCurses!"
    NCurses.render
    readChar win

main = NCurses.runCurses $ work
-- the main program

Now, let’s make NCurses print the output five times. In HSCurses we did something like this:

printHello line col = do
-- prints a welcome message
    HSCurses.mvWAddStr HSCurses.stdScr line col "Hello, HSCurses"

work = do
-- displays welcome messages
    forM_ [0..5] $ \line ->
        printHello line 0
    HSCurses.refresh
    HSCurses.getCh
    return ()

NCurses does not have a special function to print text at an offset; instead, it has moveCursor which explicitly allows you to move the cursor; afterwards you can print using drawText:

printHello line col = do
-- prints a welcome message
    NCurses.moveCursor line col
    NCurses.drawText $ Text.pack "Hello again, Curses!"

work = do
-- displays welcome messages
    win <- NCurses.defaultWindow
    NCurses.updateWindow win $ do
        forM_ [0..5] $ \line ->
            printHello line 0
    NCurses.render
    readChar win

Here we are again using the forM_ loop construct.

The drawText and moveCursor combo in NCurses has the same problems as mvWAddStr in HSCurses: try to move the cursor outside of your screen and you crash; try to print something that’ll wrap and you destroy your screen. Let’s try to wrap these like with HSCurses; as a bonus we can get rid of Text.pack and NCurses.updateWindow:

drawTextAt win y' x' s = do
-- a safe alternative to moveCursor and drawText
    (rows, cols) <- NCurses.screenSize
    let y = toInteger y'
    let x = toInteger x'
    when ((y >= 0) && (x >= 0) && (y < rows) && (x < cols)) $ do
        let space = fromInteger $ cols - x - 1
        let s2 = take space s
        NCurses.updateWindow win $ do
            NCurses.moveCursor (toInteger y) (toInteger x)
            NCurses.drawText $ Text.pack s2

Compare with:

mvWAddStr2 w y x s = do
-- a safe alternative to mvWAddStr
    (rows, cols) <- HSCurses.scrSize
    when ((y >= 0) && (x >= 0) && (y < rows) && (x < cols)) $ do
        let space = cols - x - 1
        let s2 = take space s
        HSCurses.mvWAddStr w y x s2

The two are pretty much the same: instead of HSCurses.scrSize you call NCurses.screenSize; you call NCurses.updateWindow; and you have to juggle the types a little bit, because NCurses was written by some sort of type freak. Personally I haven’t seen a terminal with more than 536870911 rows or columns, and that’s the age-old Haskell ‘98 bound on Int.

Of course, we have to change the other functions a bit as well to pass the win parameter. Here is the whole source as it is right now:

module Main where

import qualified UI.NCurses as NCurses
import qualified Data.Text as Text
import Control.Monad (forM_, when)

readChar win = do
-- captures keyboard input
    event <- NCurses.getEvent win Nothing
    case event of
        Just (NCurses.EventCharacter _) -> return ()
        _ -> readChar win

drawTextAt win y x s = do
-- a safe alternative to moveCursor and drawText
    (rows, cols) <- NCurses.screenSize
    let y = toInteger y'
    let x = toInteger x'
    when ((y >= 0) && (x >= 0) && (y < rows) && (x < cols)) $ do
        let space = fromInteger $ cols - x - 1
        let s2 = take space s
        NCurses.updateWindow win $ do
            NCurses.moveCursor (toInteger y) (toInteger x)
            NCurses.drawText $ Text.pack s2

printHello win line col = do
-- prints a welcome message
    drawTextAt win line col "Hello again, Curses!"

work = do
-- displays welcome messages
    win <- NCurses.defaultWindow
    forM_ [0..5] $ \line ->
        printHello win line 0
    NCurses.render
    readChar win

main = NCurses.runCurses $ work
-- the main program

Let’s make another list of things

Let’s have more cheese:

fromages =
    [ "Abondance de Savoie - 1990"
    , "Banon - 2003"
    , "Beaufort - 1968"
    , "Bleu d'Auvergne - 1975"
    , "Bleu de Gex-Haut-Jura - 1977"
    , "Bleu des Causses - 1979"
    , "Bleu du Vercors-Sassenage - 1998"
    , "Brie de Meaux - 1980"
    , "Brie de Melun - 1990"
    , "Brocciu - 1983 (sheep and goats' milk)"
    , "Camembert de Normandie - 1983"
    , "Cantal - 1956"
    , "Chabichou du Poitou - 1990"
    , "Chaource - 1970"
    , "Chevrotin - 2002"
    , "Comté - 1952"
    , "Crottin de Chavignol - 1976"
    , "Époisses - 2004"
    , "Fourme d'Ambert - 1972"
    , "Fourme de Montbrison - 1972"
    , "Gruyère - 2007"
    , "Laguiole - 1961"
    , "Langres - 1991"
    , "Livarot - 1972"
    , "Mâconnais - 2005"
    , "Maroilles - 1976"
    , "Morbier - 2000"
    , "Munster-Géromé - 1969"
    , "Neufchâtel - 1969"
    , "Ossau-Iraty - 1980"
    , "Pélardon des Cevennes - 2000"
    , "Picodon - 1983"
    , "Pont l'Evêque - 1976"
    , "Pouligny-Saint-Pierre - 1972"
    , "Reblochon - 1958"
    , "Rigotte de Condrieu - 2008"
    , "Rocamadour see also Cabecou - 1996"
    , "Roquefort - 1925"
    , "Saint-Nectaire - 1955"
    , "Sainte-Maure de Touraine - 1990"
    , "Salers - 1979"
    , "Selles-sur-Cher - 1975"
    , "Tome des Bauges - 2002"
    , "Vacherin Mont d'Or - 1981"
    , "Valençay - 1998"
    ]

The getName function has not changed:

getName cheese = cheese
-- returns the name of a cheese.
-- This will become more complicated some day.

whereas printCheese changes only slightly:

printCheese win line cheese = do
-- prints out info on a cheese
    drawTextAt win line 0 $ getName cheese

and finally work only has cosmetical differences from its HSCurses counterpart:

work = do
-- displays cheeses
    let fromages2 = zip [0..] fromages
    win <- NCurses.defaultWindow
    forM_ fromages2 $ \(line, cheese) ->
        printCheese win line cheese
    NCurses.render
    readChar win

The really nice thing here is that multibyte characters work, inlike in HSCurses. This is a big plus in my eyes.

Interactivity

Making the NCourses version of our program is trivial, just like in HSCourses. The changes are exactly the same and not worth describing again, here’s the code:

printCheese win line cheese cursor = do
-- prints out info on a cheese
    let indicator = if cursor
        then " > "
        else "   "
    drawTextAt win line 0 $ indicator ++ getName cheese

in work:

forM_ fromages2 $ \(line, cheese) ->
        printCheese win line cheese False

Making the program respond to keyboard input is very similar as well:

handleKeyboard c = case c of
-- handles keyboard input
    'q' -> return ()
    _ -> work

work = do
-- displays cheeses
    let fromages2 = zip [0..] fromages
    win <- NCurses.defaultWindow
    forM_ fromages2 $ \(line, cheese) ->
        printCheese win line cheese False
    NCurses.render
    ev <- NCurses.getEvent win Nothing
    case ev of
        Just (NCurses.EventCharacter c) -> handleKeyboard c
        _ -> return ()

here we can throw away readChar; it’s just a copy-paste to gut it and get the familiar layout from HSCurses. The notable difference here is that we get the input from NCurses.getEvent win Nothing and that we match against a different pattern. The handleKeyboard function is exactly the same.

Implementing our round-robin functionality is exactly the same; I’ll spare you the explanations and show you the new code:

handleKeyboard c position = case c of
-- handles keyboard input
    'q' -> return ()
    _ -> work ((position + 1) `mod` 10)

work requestedPosition = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    let fromages2 = zip [0..] fromages
    win <- NCurses.defaultWindow
    forM_ fromages2 $ \(line, cheese) ->
        printCheese win line cheese (line == position)
    NCurses.render
    ev <- NCurses.getEvent win Nothing
    case ev of
        Just (NCurses.EventCharacter c) -> handleKeyboard c position
        _ -> return ()

main = NCurses.runCurses $ (work 0)
-- the main program

Giving control over the cursor is exactly the same:

handleKeyboard c position = case c of
-- handles keyboard input
    'q' -> return ()
    'j' -> work (position + 1)
    'k' -> work (position - 1)
    _ -> work position

as is adding offset functionality:

handleKeyboard c position offset = case c of
(...)

work requestedPosition offset = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    let fromages2 = zip [0..] fromages
    win <- NCurses.defaultWindow
    forM_ fromages2 $ \(line, cheese) ->
        printCheese win (line + offset) cheese (line == position)
    NCurses.render
    ev <- NCurses.getEvent win Nothing
    case ev of
        Just (NCurses.EventCharacter c) -> handleKeyboard c position offset
        _ -> return ()

main = NCurses.runCurses $ (work 0 0)
-- the main program

and making the view scroll with the cursor:

work requestedPosition offset = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    (rows, cols) <- NCurses.screenSize
    let screenPosition = position + offset
    let offset2 = if (toInteger screenPosition) >= rows
        then fromInteger $
            (toInteger offset) - ((toInteger screenPosition) - rows + 1)
        else if screenPosition < 0
            then offset - screenPosition
            else offset
    let fromages2 = zip [0..] fromages
    win <- NCurses.defaultWindow
    forM_ fromages2 $ \(line, cheese) ->
        printCheese win (line + offset2) cheese (line == position)
    NCurses.render
    ev <- NCurses.getEvent win Nothing
    case ev of
        Just (NCurses.EventCharacter c) -> handleKeyboard c position offset2
        _ -> return ()

Blanking is not an easy as in HSCurses; there’s no standard function for blanking the whole screen. Let’s write our own:

blank win = do
-- blanks the screen
    (rows, cols) <- NCurses.screenSize
    let blankLine = replicate (fromInteger cols) ' '
    forM_ [0..(rows - 1)] $ \line ->
        drawTextAt win line 0 blankLine

and plug it into main:

win <- NCurses.defaultWindow
    blank win
    forM_ fromages2 $ \(line, cheese) ->
        printCheese win (line + offset2) cheese (line == position)

It works just as expected.

Colors

Setting up colors is somewhat similar. There are still pairs of colors and they are still assigned to integer ID’s. Everything is in the NSCurses module, no need to import new things.

Creating an allocator works differently; NSCurses does not use side-effects like HSCurses does; since work is run by the monad we can use bind in order to pass it parameters:

main = NCurses.runCurses $ allocate >>= (work 0 0)
-- the main program

The allocator is very simple; we can leverage Maybe to take care of the case when the terminal is monochromatic; we did not do that with HSCurses although it’s not impossible to imagine something similar. The basic thing you want to do is:

allocate = do
-- sets up NCurses.
    NCurses.newColorID NCurses.ColorWhite NCurses.ColorBlack 1

With conditioning it looks like this:

allocate = do
-- sets up NCurses.
    maxId <- NCurses.maxColorID
    if  maxId > 0
        then do
            supportsColor <- NCurses.supportsColor
            if supportsColor
                then do
                    cid <- (NCurses.newColorID
                        NCurses.ColorBlack
                        NCurses.ColorWhite
                        1
                        )
                    return (Just cid)
                else return Nothing
        else return Nothing

The work and handleKeyboard functions only need to accept the new parameter for the thing to compile:

handleKeyboard c position offset colorId = case c of
(...)

work requestedPosition offset colorId = do
(...)

In order to make the display use the new color you need to pass it onto printCheese:

printCheese win line cheese cursor colorId = do
-- prints out info on a cheese
    let (indicator, useColor) = if cursor
        then (" > ", True)
        else ("   ", False)
    case colorId of
        Just cid -> when useColor $ do
            NCurses.updateWindow win $ NCurses.setColor cid
        Nothing -> return ()
    drawTextAt win line 0 $ indicator ++ getName cheese
    NCurses.updateWindow win $ NCurses.setColor NCurses.defaultColorID

If we make the selection line as wide as the terminal the end of printCheese looks like this:

(rows, cols) <- NCurses.screenSize
    drawTextAt win line 0 $ replicate (fromInteger cols) ' ' 
    drawTextAt win line 0 $ indicator ++ getName cheese
    NCurses.updateWindow win $ NCurses.setColor NCurses.defaultColorID

The code should currently look like this:

module Main where

import qualified UI.NCurses as NCurses
import qualified Data.Text as Text
import Control.Monad (forM_, when)

allocate = do
-- sets up NCurses.
    maxId <- NCurses.maxColorID
    if  maxId > 0
        then do
            supportsColor <- NCurses.supportsColor
            if supportsColor
                then do
                    cid <- (NCurses.newColorID
                        NCurses.ColorBlack
                        NCurses.ColorWhite
                        1
                        )
                    return (Just cid)
                else return Nothing
        else return Nothing

drawTextAt win y' x' s = do
-- a safe alternative to moveCursor and drawText
    (rows, cols) <- NCurses.screenSize
    let y = toInteger y'
    let x = toInteger x'
    when ((y >= 0) && (x >= 0) && (y < rows) && (x < cols)) $ do
        let space = fromInteger $ cols - x - 1
        let s2 = take space s
        NCurses.updateWindow win $ do
            NCurses.moveCursor (toInteger y) (toInteger x)
            NCurses.drawText $ Text.pack s2

fromages =
    [ "Abondance de Savoie - 1990"
    , "Banon - 2003"
    , "Beaufort - 1968"
    , "Bleu d'Auvergne - 1975"
    , "Bleu de Gex-Haut-Jura - 1977"
    , "Bleu des Causses - 1979"
    , "Bleu du Vercors-Sassenage - 1998"
    , "Brie de Meaux - 1980"
    , "Brie de Melun - 1990"
    , "Brocciu - 1983 (sheep and goats' milk)"
    , "Camembert de Normandie - 1983"
    , "Cantal - 1956"
    , "Chabichou du Poitou - 1990"
    , "Chaource - 1970"
    , "Chevrotin - 2002"
    , "Comté - 1952"
    , "Crottin de Chavignol - 1976"
    , "Époisses - 2004"
    , "Fourme d'Ambert - 1972"
    , "Fourme de Montbrison - 1972"
    , "Gruyère - 2007"
    , "Laguiole - 1961"
    , "Langres - 1991"
    , "Livarot - 1972"
    , "Mâconnais - 2005"
    , "Maroilles - 1976"
    , "Morbier - 2000"
    , "Munster-Géromé - 1969"
    , "Neufchâtel - 1969"
    , "Ossau-Iraty - 1980"
    , "Pélardon des Cevennes - 2000"
    , "Picodon - 1983"
    , "Pont l'Evêque - 1976"
    , "Pouligny-Saint-Pierre - 1972"
    , "Reblochon - 1958"
    , "Rigotte de Condrieu - 2008"
    , "Rocamadour see also Cabecou - 1996"
    , "Roquefort - 1925"
    , "Saint-Nectaire - 1955"
    , "Sainte-Maure de Touraine - 1990"
    , "Salers - 1979"
    , "Selles-sur-Cher - 1975"
    , "Tome des Bauges - 2002"
    , "Vacherin Mont d'Or - 1981"
    , "Valençay - 1998"
    ]

getName cheese = cheese
-- returns the name of a cheese.
-- This will become more complicated some day.

printCheese win line cheese cursor colorId = do
-- prints out info on a cheese
    let (indicator, useColor) = if cursor
        then (" > ", True)
        else ("   ", False)
    case colorId of
        Just cid -> when useColor $ do
            NCurses.updateWindow win $ NCurses.setColor cid
        Nothing -> return ()
    (rows, cols) <- NCurses.screenSize
    drawTextAt win line 0 $ replicate (fromInteger cols) ' ' 
    drawTextAt win line 0 $ indicator ++ getName cheese
    NCurses.updateWindow win $ NCurses.setColor NCurses.defaultColorID

blank win = do
-- blanks the screen
    (rows, cols) <- NCurses.screenSize
    let blankLine = replicate (fromInteger cols) ' '
    forM_ [0..(rows - 1)] $ \line ->
        drawTextAt win line 0 blankLine

handleKeyboard c position offset colorId = case c of
-- handles keyboard input
    'q' -> return ()
    'j' -> work (position + 1) offset colorId
    'k' -> work (position - 1) offset colorId
    _ -> work position offset colorId

work requestedPosition offset colorId = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    (rows, cols) <- NCurses.screenSize
    let screenPosition = position + offset
    let offset2 = if (toInteger screenPosition) >= rows
        then fromInteger $
            (toInteger offset) - ((toInteger screenPosition) - rows + 1)
        else if screenPosition < 0
            then offset - screenPosition
            else offset
    let fromages2 = zip [0..] fromages
    win <- NCurses.defaultWindow
    blank win
    forM_ fromages2 $ \(line, cheese) ->
        printCheese win (line + offset2) cheese (line == position) colorId
    NCurses.render
    ev <- NCurses.getEvent win Nothing
    case ev of
        Just (NCurses.EventCharacter c) ->
            handleKeyboard c position offset2 colorId
        _ -> return ()

main = NCurses.runCurses $ allocate >>= (work 0 0)
-- the main program

Caveat emptor

All in all NCurses was a much better experience than HSCurses. A more functional monadic shell coupled with a more concise set of functions has made this possible. The fact that it works with multibyte characters makes it a much better choice than HSCurses. There have been some quirks however:

  1. The lack of a blanking function, even if it’s a simple one, was noticeable
  2. I have encountered some redrawing problems where the highlighted row wasn’t being updated if it was scrolling the screen’s lower edge. I have been unable to fix this. If you have any ideas, please let me know. Even with the above problems NCurses is much better than HSCurses.

Vty

The vty package is a departure from the curses library and tries to do everything on its own. It’s structured differently, into many small modules, and uses the concept of “images” and “pictures”. Here is a short description of Vty:

A very simple terminal interface library.

In 150 non-blank non-comment lines of Haskell (and 7 lines of C) vty provides:

  • Automatic handling of suspend/resume (SIGTSTP+SIGCONT)
  • Automatic handling of window resizes
  • Automatic computation of minimal differences
  • Minimizes repaint area, thus virtually eliminating the flicker problem that plagues ncurses programs
  • Automatically decodes keyboard keys into (key,[modifier]) tuples
  • Automatically supports refresh on Ctrl-L.
  • Automatically supports timeout after 50ms for lone ESC (a barely noticable delay)
  • Extensive color scheme support: background colors, default colors, reverse-video, bold, underline, half-bright, and blinking attributes.
  • Unicode characters on output, automatically setting and resetting UTF-8 mode (beware double width and combining characters!)
  • Disables ISIG and IXOFF, allowing C-q, C-s, C-c, C-z, and C-\ to be received as input.
  • Interface is designed for relatively easy compatible extension.

Current disadvantages:

  • No current support for non-ANSI terminals.
  • Minimal support for special keys on terminals other than the linux-console. (F1-5 and arrow keys should work, but anything shifted isn’t likely to.)
  • Uses the TIOCGWINSZ ioctl to find the current window size, which appears to be limited to Linux and *BSD.

The Vty module suite is much more low-level than NCurses or even HSCurses. I think it has to be very good for slightly upgrading the visual output of what otherwise is a listing in the terminal — think of colorizing grep output, making a spinner for wget, making progress bars, etc.

Hello, Vty!

You use Vty with normal Haskell functions. Therefore the simplest “hello world” style program does not use Vty at all:

#!/usr/bin/env runhaskell
module Main where

main = do
-- the main program
    putStrLn "Hello, Vty!"

Let’s now make a real “hello world” which puts the terminal in graphic mode and waits for input before terminating:

main = do
-- the main program
    vt <- Vty.mkVty
    t <- Vty.terminal_handle
    putStrLn "Hello, Vty!"
    Vty.next_event vt
    Vty.release_terminal t

We can move the cursor with “set_cursor_pos”:

Vty.set_cursor_pos t 6 2

It is notable that the coordinates are swapped in comparison to HSCurses and NCurses.

Now it’s time to try and display five messages. Let’s write a function which prints at a certain spot; again we have to do some culling and filtering to prevent wrapping. We get the size of the terminal using:

Vty.DisplayRegion c r <- Vty.display_bounds term

We again add printHello and drawTextAt:

#!/usr/bin/env runhaskell

module Main where

import qualified Graphics.Vty as Vty
import qualified System.IO as I
import Control.Monad (when)

drawTextAt term y x s = do
-- draws text at a specific location
    Vty.DisplayRegion cols2 rows2 <- Vty.display_bounds term
    let (cols, rows) = (fromEnum cols2, fromEnum rows2)
    when ((y >= 0) && (x >= 0) && (y < rows) && (x < cols)) $ do
        let space = cols - x
        let s2 = take space s
        Vty.set_cursor_pos term (toEnum x) (toEnum y)
        putStr s2

printHello term line col =
-- displays a greeting
    drawTextAt term line col "Hello, Vty!"

main = do
    vt <- Vty.mkVty
    t <- Vty.terminal_handle
    printHello t 0 0
    I.hFlush I.stdout
    Vty.next_event vt
    Vty.release_terminal t

we can again use forM_ to structure our loop:

forM_ [0..5] $ \line ->
        printHello t line 0

We have to, however, move the flush from main to drawTextAt; it seems the cursor only moves at a flush:

putStr s2
        I.hFlush I.stdout

Let’s again make a list of things

Everything is better with a bit of french cheese added:

fromages =
    [ "Abondance de Savoie - 1990"
    , "Banon - 2003"
    , "Beaufort - 1968"
    , "Bleu d'Auvergne - 1975"
    , "Bleu de Gex-Haut-Jura - 1977"
    , "Bleu des Causses - 1979"
    , "Bleu du Vercors-Sassenage - 1998"
    , "Brie de Meaux - 1980"
    , "Brie de Melun - 1990"
    , "Brocciu - 1983 (sheep and goats' milk)"
    , "Camembert de Normandie - 1983"
    , "Cantal - 1956"
    , "Chabichou du Poitou - 1990"
    , "Chaource - 1970"
    , "Chevrotin - 2002"
    , "Comté - 1952"
    , "Crottin de Chavignol - 1976"
    , "Époisses - 2004"
    , "Fourme d'Ambert - 1972"
    , "Fourme de Montbrison - 1972"
    , "Gruyère - 2007"
    , "Laguiole - 1961"
    , "Langres - 1991"
    , "Livarot - 1972"
    , "Mâconnais - 2005"
    , "Maroilles - 1976"
    , "Morbier - 2000"
    , "Munster-Géromé - 1969"
    , "Neufchâtel - 1969"
    , "Ossau-Iraty - 1980"
    , "Pélardon des Cevennes - 2000"
    , "Picodon - 1983"
    , "Pont l'Evêque - 1976"
    , "Pouligny-Saint-Pierre - 1972"
    , "Reblochon - 1958"
    , "Rigotte de Condrieu - 2008"
    , "Rocamadour see also Cabecou - 1996"
    , "Roquefort - 1925"
    , "Saint-Nectaire - 1955"
    , "Sainte-Maure de Touraine - 1990"
    , "Salers - 1979"
    , "Selles-sur-Cher - 1975"
    , "Tome des Bauges - 2002"
    , "Vacherin Mont d'Or - 1981"
    , "Valençay - 1998"
    ]

The supporting functions:

getName cheese = cheese
-- returns the name of a cheese.
-- This will become more complicated some day.

printCheese term line cheese = do
-- prints out info on a cheese
    drawTextAt term line 0 $ getName cheese

Plug this into main:

main = do
-- displays cheeses
    vt <- Vty.mkVty
    t <- Vty.terminal_handle
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese t line cheese
    Vty.next_event vt
    Vty.release_terminal t

Works! Additionally, multibyte characters are displayed as well which is very good.

Interactivity

In order to have interactivity we first need the ability to mark where we are. Let’s give the display a cursor:

printCheese term line cheese cursor = do
-- prints out info on a cheese
    let indicator = if cursor
        then " > "
        else "   "
    drawTextAt term line 0 $ indicator ++ getName cheese

Additionally, we need to change main:

printCheese t line cheese False

Before we go on with the keyboard control we need to set up the allocator; the manual says we should only have one copy of Vty.Vty around, otherwise Vty will bug out. Additionally, we need to release_terminal at the end. Let’s do the usual:

allocate = do
-- sets up Vty
    vt <- Vty.mkVty
    t <- Vty.terminal_handle
    return (vt, t)

deallocate t =
-- frees Vty resouces
    Vty.release_terminal t

work (vt, t) = do
-- displays cheeses
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese t line cheese False
    Vty.next_event vt
    return t

main = allocate >>= work >>= deallocate
-- the main program

Actual keyboard control looks nearly the same as in HSCurses and NCurses; this time we pattern-match against EvKey, which has the nice ability to tell us about the keyboard event and the currently pressed modifiers separately: EvKey Key [Modifier]. The Key can again be pattern-matched against KASCII c. The handleKeyboard function is almost exactly the same; the only difference is that work has to ultimately return the terminal handle for the deallocate function to use:

handleKeyboard c (vt, t) = case c of
-- handles keyboard input
    'q' -> return t
    _ -> work (vt, t)

work (vt, t) = do
-- displays cheeses
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese t line cheese False
    ev <- Vty.next_event vt
    case ev of
        Vty.EvKey (Vty.KASCII c) [] -> handleKeyboard c (vt, t)
        _ -> return t

Now it’s time to make the program display the cursor and respond to keyboard input:

handleKeyboard c position (vt, t) = case c of
-- handles keyboard input
    'q' -> return t
    _ -> work ((position + 1) `mod` 10) (vt, t)

work requestedPosition (vt, t) = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese t line cheese (line == position)
    ev <- Vty.next_event vt
    case ev of
        Vty.EvKey (Vty.KASCII c) [] -> handleKeyboard c position (vt, t)
        _ -> return t

main = allocate >>= (work 0) >>= deallocate
-- the main program

Making the cursor respond to j and k is the same, too:

handleKeyboard c position (vt, t) = case c of
-- handles keyboard input
    'q' -> return t
    'j' -> work (position + 1) (vt, t)
    'k' -> work (position - 1) (vt, t)
    _ -> work position (vt, t)

Now it’s time to add the ability to scroll. First, add the offset:

handleKeyboard c position offset (vt, t) = case c of
-- handles keyboard input
    'q' -> return t
    'j' -> work (position + 1) offset (vt, t)
    'k' -> work (position - 1) offset (vt, t)
    _ -> work position offset (vt, t)

work requestedPosition offset (vt, t) = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese t (line + offset) cheese (line == position)
    ev <- Vty.next_event vt
    case ev of
        Vty.EvKey (Vty.KASCII c) [] -> handleKeyboard c position offset (vt, t)
        _ -> return t

main = allocate >>= (work 0 0) >>= deallocate
-- the main program

Then, the ability to move the view with the cursor is added as expected:

work requestedPosition offset (vt, t) = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    Vty.DisplayRegion cols2 rows2 <- Vty.display_bounds t
    let (cols, rows) = (fromEnum cols2, fromEnum rows2)
    let screenPosition = position + offset
    let offset2 = if screenPosition >= rows
        then offset - (screenPosition - rows + 1)
        else if screenPosition < 0
            then offset - screenPosition
            else offset
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese t (line + offset2) cheese (line == position)
    ev <- Vty.next_event vt
    case ev of
        Vty.EvKey (Vty.KASCII c) [] -> handleKeyboard c position offset2 (vt, t)
        _ -> return t

We need to blank the screen; Vty does not have a ready facility for that. I have tried writing one with Vty’s fills, but that didn’t seem to do anything:

blank vt term = do
-- blanks the screen
    Vty.DisplayRegion cols2 rows2 <- Vty.display_bounds term
    let fill = Vty.char_fill Vty.current_attr ' ' cols2 rows2
    Vty.update vt (Vty.pic_for_image fill)

…so I have gone the other way:

blank vt term = do
-- blanks the screen
    Vty.DisplayRegion cols2 rows2 <- Vty.display_bounds term
    let (cols, rows) = (fromEnum cols2, fromEnum rows2)
    let blankLine = replicate cols ' '
    forM_ [0..(rows - 1)] $ \line ->
        drawTextAt term line 0 blankLine

Naturally, that’s not going to perform just as well; it creates blinking just like when we did that with NCurses.

Colors

Usage of colors in Vty is fairly easy; no need to define color pairs or anything like that. You can use the Graphics.Vty.Inline module in order to change character style. According to the authors of Vty, “Seriously, terminal color support is INSANE.”

I have first tried to do something like this:

import qualified Graphics.Vty.Inline as VtyI
(...)
printCheese term line cheese cursor = do
-- prints out info on a cheese
    let (indicator, useColor) = if cursor
        then (" > ", True)
        else ("   ", False)
    when useColor $ VtyI.put_attr_change term $ do
            VtyI.fore_color Vty.black
            VtyI.back_color Vty.white
    drawTextAt term line 0 $ indicator ++ getName cheese
    VtyI.put_attr_change term $ VtyI.default_all

This, however, did not work as expected: the coloring started at the end of the previous line, i.e. where the cursor had been before moving to a new line and printing. We need to pass our color-changing routine as a parameter to drawTextAt instead:

drawTextAt term y x s attr = do
-- draws text at a specific location
    Vty.DisplayRegion cols2 rows2 <- Vty.display_bounds term
    let (cols, rows) = (fromEnum cols2, fromEnum rows2)
    when ((y >= 0) && (x >= 0) && (y < rows) && (x < cols)) $ do
        let space = cols - x
        let s2 = take space s
        Vty.set_cursor_pos term (toEnum x) (toEnum y)
        case attr of
            Nothing -> return()
            Just f -> VtyI.put_attr_change term f
        putStr s2
        VtyI.put_attr_change term $ VtyI.default_all
        I.hFlush I.stdout

invertedStyle = do
-- inverts the text
    VtyI.fore_color Vty.black
    VtyI.back_color Vty.white

printCheese term line cheese cursor = do
-- prints out info on a cheese
    let (indicator, useColor) = if cursor
        then (" > ", True)
        else ("   ", False)
    let attr = if useColor
        then Just invertedStyle
        else Nothing
    drawTextAt term line 0 (indicator ++ (getName cheese)) attr

We can now make the selection wider. I have been unable to print two lines on top of each other; it seemed like the last thing I printed “won”. I think Vty might be moving the cursor by printing spaces, or something like that. I have instead opted to augument the current string by spaces. Note that this might break if you use double-width characters. Good thing chou dofu is not an actual cheese. I have changed the printCheese function like this:

printCheese term line cheese cursor = do
-- prints out info on a cheese
    let (indicator, useColor) = if cursor
        then (" > ", True)
        else ("   ", False)
    let attr = if useColor
        then Just invertedStyle
        else Nothing
    let output = indicator ++ (getName cheese)
    Vty.DisplayRegion cols2 rows2 <- Vty.display_bounds term
    let (cols, rows) = (fromEnum cols2, fromEnum rows2)
    let blank = if useColor
        then replicate (cols - (length output)) ' '
        else ""
    drawTextAt term line 0 (output ++ blank) attr

This is what the code should look like now:

#!/usr/bin/env runhaskell

module Main where
import qualified Graphics.Vty as Vty
import qualified Graphics.Vty.Inline as VtyI
import qualified System.IO as I
import Control.Monad (forM_, when)

import qualified Data.Typeable as T

fromages =
    [ "Abondance de Savoie - 1990"
    , "Banon - 2003"
    , "Beaufort - 1968"
    , "Bleu d'Auvergne - 1975"
    , "Bleu de Gex-Haut-Jura - 1977"
    , "Bleu des Causses - 1979"
    , "Bleu du Vercors-Sassenage - 1998"
    , "Brie de Meaux - 1980"
    , "Brie de Melun - 1990"
    , "Brocciu - 1983 (sheep and goats' milk)"
    , "Camembert de Normandie - 1983"
    , "Cantal - 1956"
    , "Chabichou du Poitou - 1990"
    , "Chaource - 1970"
    , "Chevrotin - 2002"
    , "Comté - 1952"
    , "Crottin de Chavignol - 1976"
    , "Époisses - 2004"
    , "Fourme d'Ambert - 1972"
    , "Fourme de Montbrison - 1972"
    , "Gruyère - 2007"
    , "Laguiole - 1961"
    , "Langres - 1991"
    , "Livarot - 1972"
    , "Mâconnais - 2005"
    , "Maroilles - 1976"
    , "Morbier - 2000"
    , "Munster-Géromé - 1969"
    , "Neufchâtel - 1969"
    , "Ossau-Iraty - 1980"
    , "Pélardon des Cevennes - 2000"
    , "Picodon - 1983"
    , "Pont l'Evêque - 1976"
    , "Pouligny-Saint-Pierre - 1972"
    , "Reblochon - 1958"
    , "Rigotte de Condrieu - 2008"
    , "Rocamadour see also Cabecou - 1996"
    , "Roquefort - 1925"
    , "Saint-Nectaire - 1955"
    , "Sainte-Maure de Touraine - 1990"
    , "Salers - 1979"
    , "Selles-sur-Cher - 1975"
    , "Tome des Bauges - 2002"
    , "Vacherin Mont d'Or - 1981"
    , "Valençay - 1998"
    ]

getName cheese = cheese
-- returns the name of a cheese.
-- This will become more complicated some day.

drawTextAt term y x s attr = do
-- draws text at a specific location
    Vty.DisplayRegion cols2 rows2 <- Vty.display_bounds term
    let (cols, rows) = (fromEnum cols2, fromEnum rows2)
    when ((y >= 0) && (x >= 0) && (y < rows) && (x < cols)) $ do
        let space = cols - x
        let s2 = take space s
        Vty.set_cursor_pos term (toEnum x) (toEnum y)
        case attr of
            Nothing -> return()
            Just f -> VtyI.put_attr_change term f
        putStr s2
        VtyI.put_attr_change term $ VtyI.default_all
        I.hFlush I.stdout

blank vt term = do
-- blanks the screen
    Vty.DisplayRegion cols2 rows2 <- Vty.display_bounds term
    let (cols, rows) = (fromEnum cols2, fromEnum rows2)
    let blankLine = replicate cols ' '
    forM_ [0..(rows - 1)] $ \line ->
        drawTextAt term line 0 blankLine Nothing

invertedStyle = do
-- inverts the text
    VtyI.fore_color Vty.black
    VtyI.back_color Vty.white

printCheese term line cheese cursor = do
-- prints out info on a cheese
    let (indicator, useColor) = if cursor
        then (" > ", True)
        else ("   ", False)
    let attr = if useColor
        then Just invertedStyle
        else Nothing
    let output = indicator ++ (getName cheese)
    Vty.DisplayRegion cols2 rows2 <- Vty.display_bounds term
    let (cols, rows) = (fromEnum cols2, fromEnum rows2)
    let blank = if useColor
        then replicate (cols - (length output)) ' '
        else ""
    drawTextAt term line 0 (output ++ blank) attr

allocate = do
-- sets up Vty
    vt <- Vty.mkVty
    t <- Vty.terminal_handle
    return (vt, t)

deallocate t =
-- frees Vty resouces
    Vty.release_terminal t

handleKeyboard c position offset (vt, t) = case c of
-- handles keyboard input
    'q' -> return t
    'j' -> work (position + 1) offset (vt, t)
    'k' -> work (position - 1) offset (vt, t)
    _ -> work position offset (vt, t)

work requestedPosition offset (vt, t) = do
-- displays cheeses
    let position = max 0 (min requestedPosition (length fromages - 1))
    Vty.DisplayRegion cols2 rows2 <- Vty.display_bounds t
    let (cols, rows) = (fromEnum cols2, fromEnum rows2)
    let screenPosition = position + offset
    let offset2 = if screenPosition >= rows
        then offset - (screenPosition - rows + 1)
        else if screenPosition < 0
            then offset - screenPosition
            else offset
    blank vt t
    let fromages2 = zip [0..] fromages
    forM_ fromages2 $ \(line, cheese) ->
        printCheese t (line + offset2) cheese (line == position)
    ev <- Vty.next_event vt
    case ev of
        Vty.EvKey (Vty.KASCII c) [] ->
            handleKeyboard c position offset2 (vt, t)
        _ -> return t

main = allocate >>= (work 0 0) >>= deallocate
-- the main program

Caveat emptor

The Vty module was a breeze to use. One problem was that its drawing functions — that is, anything that has to do with the Image or Picture types — just don’t seem to work for me. I couldn’t get them to work, they were eating output or just not doing anything. The failed attempt at blank above is one of those things; here’s another example of Vty’s weirdness:

#!/usr/bin/env runhaskell

module Main where
import qualified Graphics.Vty as Vty

main = do
    vt <- Vty.mkVty
    t <- Vty.terminal_handle
    Vty.DisplayRegion w h <- Vty.display_bounds t
    print w
    print h
    -- outputs "hi there"
    -- let i = Vty.vert_cat [Vty.string Vty.current_attr "hello", Vty.string Vty.current_attr "hi there"]
    --
    -- outputs hello
    -- let i = Vty.vert_cat [Vty.string Vty.current_attr "hello", Vty.string Vty.current_attr "hello"]
    --
    -- outputs nothing
    -- let i = Vty.vert_cat [Vty.string Vty.current_attr "hello", Vty.empty_image]
    --
    -- outputs nothing
    -- let i = Vty.vert_cat [Vty.empty_image, Vty.string Vty.current_attr "hello"]
    --
    -- outputs nothing
    -- let i = Vty.string Vty.current_attr "hello"
    --
    -- outputs "hello2hello3":
    -- let i = Vty.vert_cat [Vty.string Vty.current_attr "hello", Vty.string Vty.current_attr "hello2", Vty.string Vty.current_attr "hello3"]
    -- 
    -- outputs "hello3":
    -- let i = Vty.vert_cat [Vty.empty_image, Vty.string Vty.current_attr "hello2", Vty.string Vty.current_attr "hello3"]
    --
    -- outputs "hello3":
    -- let i = Vty.vert_cat [Vty.empty_image, Vty.empty_image, Vty.string Vty.current_attr "hello2", Vty.string Vty.current_attr "hello3"]
    -- 
    -- outputs "hello3":
    -- let i = Vty.vert_cat [Vty.empty_image, Vty.empty_image, Vty.string Vty.current_attr "hello2", Vty.string Vty.current_attr "hello3", Vty.empty_image, Vty.empty_image, Vty.empty_image, Vty.empty_image]
    --
    -- outputs "hello3":
    let i = Vty.vert_cat [Vty.string Vty.current_attr "", Vty.string Vty.current_attr "", Vty.string Vty.current_attr "hello2", Vty.string Vty.current_attr "hello3"]
    --
    -- outputs "ahello2hello3":
    -- let i = Vty.vert_cat [Vty.string Vty.current_attr "a", Vty.string Vty.current_attr "a", Vty.string Vty.current_attr "hello2", Vty.string Vty.current_attr "hello3"]
    --
    -- outputs "bhello2hello3":
    let i = Vty.vert_cat [Vty.string Vty.current_attr "a", Vty.string Vty.current_attr "b", Vty.string Vty.current_attr "hello2", Vty.string Vty.current_attr "hello3"]

    let p = Vty.pic_for_image i
    Vty.update vt p

    -- after the first update, let's do the same again:
    --
    -- outputs "ahello2hello2"
    -- let i = Vty.vert_cat [Vty.string Vty.current_attr "a", Vty.string Vty.current_attr "a", Vty.string Vty.current_attr "hello2", Vty.string Vty.current_attr "hello3"]
    -- 
    -- outputs "aahello2hello3"
    let i = Vty.vert_cat [Vty.string Vty.current_attr "a", Vty.string Vty.current_attr "a", Vty.string Vty.current_attr "hello2", Vty.string Vty.current_attr "hello3"]

    let p = Vty.pic_for_image i
    Vty.update vt p


    return ()

Apparently, Vty’s idea of graphical output is like this:

You use Graphics.Vty.Picture.pic_for_image to transform an Image into a Picture. Finally, you use Graphics.Vty.Terminal.output_picture to print to Picture to a DisplayHandle.

You get a DisplayHandle from a DisplayRegion and TerminalHandle using display_context; you get a TerminalHandle using Graphics.Vty.Terminal.terminal_handle and release it with release_terminal. You get a DisplayRegion using display_bounds apparently.

This is a great idea, but it just didn’t work for me. I’m curious as to how it turns out in practice; if anyone knows what I’ve been doing wrong, or if Vty was just broken and if there’s a fix, then please let me know via the comments.

In the end…

I think that both NCurses and Vty are pretty cool. Turns out HSCurses is unusable unless the multibyte character problems are fixed.

It might be a curses limitation, but it’s not acceptable to have the application crashing when output is being done outside of the terminal. A safety net should be in place for this situation. In my experience Vty performed best there, simply stopping the cursor at the border.

Each of the frameworks had its own issues:

  • HSCurses doesn’t feel too Haskellish, has problems with printing characters, and likes to crash; additionally it doesn’t export some useful functions from curses
  • both NCurses and Vty are missing a way to blank the screen easily and quickly
  • NCurses and Vty promise more than they actually seem to do
  • NCurses and Vty only work if the program is compiled
  • all thee frameworks are missing a load of documentation; most importantly ready examples that include every function you might want to use. A function without a usage example is useless, especially given the vastly different APIs that all three try to implement for their more advanced features.

All three are cool; all three have issues that would prevent me from using them in release programs (HSCurses‘ crashiness, NCurses’ and Vty’s blanking behaviour); however, they don’t seem to be far off. Work on that guys, and you’re golden. Let’s have a new Vim clone in Haskell, or something.

I am perfectly aware that the code I am showing here might not be the best; maybe I’m missing some important points as to how things should be done; feel free to comment and show how you would do things! I’m especially interested in how to better blank in NCurses and Vty, and how to use the drawing options in Vty which seem very powerful if you go by the description. It would also be cool to see how you can better use NCurses and Vty in a more functional way rather than having do blocks everywhere like in my code.

I think curses and similar toolkits are pretty cool; there’s a lot to be said about complicated interfaces in text-mode terminals. Sure, you can make the next roguelike with those, but you can also use those libraries to facilitate your work, to make interesting, modal, multi-view user interfaces, and to create a user interface that is different the command line and geared specifically for the needs of a niche.

Idea: type-diff, a tool for managing code in Haskell and other type-inferred languages

I have just stumbled upon Michael Feathers' blog. If you haven’t read any of Michael’s stuff then let me assure you it is very good reading.

In one post, Michael makes a point about type inference and dynamic typing:

I came across [a blog post] this morning. It describes research into inferring types from production runs of dynamically typed programs. […] Anything we can do to make programming easier is good. In particular, typing type annotations seems suspect if we can generate them from runtime data. We can avoid premature design commitment and still reap benefits by post-annotating code in an IDE or generating on the fly static checkers.

This got me creative. Here’s my response in the comments:

Michael, I like the idea of using production runs to type code. However, here’s the thing: why do we assign types to our code? “To know what it does” breaks up into two sub-reasons that I can identify:

  1. when first writing code, it helps speed up development and makes it easy to define the general structure of our idea; if our structure sucks then the type checker will find out
  2. when maintaining code, it helps ensure that no one does something stupid that would change the way a lot of our code works. An example is if we have a program which accepts data, processes it to strings, and then does a lot of manipulation on those strings; then prints them.

block of data –> process into strings –> work on strings –> print string output

For example, calculate the frequency of strings, do some heavy stats on them, and then print them out.

At some point we have to fix the fact that in some huge file the back end crashes and change the input back end to spit out (Int, String)to get the line number that string was on in the file. Instead of String and now half our application is not passing around (Int, String) and the front-end doesn’t accept that anymore for printing.

In this situation you would see that the front-end is not adhering to a specific type and you might be compelled to change the front end; this is not the whole story however and the fix is probably to change the back end, given that the processor doesn’t care about the line number anyways.

This can be aided by some sort of meta SCM with knowledge of the language it’s storing; it could tell you how many of the inferred types have changed between two revisions; if it’s, statistically seen, too much (noticeably more than the average) then it could mean trouble; probably something you might want to fix.

The SCM would need to understand the structure of the language and keep track of the types. Not trivial — but not too hard if you’re into this sort of thing. A lot of people write parsers in “FP” languages so “FP” languages with type inference are more likely to get this sort of solution.

Does anyone else think this would be a good idea for boosting Haskell productivity? Right now people use types as unit tests; in fact the most repeated mantra is to stringently describe types of stuff that your module exports as a public API, but not to bother with doing that for the innards. I think that’s wrong — on the one hand, fixing the API with types limits reuse and with restrictive typing you can break things rather than fix things; on the other hand, you need to keep track when the “private” code breaks, as well. When it does break you’re usually looking for a specific commit or series of commits in your SCM that will have messed it up. Often, broken code means broken types are inferred. Such a diff, a type-diff kind of tool could be used to track down where this has happened; you could use the size of the delta coming from a type-diff tool as an interesting code metric.

Regarding strong typing of public APIs — it is my experience that giving more power to users is better than walling them in. Give the users the ability to influence the type being inferred; but give them an ability to figure out what breaks, too. With a type-diff kind of tool you could have both: the ability to make the types more flexible, but also the ability to see when incorrect use of your code messes up the types in that third-party module you’re using.

How to create a Haskell Hello World script, read input, print output, and manipulate file contents

Coming from an agile dev background, I like to do my development in scripts. I don’t like the whole process of having to compile my stuff and run it; at least one step too many. Additionally, things that are compiled are not portable. Here’s how to create a simple script.

This tutorial shows how to create an interpreted script for Haskell, how to do terminal input and output, how to work with output buffering, how to read from and write to files, how to make loops in Haskell and use the IO monad.

Seasoned Haskell users might be interested in the notion of constructive and declarative languages that I introduce.

This tutorial assumes that you have installed the Haskell Platform. If you’re using Windows, you might need to change some things (like how the interpreter is invoked). That part of the tutorial is mainly for people using GNU/Linux and similar systems, but the rest of it doesn’t change at all because Haskell is very portable.

If you are having problems at any time, give the #haskell channel on Freenode a go — they’re a very helpful bunch and the channel is active all the time. Feel free to drop comments here if something is unclear, wrong, or just doesn’t work.

The whole shebang

The GNU/Linux kernel contains an interpreter. It’s very simple and simple to write for, it is used for finding the right executor for the executable if it has a shebang. A countably infinite family of quines for the interpreter looks like this:

#!/bin/cat
(any other stuff here)

Haskell’s interpreter for scripts (at least when using the Haskell Platform) is called runhaskell. You might want to make the shebang #!/usr/local/bin/runhaskell but that would be wrong — the executable’s location is not guaranteed, and, in fact, it changes loads because people often use a Haskell Platform which does not come from their package manager, and use some sort of prefix when installing. There’s a trick to this:

#!/usr/bin/env your-interpreter

The above will find the interpreter in $PATH and will execute it. So our file initially looks like this:

#!/usr/bin/env runhaskell

How to make it execute code

When runhaskell is passed a script it looks for the function main and executes it. The simplest hello world would then look like this:

#!/usr/bin/env runhaskell
main = putStrLn "Hello, World!"

Save that as hello.hs, perform chmod a+x hello.hs and you’re good to go.

Variables

This is the first departure of declarative languages (Haskell, ML) from constructive languages (C, Python) that we notice. Haskell does not, in general, have variables as you know them from, say, Python. In constructive languages, a variable is a label which points to a mutable (changeable) cell that then points to a value. That’s what the Python people mean when they say “Strings are immutable”; in Python:

S = "Red"
# now, S points to the place in memory where "Red" is stored.
S = "Green"
# S has stopped pointing to the place in memory where "Red" is
# stored, and started pointing to the place in memory where
# "Green" is stored. However, "Red" might still be stored if
# there are other references to it. If S was the last label
# for "Red", then "Red" gets dumped from memory.

In Haskell, you can’t overwrite labels like in Python; you can, however, mask them:

(some scope here)
    (a block with some of the scope modified)
    (more of that block here)
    (yet more)
(back to the old scope)

You can do that with let:

-- on this line, x == "Red"
putStrLn x           -- prints "Red"
let x = "Green" in
    putStrLn x       -- prints "Green"
putStrLn x           -- prints "Red"

Variables in Haskell come from:

  1. Global definitions:
    x = "Hi"

    on a line of its own, unindented. This is the most basic scope in Haskell.

  2. Function arguments:
    hello x = "Hello, " ++ x ++ "!"

    This also applies to bindings in lambda expressions, the <– inside do, etc.

  3. The keyword let and its friends:
    x = "Hello!"
     let x = "Good-bye!" in
         putStrLn x

    This also applies to case and where.

Getting input

Alright, let’s make this thing interactive. You can read in stuff with getLine:

main = getLine >>= putStrLn

The >>= there, called bind, is a method of the typeclass called Monad. You don’t need to understand all that, but what you need to get out of it is that >>= can mean different things in different contexts. In our context, we’re talking about a specific >>= called “the >>= of IO”. What you need to know is that IO’s >>= works like this:

new_action = action >>= callback

That is, >>= takes an action, executes it, plugs the output into the callback, and assigns what came out of that to new_action. The previous sentence is a lie.

Working with programs here

You see, when doing IO stuff (that is, Haskell’s input-output functionality), you are operating on programs. And our action is one such program which gets executed on runtime; this program can be meshed with other programs to generate other programs. Our line new_action = action >>= callback was not actually executing the program called action; it just mashed that program with the callback, without ever executing it. The execution happened at runtime, when runhaskell got our program and performed it, not at compile time. So, to be more concrete:

main = getLine >>= putStrLn

the above code does not get a line and then print it; it creates a computer program which gets a line and then prints it. This program is then executed by our faithful runhaskell; only at this point do we actually get asked for input. The program we write using IO is just a description what could happen; possibly multiple times (as we will do later on to make “loops”) or no times (if, e.g., we end up not executing that specific branch of our application).

Adding programs together

We know that >>= takes a program and a callback, and returns a program; this callback is not a real program in its own right; it lacks semantics. You can also make a program out of two programs — execute two programs one after another using >>:

main = (getLine >>= putStrLn) >> (getLine >>= putStrLn)

This takes the two programs called “getLine >>= putStrLn” and runs them one after another. Basically, >> works like this:

new_action = old_action_1 >> old_action_2

The above definition of main will ask you for input, print it out, ask you for input, print it out, and then terminate.

Building values

In Haskell, you create new values by relating them to other values. In Python, you create new values by taking old values and doing things with them. This is the most basic difference between Haskell and Python; or, if we talk about the language families, you can say that this is the most basic difference between declarative languages (Haskell, ML, …) and constructive languages (Python, C, …).

A mathematician will understant if I say this is like the difference between synthetic and analytic geometry. Indeed, in synthetic geometry you say:

10    To get the bisector of a line segment,
20    find the first point of the line segment and call it A
30    find the second point of the line segment and call it B
40    measure the distance between A and B and call it r
50    draw a circle centered at A with radius r, call it C
60    draw a circle centered at B with radius r, call it D
70    find the points of intersection of C and D
80    take one of those points of intersection, call it E
90    take another one, call it F
100   place a pencil on E
110   place a straight edge so that it touches the pencil tip
120   watching out that the straight edge stays adjacent to the tip,
130       move it so that it also touches the point F
140   draw a line against the straight edge

In Python, it could look like this:

def line_bisector(line_segment):
    """ Gets you a line bisector.
        """
    ends = line_segment.ends()
    A = ends.pop()
    B = ends.pop()
    r = distance(A, B)
    C = circle(A, r)
    D = circle(B, r)
    intersections = intersect_sets(C, D)
    E = intersections.pop()
    F = intersections.pop()
    edge = StraightEdge()
    edge.fix_at(E)
    edge.fix_at(F)
    edge.draw_line()

Not so in Haskell. In Haskell, you define what things are, rather than how to get them. In other words, you declare rather than construct. A Haskell version of this program could look like this in pseudocode:

lineSisector line_segment = lineThrough(
    intersectionOf(
        circlesAtEnds(
            line_segment
            (lenghtOf line_segment)
            )))

circlesAtEnds line_segment radius =
    circlesAtPoints (endsOf line_segment), radius

-- endsOf is just an example, it does not get talked about,
-- just like line_segment.ends() did not get talked about in
-- the Python code

circlesAtPoints (x, y) r = (circle x r, circle y r)

lenghtOf segment =
    distance (fst (endsOf segment)) (snd (endsOf segment))

-- it is intuitive what circle does, and what intersectionOf
-- and lineThrough do; their counterparts are not explained
-- in the Python code, either.

This looks very similar to what you would say in analytic geometry:

L = l(⋂ {S(P, |line_segment|) : P an end of line_segment})

l({x_n}) = line going through x_n for all defined n

⋂ {E_n} = {x : ∀ n, x ∊ E_n }

|segment| = d(x, y) where x and y are ends of segment

Even without understanding the mathematical notation, you can look at the Haskell pseudocode above and notice that we have always defined things in terms of other things, without doing stuff with those other things. See it as the difference between an engineer making a blueprint of how a house should be, and the construction workers laying cement, bricks, and mortar. In the end, as a result, we get a house; the second way is much more long-winded and not really necessary just to demonstrate a house; a blueprint is good for our purposes.

Looping stuff

If we want to repeat this over and over we need something akin to a loop. I can only guess where the name loop came from in the stone-age of computing where you wrote your programs in stone tablets — or perforated cards and tape. You could probably stick the ends of your perforated tape together in order for your calculating machine to perform the same action over and over. I remember once tacking together pieces of paper and feeding them to a printer in a loop; the printer transport broke after several rounds; I assume tape loops would have been a very difficult mechanical contraption which would therefore cost a lot of money and would therefore be very desirable and hailed as the advent of new computing.

To cut on cost you could learn how to rewind tape to the beginning; this is much simpler, since the tape does not need to be kept in a loop that requires tension adjustment and a holding cartridge and rollers; you just reverse the direction of the engine (driver — like graphic card driver) in your tape drive, and you run it until you somehow figure out you are back to the beginning of the tape. For example, you can detect this with a mechanical object that is at the beginning of the tape and touches a switch that signals “this is it”.

Contemporary computers still operate on tape; it might be billions of times faster than the original, a solid-state cache, but it’s still tape. The file system is a tape, the RAM is a tape, the Internet is a tape. This is reflected by languages adhering to this tape logic. Loops in C and Python and Java and PHP are best illustrated in the best of all such languages, BASIC:

10    PRINT "HELLO"
20    PRINT "I AM A LOOP ITERATION"
30    PRINT "I AM DONE, HAVE A NICE DAY!"
40    GOTO 10

Haskell does not have this concept (it has something different, maybe better). Sure, you can have loops in Haskell just like you can have recursion in Python; it’s advised against. It’s unnatural, and, in the case of Python, it just plain doesn’t work. It is unnatural in the same way that using English words when speaking German sounds stupid; in the same way that wearing trousers backwards doesn’t work out. You can if you want to, but it’s still stupid.

Haskell’s idea of “things are compositions of other things” applies again; instead of a loop instruction, in Haskell we define something that repeats itself in this way:

something that repeats is some job plus something that repeats

Or, to use pseudocode:

something_that_repeats = something + something_that_repeats

Above, the something is our iteration; if we unroll this once, we get:

something_that_repeats = something + something + something_that_repeats

If we unroll this again, we get:

something_that_repeats = something + something + something + something_that_repeats

And so on. Here are some examples:

shout = "A" ++ shout
-- this gives us an infinite string: "AAAAAAAAAA..."

rising x = [x] ++ rising (x+1)
-- rising 1 will give us an infinite list: [1, 2, 3, ...]

shout2 = "A" ++ shout2 ++ "A"
-- this gives us the string faster ;-)

shout3 = "AAAAAAA" ++ shout3 ++ "AAAAAAAAAAAAAAA"
-- max optimization ;-)

This is, as you probably know, called recursion.

Looping our program

Let’s look at our short Haskell application again:

#!/usr/bin/env runhaskell
main = (getLine >>= putStrLn) >> (getLine >>= putStrLn)

In the same way, we can make our program recursive; just replace the second action on the right of >> with the whole thing:

#!/usr/bin/env runhaskell
main = (getLine >>= putStrLn) >> main

The above program will ask for input and print it out again, over and over, until we kill it.

#!/usr/bin/env runhaskell
main = (getLine >>= putStrLn) >> main >> main

The above will work too.

This will, however, not work:

#!/usr/bin/env runhaskell
main = main >> (getLine >>= putStrLn) -- doesn't work

This is because, when constructing an infinite value (list, tree, etc), we should not recurse infinitely when defining one element of it; here, the first job that main would be doing is defined like that. This is called “head recursion”.

This can be illustrated easier in this way:

quiet = quiet ++ "A"

What is the first character of quiet? We don’t really know.

We can also restructure our program to look more like a loop, in this way:

iteration = getLine >>= putStrLn
main = iteration >> main

Let us also comment on our code a bit; at two lines it might already be too complex for some to understand:

iteration = getLine >>= putStrLn
-- echoes what we type in

main = iteration >> main
-- The main program

Finally, we can introduce do notation; this is a special notation for IO which wraps around >>= and makes our IO actions more legible. This exposes IO for what it really is: simulation of a constructive language inside the declarative language Haskell. We use do like this:

#!/usr/bin/env runhaskell
iteration = do
-- echoes what we type in
    x <- getLine
    putStrLn x

main = iteration >> main
-- The main program

Notice that we have assignments here with the <– operator. This is exactly equivalent to the previous version of iteration. The Freenode IRC channel #haskell has a very nice bot called lambdabot with a few useful commands. You can use them in the channel, or in a query with lambdabot. One such command is @undo, which we can use to unwrap do notation:

@undo iteration = do x <- getLine; putStrLn x
iteration = getLine >>= \x -> putStrLn x

Now to understand this you need to know a bit about lambda calculus. The short version is, that the function:

f x = putStrLn x

can also be expressed as

f = \x -> putStrLn x

Think of \, (or lambda, or 𝜆 as it is called in other languages) as unshifting a value off the argv in C, COBOL, Python, asm, or PHP. Now the above is exactly equivalent to:

f = putStrLn

since the two functions take the same kinds and numbers of arguments.

More functionality

We might now want the program to do some more things; for example, let’s have it greet people:

#!/usr/bin/env runhaskell
iteration = do
    -- Greets someone
    putStrLn "Please enter your name..."
    name <- getLine
    putStr ("Hello, " ++ name ++ ". ")
    putStrLn "Pleased to meet you!"

main = iteration >> main
-- The main program

This will naturally echo out greetings. The putStr function prints out a string, and putStrLn prints it out and moves to a new line. Let’s give it a prompt; we want the line on which we will be entering to start with “> ”:

#!/usr/bin/env runhaskell
iteration = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    name <- getLine
    putStr ("Hello, " ++ name ++ ". ")
    putStrLn "Pleased to meet you!"

main = iteration >> main
-- The main program

Oops! This doesn’t work quite as expected. The output we get is:

$ ./guess.hs 
Please enter your name...
Bob
> Hello, Bob. Pleased to meet you!
Please enter your name...

Some of you might have figured out that Haskell only flushes output when a new line is printed; we need to explicitly flush the output with hFlush. It is, however, not loaded at startup; we must import the module it is in, System.IO:

import System.IO

This makes our namespace messy, though; it dumps all the stuff that’s inside System.IO directly into the global context and if we try to go back to source code that does that with umpteens of modules, then, after ten years (or ten weeks), we can’t figure out what came from where. Instead, let’s use a qualifier:

import qualified System.IO as I

Now we can use hFlush. It takes the stream to flush as a parameter; System.IO.stdout is one such stream.

#!/usr/bin/env runhaskell
import qualified System.IO as I

iteration = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    I.hFlush I.stdout
    name <- getLine
    putStr ("Hello, " ++ name ++ ". ")
    putStrLn "Pleased to meet you!"

main = iteration >> main
-- The main program

This works perfectly!

Now, let’s make this script react to certain people. First, let’s define some names:

known_friends = ["Don", "Simon", "Eduard", "Philippa"]
-- This program knows those people

Then, let’s make a function that checks if the person is known:

known name = any (== name) known_friends

The above requires some explanation. First of all, the syntax may be confusing. The first word on the line is known, the name of the new value (new function). Then come the names of the parameters, if any. Then we have an equals sign (=); then comes the code, the body of, the function. Second, any is a function that takes a comparison and a list, and returns a logical value. The comparison should take one parameter, and return a logical value. Third, (== name) is a comparison function just like we need; in Haskell, everthing is a function, and so is ==; however, == is a function of two parameters, and our comparison needs to take one parameter and evaluate to a truth value. We can manipulate functions using partial application (also known as currying) in order to get functions of less variables; this way we got from the function == which takes two variables to a function which just takes one variable and is called == name. If we wanted to compare with just one name, we could have also written it like this:

isPhilippa name = "Philippa" == name
-- tells us whether a name is equal to "Philippa"
philippaKnown = any isPhilippa known_friends

However, we would want to parametrize who we are checking for:

isSomeone someone name = someone == name

Then, we need to partially apply the function:

isPhilippa name = isSomeone "Philippa" name

What we have just done is to create a function called isPhilippa, which is isSomeone with its first parameter, someone, fixed to the value “Philippa”. However, this is just the same as:

isPhilippa = isSomeone "Philippa"

The above version uses so called “point free” syntax; it is used in mathematics to define functions in terms of other functions without actually passing around the point at which those two functions interface. In other words, we can write something like:

ctg = 1/tan

Which has the benefit that, if we use notation saying that e.g. r is a real number and z is a complex number, then we’d have to define the functions first for real numbers:

ctg r = 1/(tan r)

…and then for complex numbers, once we figure out how to extend our trigonometric functions to the complex plane:

ctg z = 1/(tan z)

This looks stupid because it’s the same thing. Point-free notation avoids this sort of situation.

Generally, if we have code of the form:

some_function param1 param2 last_param = another_function "foo" last_param

— that is, the past parameter of the left function is passed as the last parameter of the right function — then we can just lop it off both sides. It’s kind-of like simplifying equations in mathematics. Argument reduction:

some_function param1 param2 last_param = another_function "foo" last_param
-- is equivalent to:
some_function param1 param2 = another_function "foo"

Multiplicative reduction:

sin x * 2 = a*x * 2  -- /divide by 2
-- is equivalent to:
sin x = a*x

Argument reduction works only when the argument being removed is the furthest right.

Regarding our functions checking for Philippa, we could now do something like:

isSomeone someone name = someone == name
isPhilippa = isSomeone "Philippa"
philippaKnown = any isPhilippa known_friends

However, this is just the same as:

isSomeone someone name = someone == name
philippaKnown = any (isSomeone "Philippa") known_friends

Now, we can rewrite the definition of isSomeone:

isSomeone someone name = (==) someone name
philippaKnown = any (isSomeone "Philippa") known_friends

This changes == from being an infix operator to being a normal function. We can now remove a parameter from the definition, making the code more point-free:

isSomeone someone = (==) someone

And then another one:

isSomeone = (==)

…so in the end, we have:

philippaKnown = any ((==) "Philippa") known_friends

Now we can parametrize the name:

known name = any ((==) name) known_friends

or just:

known name = any (== name) known_friends

Note that, technically, the last line above fixes the second argument, whereas the lines before that fixed the first parameter of ==. Makes no difference this time around, though.

OK, let’s go back to our program. We want it to check if he knows the person in question; and we want it to change the greeting. Here’s what we have written until now:

#!/usr/bin/env runhaskell
import qualified System.IO as I

known_friends = ["Don", "Simon", "Eduard", "Philippa"]
-- This program knows those people

known name = any (== name) known_friends
-- Tells us if a person is known

iteration = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    I.hFlush I.stdout
    name <- getLine
    putStr ("Hello, " ++ name ++ ". ")
    putStrLn "Pleased to meet you!"

main = iteration >> main
-- The main program

Time to add an if clause:

#!/usr/bin/env runhaskell
import qualified System.IO as I

known_friends = ["Don", "Simon", "Eduard", "Philippa"]
-- This program knows those people

known name = any (== name) known_friends
-- Tells us if a person is known

iteration = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    I.hFlush I.stdout
    name <- getLine
    if known name
        then putStrLn ("Hello, " ++ name ++ "! How have you been?")
        else putStrLn ("Pleased to meet you, " ++ name ++ "!")

main = iteration >> main
-- The main program

Remembering people — how to return from a do block

In order for iteration to remember the people it is greeting, we need to be able to change what list of friends it operates on. You cannot append to lists in Haskell. You can, however, pass in different lists of friends. First, let’s make the friends get passed around in a loop. Let’s change:

main = iteration >> main

to:

loop friends = iteration >> loop friends
-- Loops the whole thing.

main = loop known_friends
-- The main program

Now we need to make iteration take friends as a parameter, which we will pass down to known.

#!/usr/bin/env runhaskell
import qualified System.IO as I

known_friends = ["Don", "Simon", "Eduard", "Philippa"]
-- This program knows those people

known name friends = any (== name) friends
-- Tells us if a person is known

iteration friends = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    I.hFlush I.stdout
    name <- getLine
    if known name friends
        then putStrLn ("Hello, " ++ name ++ "! How have you been?")
        else putStrLn ("Pleased to meet you, " ++ name ++ "!")

loop friends = iteration friends >> loop friends
-- Loops the whole thing.

main = loop known_friends
-- The main program

Then, we need to make iteration yield a value. Let’s first change our loop to do notation before we do that:

loop friends = do
-- Loops the whole thing.
    iteration friends
    loop friends
main = loop known_friends
-- The main program

Now, let’s have iteration yield a value. The return value of a do block is the result of the last action in it; we can just put a getLine at the end of our iteration:

iteration friends = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    I.hFlush I.stdout
    name <- getLine
    if known name friends
        then putStrLn ("Hello, " ++ name ++ "! How have you been?")
        else putStrLn ("Pleased to meet you, " ++ name ++ "!")
    putStrLn "Please enter a name to remember as a friend..."

…and then use the result of that and append it to our list of friends:

loop friends = do
-- Loops the whole thing.
    new_friend <- iteration friends
    loop ([new_friend] ++ friends)

However, Haskell has a special operator for prepending to lists, strings, etc that we can use:

loop (new_friend:friends)

We might just want our program to remember people who it greeted; we can’t do that right now because we get the name of the person greeted at the top of the iteration, but it needs to be the result of the last line of the do block! We therefore need to somehow take that result, called name, and make it the result of another computation. For this, we can use the return function. It’s a method of every monad; in the IO monad it’s kinda like a noop or an identity function in that it takes a computation result, and makes it the result of the computation we are defining. Thus, we can do:

iteration friends = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    I.hFlush I.stdout
    name <- getLine
    if known name friends
        then putStrLn ("Hello, " ++ name ++ "! How have you been?")
        else putStrLn ("Pleased to meet you, " ++ name ++ "!")
    return name

…and our script can now remember us!

Using Just as out-of-band communication

Right now our script can add names to the list; however it will add duplicates. We want to prevent this. We could use a sentinel value; if that shows up we don’t add it. For example:

iteration friends = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    I.hFlush I.stdout
    name <- getLine
    if known name friends
        then do
            putStrLn ("Hello, " ++ name ++ "! How have you been?")
            return ""
        else do
            putStrLn ("Pleased to meet you, " ++ name ++ "!")
            return name

loop friends = do
-- Loops the whole thing.
    new_friend <- iteration friends
    if new_friend == ""
        then loop friends
        else loop (new_friend:friends)

Notice two things. Firstly, we are using return inside then and else. The value being returned percolates up to the if and becomes its value; since the if is the last thing in our do block, it’s the return value of the do block.

Secondly, notice that we had to wrap the bodies of the then and else in do blocks themselves. The body of a then or else must be a single value; that value is the resultant value of the whole if clause, depending on what branch is chosen. A do block is just a single value; do composes multiple values together and exposes the one on the last line as its value.

So, if we are in the else, our return name bubbles up to the do it’s immediately inside, then to the else that do is inside, then to the if. Since the if is the last ting in the big do block, that return name bubbles up to that, and finally becomes the return value of iteration.

To see that everything works the way we imagine it does we can use the print function. It takes any value and prints out a representation of it to stdout; it’s useful for debugging.

loop friends = do
-- Loops the whole thing.
    print friends
    new_friend <- iteration friends
    if new_friend == ""
        then loop friends
        else loop (new_friend:friends)

Here is our whole program until now:

#!/usr/bin/env runhaskell
import qualified System.IO as I

known_friends = ["Don", "Simon", "Eduard", "Philippa"]
-- This program knows those people

known name friends = any (== name) friends
-- Tells us if a person is known

iteration friends = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    I.hFlush I.stdout
    name <- getLine
    if known name friends
        then do
            putStrLn ("Hello, " ++ name ++ "! How have you been?")
            return ""
        else do
            putStrLn ("Pleased to meet you, " ++ name ++ "!")
            return name

loop friends = do
-- Loops the whole thing.
    print friends
    new_friend <- iteration friends
    if new_friend == ""
        then loop friends
        else loop (new_friend:friends)

main = loop known_friends
-- The main program

What we are doing here is called “in-band signaling”; this is a term which comes from the world of radio, information theory, and communication. It means selecting a special value of our type, and using it with additional semantics. Naturally, no one will have a name which consists of the empty string; However, it’s not so great to use in-band signaling if our values are less predictable; for example, how do we use in-band signaling when returning a number? We could use some huge number, but that breaks when someone uses that specific number; bad idea. Additionally, other programmers who use our function might not be aware of those special semantics; this is summed up by stating that the semantics of a value should be encoded in its type. That is, a number is a number, and a string is a sting, and that’s all. Full stop. Finito.

To do this properly, we can use out-of-band signaling. In some languages we could throw an exception; Haskell can do that, but let’s not do that just yet. We could also return a pair, where the first element is the status, and the second element is the actual value being returned. That’s stupid, though; what do we return as the actual value if we don’t want to return anything? The implementation of our type’s semantics could leak out to the user code of the users still keep on relying on that as a sentinel value. Finally, many languages allow you to return None, Null, or False in place of the string; it’s a form of polymorphism. Haskell’s functions can only return one type; if they return a string in one place, it has to be a string everywhere else too or the program won’t even compile. The good thing is we can create new types in Haskell. Kinda how classes in many object oriented languages end up being used, except Haskell types don’t have the inheritance drama; additionally, they’re just as good as str in Python or int in C++ and aren’t second-category citizens like classes in those languages are. One way to create a special type is using the keyword Maybe.

Before we go on, let’s transform our loop function a bit:

loop friends = do
-- Loops the whole thing.
    print friends
    new_friend <- iteration friends
    case new_friend of
        "" -> loop friends
        _ -> loop (new_friend:friends)

The case clause is like a cooler version of if; you know it from some constructive languages as well. It allows us to have multiple branches and match against values; additionally we can do pattern matching. The first case which is matched is used; further cases are not inspected. The final option has a pattern of _. A pattern of the form x just matches everything. It is used as a catch-all in case the previous options didn’t match.

Now let’s use Maybe to do some out-of-band signaling. There are two out-of-band values for Maybe: Just and Nothing. You use Just by making your return value Just whatever_value; you use Nothing by simply making your return value Nothing. Using Nothing is much like using Null in Java, None in Python or 0 or -1 in C, but without the problems. Let’s convert iteration; our current version is:

if known name friends
        then do
            putStrLn ("Hello, " ++ name ++ "! How have you been?")
            return ""
        else do
            putStrLn ("Pleased to meet you, " ++ name ++ "!")
            return name

We change it to:

if known name friends
        then do
            putStrLn ("Hello, " ++ name ++ "! How have you been?")
            return Nothing
        else do
            putStrLn ("Pleased to meet you, " ++ name ++ "!")
            return (Just name)

Our signal has changed, so we need to change not only the place we emit it, but also where we receive it:

loop friends = do
-- Loops the whole thing.
    print friends
    new_friend <- iteration friends
    case new_friend of
        Nothing -> loop friends
        Just new_friend -> loop (new_friend:friends)

Notice two cool things. Firstly, we barely changed anything, but this change gives the compiler the ability to reason about our code in a much more advanced way. Secondly, notice that this time we have made a more involved pattern match; it’s Just new_friend. It gives us direct access to new_friend as a variable. You can match against all sorts of structures which is very powerful; this allows you to extract values that are buried deep inside the value you are unpacking. Here is our whole program until now:

#!/usr/bin/env runhaskell
import qualified System.IO as I

known_friends = ["Don", "Simon", "Eduard", "Philippa"]
-- This program knows those people

known name friends = any (== name) friends
-- Tells us if a person is known

iteration friends = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    I.hFlush I.stdout
    name <- getLine
    if known name friends
        then do
            putStrLn ("Hello, " ++ name ++ "! How have you been?")
            return Nothing
        else do
            putStrLn ("Pleased to meet you, " ++ name ++ "!")
            return (Just name)

loop friends = do
-- Loops the whole thing.
    print friends
    new_friend <- iteration friends
    case new_friend of
        Nothing -> loop friends
        Just new_friend -> loop (new_friend:friends)

main = loop known_friends
-- The main program

Persistence using IO — reading from and writing to files

So we can make our script learn new friends! However, every time it terminates its brain is reset. Let’s try storing that information somewhere. But first, we need to be able to read it. You may know Python’s with keyword; it creates a new context within which e.g. a file handle is available. Haskell has something similar for files; it’s called withFile and, as everything, it is a function. Instead of creating a block or context, withFile takes a callback as an argument, which it then executes with the handle as the parameter to that callback. Before we apply it, let’s transform our current code a bit; change:

main = loop known_friends
-- The main program

to:

main = do
-- The main program
    friends <- return known_friends
    loop friends

Not really a change at all. We are still passing known_friends to loop. If you don’t know why we’re using return here, read above where we introduce return for the first time. Let’s split the initialization into a separate function:

start = do
-- Initializes the program
    friends <- return known_friends
    loop friends

main = start
-- The main program

Trivial. Now, however, start can act as our callback. We get withFile from System.IO which we already import in our script.

start conf = do
-- Initializes the program
    friends <- return known_friends
    loop friends

main = I.withFile "friends.conf" I.ReadWriteMode start
-- The main program

It’s easy to see that the first parameter is the file name, the second is the opening mode (kinda like in fopen(3), see man fopen), and the third is the callback that makes use of the new file handle. Let’s get the contents:

start conf = do
-- Initializes the program
    contents <- I.hGetContents conf
    print contents
    friends <- return known_friends
    loop friends

We are printing the contents for debugging purposes. Seems to be working! Try it with a file yourself. Ideally, we would first rewind the handle, because our function doesn’t really know where the handle comes from; it might be dirty; you don’t know where that handle was.

start conf = do
-- Initializes the program
    I.hSeek conf I.AbsoluteSeek 0
    contents <- I.hGetContents conf
    print contents
    friends <- return known_friends
    loop friends

Now, let’s use the contents of the file:

start conf = do
-- Initializes the program
    I.hSeek conf I.AbsoluteSeek 0
    contents <- I.hGetContents conf
    print contents
    friends <- return (lines contents)
    loop friends

We have used the lines function, which takes a string, and splits it on newlines into a list of strings. This is exactly what we want. We can simplify our code:

start conf = do
-- Initializes the program
    I.hSeek conf I.AbsoluteSeek 0
    contents <- I.hGetContents conf
    print contents
    loop (lines contents)

OK, so our program is configurable now, but it doesn’t store data yet. Let’s get about to doing that now. We need to write to our handle, and the best place to do that is inside loop, since that’s where we figure out if a name is new or not. We first need to pass the parameter:

in start, change:

loop (lines contents)

to:

loop (lines contents) conf

and in loop, change:

loop friends = do
-- Loops the whole thing.
    print friends
    new_friend <- iteration friends
    case new_friend of
        Nothing -> loop friends
        Just new_friend -> loop (new_friend:friends)

to:

loop friends conf = do
-- Loops the whole thing.
    print friends
    new_friend <- iteration friends
    case new_friend of
        Nothing -> loop friends conf
        Just new_friend -> loop (new_friend:friends) conf

We also need to position the handle correctly; we want it to be at the end, and we don’t want to depend on the semantics of side-effects:

loop friends conf = do
-- Loops the whole thing.
    I.hSeek conf I.SeekFromEnd 0
    print friends
    new_friend <- iteration friends
    case new_friend of
        Nothing -> loop friends conf
        Just new_friend -> loop (new_friend:friends) conf

Uh-oh, this doesn’t work! As it turns out, the handle is closed already at this point; this happened in hGetContents; but we want to keep appending. We need to figure out a different way to get the contents. Let’s write a function which takes a handle, and returns all the lines in a list:

parseConf conf = do
    -- Parses the config file given a handle.
    eof <- I.hIsEOF conf
    if eof
        then return []
        else do
            line <- I.hGetLine conf
            rest <- parseConf conf
            return (line:rest)

Let’s also make start use the thing:

start conf = do
-- Initializes the program
    I.hSeek conf I.AbsoluteSeek 0
    friends <- parseConf conf
    print friends
    loop friends conf

That works! Let’s go back to hacking loop: we just need to write to the handle and we should be good.

loop friends conf = do
-- Loops the whole thing.
    I.hSeek conf I.SeekFromEnd 0
    print friends
    new_friend <- iteration friends
    case new_friend of
        Nothing -> loop friends conf
        Just new_friend -> do
            I.hPutStrLn conf new_friend
            loop (new_friend:friends) conf

Give it a try — it works! The program writes to the file and reads back from it when restarted; its digital identity is persistent. Here is the whole program as it should be:

#!/usr/bin/env runhaskell
import qualified System.IO as I

known_friends = ["Don", "Simon", "Eduard", "Philippa"]
-- This program knows those people

known name friends = any (== name) friends
-- Tells us if a person is known

iteration friends = do
    -- Greets someone
    putStrLn "Please enter your name..."
    putStr "> "
    I.hFlush I.stdout
    name <- getLine
    if known name friends
        then do
            putStrLn ("Hello, " ++ name ++ "! How have you been?")
            return Nothing
        else do
            putStrLn ("Pleased to meet you, " ++ name ++ "!")
            return (Just name)

parseConf conf = do
    -- Parses the config file given a handle.
    eof <- I.hIsEOF conf
    if eof
        then return []
        else do
            line <- I.hGetLine conf
            rest <- parseConf conf
            return (line:rest)

loop friends conf = do
-- Loops the whole thing.
    I.hSeek conf I.SeekFromEnd 0
    print friends
    new_friend <- iteration friends
    case new_friend of
        Nothing -> loop friends conf
        Just new_friend -> do
            I.hPutStrLn conf new_friend
            loop (new_friend:friends) conf

start conf = do
-- Initializes the program
    I.hSeek conf I.AbsoluteSeek 0
    friends <- parseConf conf
    print friends
    loop friends conf

main = I.withFile "friends.conf" I.ReadWriteMode start
-- The main program

We have learnt how to get keyboard input and do terminal output, we have learnt how to read from and write to files. This should get you guys started doing some practical things with Haskell as a general systems language. It’s very powerful for that, especially given how portable it is. I hope you liked this tutorial; there are probably errors in it — if you notice any, please post! This tutorial might get changed a bit or enhanced if new ideas come up.

The Haskell community (specifically the #haskell IRC channel on Freenode) has been very helpful in creating this tutorial; they have at some point explained literally every single Haskell thing in this document! Really, there’s little more you could wish for; those guys rule.

Talk: Why learn Haskell? (+ slides)

My dear friend and acquaintance Keegan McAllister is going to give a talk on Haskell in Boston’s MIT CSAIL, geared towards the often-asked question: why learn it?. Here’s a short abstract in his own words:

Why learn Haskell?

Tired of programming languages that just rehash old concepts with new syntax? Haskell is a truly unique language that will expand your mind and show you new ways to solve problems. We’ll take a whirlwind tour of Haskell features, from simple to sophisticated, foundational to practical. We’ll see how Haskell offers a better way to write software, be it pure computation or concurrent transactional network code. I won’t get bogged down in details of syntax, or waving my hands about the ineffable beauty of functional programming. These are great topics to study on your own. This talk will give you some compelling, concrete reasons to do so.

Indeed, sounds exciting! This is meant for complete outsiders to the realm of Haskell and functional programming; apparently it should even be intelligible to complete newcomers to programming in general, so don’t worry if you think you’re not ready for that functional programming thing.

It’s in similar vein to SPJ’s talk A taste of Haskell (slides, video 1, video 2) from 2007; Haskell has progressed a lot since. Keegan’s talk is much more terse and to the point; Simon’s talk is long and a beautiful introduction if you have the time to invest (there’s a lot of stuff there). Naturally, a complete — and very fun! — introduction to Haskell can be found at Learn You a Haskell (contents) which is also available in book form.

Where: MIT CSAIL, When: Sept. 29 2011 ALSO: Oct. 11, MIT 2-143

The event starts at 19:00 (7 PM for the lot of you) and is the BAHUG; it will also have a talk on version control from the viewpoint of functional programming languages after Keegan’s. It will be happening this Thursday, September 29, 2011, in the MIT CSAIL Reading Room. If you are new to Haskell you are sure to find someone in the audience (or Keegan after the talk) to chat to and learn some basics. There will be free refreshments and apparently one of the sponsors is bringing FREE PIZZA!!! Sound good? Now if I were only on the same continent :)

The second instance of the talk will happen on Tuesday, the 11th of October 2011, again at MIT, in 2-143 (which decrypts to room 143 of building 2 at MIT). The post-talk chat is always involving and always different, so it’s worth attending both.

SLIDES!!!

Slides for Keegan’s talk are now available!

If you decide to go, please post a reply to the mailing list or as a comment here to signal a rough idea of the head count; just showing up works too, of course.

Also check out Keegan’s blog. It’s got some fresh stuff for those interested in concurrent programming, some introductory stuff on Haskell, and non-Haskell-related Linux hacking (Keegan was a dev a ksplice so obligatorily he has a lot of experience with this sort of thing). I have found the post Typing mathematical characters in X very useful.