Writing Haskell Functions With Many Nameless Parameters

No Comments Written by Etienne Laurin on 2011/10/03 in Code.

Although it could be considered bad style, it is often useful to have functions that take a large amount of similar parameters that vary in type. I am going to demonstrate a technique that allows to write such functions without naming these parameters and making the function more readable.

The function I will use for demonstration is an example of a common pattern when working with JSON data.

{-# LANGUAGE UnicodeSyntax, OverloadedStrings #-}

import Data.Aeson
import Data.Aeson.Types
import Data.Text

person  String  Float  Bool  Value
person name height over18 = object [
  "name" .= name,
  "height" .= height,
  "over18" .= over18]

The name of each parameter is repeated three times. Writing many such functions is often done by copying and pasting, which is a red flag and a possible source of errors.

In an attempt to write reusable code, we can define a simple function that builds the result in small steps.

field  ToJSON a  Text  [Pair]  a  [Pair]
field k d v =  (k, toJSON v) : d

The order of the arguments is important because it fits well into the $- combinator. This combinator is the key to the whole exercise. It builds our desired multi-argument function without having to name the arguments.

infixr 3 $-
($-)  (d  a  e)  (e  k)  d  a  k
($-) f g d a = g $ f d a

(Some readers may recognise this relative of the infamous cartoon face operator ((.).(.)))

We can now rewrite the person function.

person'  String  Float  Bool  Value
person' = ($[])
  $  field "name"
  $- field "height"
  $- field "over18"
  $- object

The first part of the function body, ($[]), is the initial value used to build our object. The last part, $- object transforms the list that was built incrementally into the final result.

This technique becomes more powerful when you write other helper functions. For example, we can also have another function that ignores Nothing.

optfield  ToJSON a  Text  [Pair]  Maybe a  [Pair]
optfield k d (Just v) = field k d v
optfield _ d Nothing = d

The $- combinator’s usefulness is not limited to building JSON objects. It can be used to write many functions that need to combine heterogeneous values for which simple utility functions such as field and optfield can be written. Functions written in this style make cleaner, less repetitive code.


EDIT: made the type of $- more general


Visualise Persist Models Using Graphviz

No Comments Written by Etienne Laurin on 2011/09/14 in Code.

My current project uses the Yesod framework for web development. One of the advantages of Yesod is the Database.Persist library. It has type-checked queries, custom sql representations for types and automatic schema migration with postgresql, sqlite and mongodb support.

Here is a little script I wrote to visualise Persist data models.

persist-graph.hs

{-# LANGUAGE UnicodeSyntax, ViewPatterns #-}

-- usage:
--  persistent-graph < config/models > schema.dot
--  neato schema.dot -Tpdf > schema.pdf

import Database.Persist.Base (EntityDef(..), ColumnDef(..))
import Database.Persist.Quasi (parse)
import Data.List (intersperse)

main = do
  defs  getContents
  let schema = parse defs
  putStr $ convert schema

graphOpts = "node [shape=record]; overlap=false; splines=true;"

convert schema = unlines ["digraph {", graphOpts, unlines $ map entity schema, "}"] 

entity (EntityDef name _ cols _ _) = unlines $ [
  name ++ " [",
  "label=\"{" ++ name  ++ "|" ++
  (concat $ intersperse "|" (map column cols))
  ++ "}\"];"] ++
  map (links name) cols

column (ColumnDef name _ _) = "<" ++ name ++ "> " ++ name

links entity (ColumnDef name typ _) =
  if "dI" == take 2 (reverse typ)
  then entity ++ ":" ++ name ++ " -> " ++ reverse (drop 2 (reverse typ))
  else []

#define true false

One Comment Written by Etienne Laurin on 2011/03/16 in Code.

What is the output of the following C++ program?

#include <iostream>

#define true false
#define false true

int main(){ std::cout << (
        false ? "false" :
        true  ? "true"  :
                "mu"
) << "\n";}


Cheating

No Comments Written by Etienne Laurin on 2010/11/21 in Code, Uncategorized.

I haven’t posted anything here in a long time, so I thought I might share a small log from freenode.


* s0lidnuts has joined ##prolog
< s0lidnuts> Hi. I'd like a commented version of the solution to the 8 queen problem. I do not know prolog nor have time to learn it now (school work). Anyone willing to help please pvt =)
< AtnNn> s0lidnuts: eight_queens([2,4,6,8,3,1,7,5]).
< s0lidnuts> AtnNn that's cheating

Somehow, this made me laugh more than anything else has this past week.


Non-Incremental Search in Emacs

No Comments Written by Etienne Laurin on 2010/09/15 in Code.

Do you sometimes find it annoying when your cursor jumps around the page as you search a large file? Looking for a solution, I discovered Emacs’ non-incremental search mode.

It is triggered by pressing Enter after C-s:

C-s <RET> string <RET>

And for backwards search:

C-r <RET> string <RET>

But after pressing Enter again, the cursor jumps to the text that Emacs found. To go back, use the double shortcut:

C-x C-x

If you use transient-mark-mode, you will want to regroup the mark and point with

C-<SPACE> C-<SPACE>

Happy hacking!


Handwriting Recognition with CellWriter on the N900

2 Comments Written by Etienne Laurin on 2010/04/18 in Code, Software.

For the adventurous, the source and package are available on the project page


Simple Command Line Network Monitor For Linux

One Comment Written by Etienne Laurin on 2010/02/19 in Code.

Here is a small shell script that displays the amount of data your computer is downloading. The numbers indicate the approximate bytes per second.

#!/bin/bash

i=eth1
n=1

if [ "$1" != "" ]; then i="$1"; fi
if [ "$2" != "" ]; then n="$2"; fi

width=`stty size | cut -f 2 -d ' '`
max=`expr $width - 10`
startt=`date +%s`

a=`cat /sys/class/net/$i/statistics/rx_bytes`
b=$a
while sleep $n; do
	v=`expr $a - $b`
	if [ $v -gt $max ]; then
		max=$v
		echo -n '^[[1m'
	fi
	printf '%8s ' $v
	echo -n '^[[1m'
	seq -s = 0 `dc -e "$v $width 9 - * $max / p"` | sed 's/[^=]//g;'
	echo -n '^[[0m'
	b=$a
	a=`cat /sys/class/net/$i/statistics/rx_bytes`
done

Download source