Haskell in Q

My new job will involve coding in Q. Q is a scripting language derived from APL. It is mostly used for stuff related to trading and finance. I don’t know it yet, I’m supposed to learn it on the job, so I decided to have a look at it.
As an exercise, I took a couple of Haskell functions in the standard Data.List module and tried to see how one can express them in Q. Experienced Q developers (called “Q gods” in the Q terminology) will probably laugh at my code, but well, I have to start from something. Any corrections and idioms from Q gods are welcome.

Here are a couple of trivial functions that are special cases of builtin Q functions

init: {-1_x} 

tail: {1_x}  

head: {x[0]}

Q is a functional language. It allows to take functions as parameters in functions. Here the takeWhile function takes a predicate as its first parameter. The code is probably too imperative for Q, I am sure there is a better way.

takeWhile:{[p;xs]
           i:0;
           while[(i < count xs) & p[xs[i]]; i+:1];
           xs[til i] / quite readable, isn't it
          }

Q supports anonymous functions so one can call takeWhile as follows:

q) takeWhile [{x>0}; (1;2;4;0;3;4;5)]
1 2 4

Here {x>0} is an anonymous function that returns true (1b) when the argument is positive and 0b when it is less or equal 0.

My first shot at Haskell’s intersperse looked like this:

intersperse: { -1 _ raze y ,' x }

Here y ,' x creates pairs with first element taken from y and second equal to x:

(1;2;3) ,' 0
1 0
2 0
3 0

raze removes one level of nesting so we get a list instead of a list of pairs:

raze (1;2;3) ,' 0
1 0 2 0 3 0

-1 _ then drops the last element

This works well, but only when the first argument is an atom (not list).

intersperse[0;(1;2;3)]
1 0 2 0 3

But when we want to intersperse a list we get an error:

q) intersperse [(0;0);((1;1);(2;2);(3;3))]
{-1 _ raze y ,' x }
'length

To get a more general version we first define Haskell’s replicate:

replicate: {[n;e]
		a: {x, (enlist z)};
		() a[ ; ;e]/ (til n) / folds the dyadic (binary) function a[ ; ;e] over a list of length n
	}

Here a: {x, (enlist z)} creates a triadic (ternary) function that appends the third argument to the first and ignores the second.
This replicate implementation seems too complicated for such simple task, but I was unable to figure out anything simpler.

We also need Haskell’s zip:

zip: { x {(x;y)}' y }

And now we can define intersperse:

intersperse: { -1 _ raze zip [y ; replicate[count y;x]] }

This works better than the first attempt:

q) intersperse [(0;0);((1;1);(2;2);(3;3))]
1 1
0 0
2 2
0 0
3 3

Haskell’s map is kind of redundant in Q as unary functions (called “monadic” in Q, isn’t that funny?) get mapped when called with a list as an argument.

q)(3+)4
7
q) (3+)(1;2;3)
4 5 6
q)

This approach causes problems though when we want to map a function that works both for atoms and lists. One such function is enlist, which creates a singleton list in Q, kind of like Haskell’s return in the list monad.

(enlist 3)[0]
3

So how can we map enlist over a list? Just calling it on a list gives a singleton list with the argument as the only element of the result:

q) count (enlist (1;2;3))
1

The only solution I can see is to use a trick with folding similar to the one in the definition of replicate above.

map: { [f;xs]
	a: {x, (enlist z[y])};
	() a[ ; ;f]/ xs
	}

This works:

q)map[enlist; (1;2;3)]
1
2
3
Advertisements

Tags:

2 Responses to “Haskell in Q”

  1. Danno Says:

    Hi there! Good to see the ranks of Q are getting more programmers oriented functionally.

    I’m more of a Haskell enthusiast than serious practitioner, but I have been programming in Q for a while. Thought I’d comment a little on the translations you’ve provided.

    There are built in functions for head and tail (first and last).

    Intersperse can be more succinctly written as:

    intersperse:{raze ((-1 _ y),\:x),-1#y}

    This uses the each-left, which works like the apostrophe each adverb, but only across the left join element.

    If you’d like to turn a function composition into a function itself, you need only surround it with parentheses. So, for example, zip becomes:

    zip:(,’)

    Also, there is a built in map function: each

    q)enlist each 1 2 3
    1
    2
    3
    q)enlist each 1
    ,1

    If you haven’t already, you should email Simon or Charlie about getting access to the K4 list box.

  2. slawekk Says:

    Intersperse can be more succinctly written as:

    intersperse:{raze ((-1 _ y),\:x),-1#y}

    This works only of you intersperse an atom into a list. If you intersperse a list into a list of lists it doesn’t do the right thing:

    q)intersperse [(0;0);((1;1);(2;2);(3;3))]
    1 1 0 0 2 2 0 0 3 3
    

    Also zip:(,’) zips correctly only lists of atoms.

    q)zip:(,')
    q)first first zip [((1;1);(2;2);(3;3)); ((4;4);(5;5);(6;6))] 
    1
    q)zip: { x {(x;y)}' y }
    q)first first zip [((1;1);(2;2);(3;3)); ((4;4);(5;5);(6;6))] 
    1 1
    

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: