As an Agilist and XP practitioner, it is not only about the values and how you are as a human towards others. That is the easy part (yes it is! Even if you are a stereotypical nerd), the hard part is getting used to the discipline required to perform the practices.

Luckily my favourite practice is TDD, and my intent is to do TDD while developing APL programs! For this journey, I intend to develop a good-enough Booking backend system with APL and TDD.

Of we go and two different files are created, one Unit test (UT) file booking_unittests.dyalog and one implementation file booking.dyalog. In the UT file, I put the tests into different procedural functions, naming them UT1 , … , UTX.

The first tests is the lookup of a Bookable Unit’s name by it’s ID.

For this, I need some kind of infrastructure, a simple mechanism that will execute my tests and report the ones that fail. I want the Unit Test framework to be lightweight and allow me to keep my freedom. As for all functional programmers out there – the natural thing to do is to write your own.

The framework consists of two procedural functions, RunTest and Test. I define each test as a procedural function as well, and “register” the test for running inside Test

 Z  RunTest Name 
  Z   Name
  :If ~ Z
    '-------------------------'
    'Unit test ',Name,' failed'
    ⎕vr Name
  :Endif


 Z  Test;Tests
  Tests  'UT1'
  Z  RunTest Tests
  :if ^/ Z
    'All ' (⍴ Tests) 'tests passed' 
  :endif

This example shows that ‘UT1′ is to be executed when I run all the Unit Tests.
So, my first test is to check whether I can get the Name by the ID.
As usual, I first run the tests, making sure it will fail

 Booking_UT.Test
-------------------------
Unit test UT1 failed
      ZUT1      
[1]    Z'a'#.Booking.GETName UTNameTable 1
                 

Seems, to work as I expected. So I write the implementation in booking.dyalog and add another test and implementation for the lookup based on ID to Name.

:Namespace Booking

 Z  GETName Input;LookupTable;BID
  (LookupTable BID)  Input
  Z LookupTable[LookupTable[;1]  BID;2]


 Z  GETID Input;LookupTable;Name
  (LookupTable Name)  Input
  Z  LookupTable[LookupTable[;2]  Name;1]

:EndNamespace 

and the two tests (with framework – I should also be able to split the framework into its own module)

:Namespace Booking_UT

UTNameTable   (1 'a') (2 'b') (3 'c')

 Z  UT1
  Z  'a'  #.Booking.GETName UTNameTable 1


 Z  UT2
  Z  3  #.Booking.GETID UTNameTable 'c'


 Z  RunTest Name
  Z   Name
  :If ~ Z
    '-------------------------'
    'Unit test ',Name,' failed'
    ⎕vr Name
  :Endif


 Z  Test;Tests
  Tests  'UT1' 'UT2'
  Z  RunTest ¨ Tests
  :if ^/ Z
    'All ' (⍴ Tests) 'tests passed' 
  :endif


:EndNamespace

run the test again for success

⎕SE.SALT.Load '/home/gianfranco/APL/Projects/Booking/book*'
 #.Booking  #.Booking_UT
      Booking_UT.Test
 All   2  tests passed

Now there is an obvious pattern in both lookup functions, and having regression tests already makes a refactoring attempt safe as the tests will guide me.

After refactoring and running the tests, the implementation looks a bit slimmer

:Namespace Booking

GETName  { Lookup ⍵[1],1,⍵[2] }
GETID  { Lookup ⍵[1],2,⍵[2] }

 Z  Lookup Input;LookupTable;Col;Key
  (LookupTable Col Key)  Input
  Z  LookupTable[LookupTable[;Col]  Key;1+2|Col]


:EndNamespace

However, I have a mental thorn in my eye because of the recurring ⍵[1],1,⍵[2] and ⍵[2],2,⍵[2] pattern. This needs to be addressed. The most obvious solution is to throw the arguments around for the base Lookup procedural function. A quick change

:Namespace Booking

GETName  { Lookup ⍵,1 }
GETID  { Lookup ⍵,2 }

 Z  Lookup Input;LookupTable;Col;Key
  (LookupTable Key Col)  Input
  Z  LookupTable[LookupTable[;Col]  Key;1+2|Col]


:EndNamespace

and test run, shows that this is a solid refactoring

 ⎕SE.SALT.Load '/home/gianfranco/APL/Projects/Booking/book*'
 #.Booking  #.Booking_UT
      Booking_UT.Test
 All   2  tests passed

Nice!
Now time for some more serious functions, I need something to actually perform a booking, given a TimeTable, an Id and Some Timeslots. As this is a blog post, I will not make it much longer than this last booking test, however, I intend to publish the code on github.

As usual, I use the Test to reason about and structure the function and the input data, my test thus looks like this

 Z  UT3;BookingSlots;BookingId;Expected
  BookingSlots  (1 2 3) (3 1) (2 1 3)
  BookingId  5
  Expected   (3 5 0 0) (1 0 5 5) (2 5 0 5)
  Z  Expected  #.Booking.Book UTTable BookingSlots BookingId

and running all the tests show that it’s failing as expected (I just wrote an empty skeleton returning the input table)


 ⎕SE.SALT.Load '/home/gianfranco/APL/Projects/Booking/book*'
 #.Booking  #.Booking_UT
      Booking_UT.Test
-------------------------
Unit test UT3 failed
      ZUT3;BookingSlots;BookingId;Expected
[1]    BookingSlots←(1 2 3)(3 1)(2 1 3)
[2]    BookingId5
[3]    Expected←↑(3 5 0 0)(1 0 5 5)(2 5 0 5)
[4]    ZExpected#.Booking.Book UTTable BookingSlots BookingId
                 
1 1 0

Now, the implementation that passes the test is

 Z  Book Input;Table;Bookings;BookingId;Rows;Slots
  (Table Bookings BookingId)  Input
  Rows  Table[;1]  ⊃¨Bookings
  Slots  1↓¨Bookings
  {Table[1↑⍵;1+1↓⍵]  BookingId} ¨ RowsSlots
  ZTable

and the proof that it works is my test

⎕SE.SALT.Load '/home/gianfranco/APL/Projects/Booking/book*'
 #.Booking  #.Booking_UT
      Booking_UT.Test
 All   3  tests passed
1 1 1            

I leave this here together with a link to the Git repo where you can see the project continue and take form.

Cheers

(really looking forward to all the comments)

A Progressive Matrix Test is a kind of IQ test which is widely popular and becoming more so. Many companies want to assure that they get the right person for the job and use this tool as a kind of indicator or sieve to see how well the prospecting employee might perform in the face of incomplete information.

I have done (as a subject) some of these myself, and thought of them directly when I saw the graphical representation that APL produces effortlessly. This was the perfect language to produce these tests! I can make money selling them!

So, taking to the task I started out with this first generation where the Matrices only have variance along the horizontal, still, this is enough to amuse me for a brief moment

:Namespace Induction

∇ X ← GEN_RandMovement Input;Amount;Magnitude
(Amount Magnitude) ← Input
X ← (1 ¯1)[? Amount ⍴ 2] × ? Amount ⍴ Magnitude

∇ X ← ApplyMovement Input;MovementVector;Matrix
(MovementVector Matrix) ← Input
X← MovementVector ⌽ Matrix

∇ X ← GenIndTest Input;Dim;Rows;Panes;First;HorMov
(Dim Panes) ← Input
First ← Dim Dim ⍴ ¯1 + ? 16 ⍴ 2
HorMov ← ↓ (⍳ Panes) ∘.× GEN_RandMovement ⍴ First
X ← { ApplyMovement ⍵ First} ¨ HorMov

:EndNamespace

Invoking this first generation version with a dimension of 5 and 4 panes (last pane is the correct answer in your test) gives

Now, I wanted to do this with a tighter APL syntax so the second generation was a bit more compressed but starting to feel (for me at this point in my APL journey) as though it will not be very maintainable.

:Namespace Induction

GenMov ← { (1 ¯1)[? ⍵[1] ⍴ 2] × ? ⍵[1] ⍴ ⍵[2] }
AppHor ← { ⊃ ⌽/⍵ }

∇ X ← GenIndTest Input;Dim;Panes;First;HorMov
(Dim Panes) ← Input
First ← (2/Dim) ⍴ ¯1 + ? 16 ⍴ 2
HorMov ← ↓ (⍳ Panes) ∘.× GenMov ⍴ First
X ← {AppHor ⍵ First} ¨ HorMov

:EndNamespace

It still works and gives me mild amusement

I continued to think about this and I wanted to rewrite the sequential application in a neater way, I was particularly bothered by

{AppHor ⍵ First} ¨ HorMov

so I got this

:Namespace Induction

GenMov ← { (1 ¯1)[? ⍵[1] ⍴ 2] × ? ⍵[1] ⍴ ⍵[2] }
AppHor ← { ⍺ ⌽ ⍵ }

∇ X ← GenIndTest Input;Dim;Panes;First;HorMov
(Dim Panes) ← Input
First ← (2/Dim) ⍴ ¯1 + ? 16 ⍴ 2
HorMov ← GenMov ⍴ First
X ← {HorMov (AppHor⍣⍵) First} ¨ ⍳Panes

:EndNamespace

This was good enough, I think I just finished my first APL refactoring.
Yay.

But, this does not reach my amusement standards, I need the Progressions to move Vertically as well.
So I thought of adding a VerMov vector into the equation and applying that as well in the power

:Namespace Induction

GenMov ← { (1 ¯1)[? ⍵[1] ⍴ 2] × ? ⍵[1] ⍴ ⍵[2] }
AppHor ← { ⍺ ⌽ ⍵ }
AppVer ← { ⍺ ⌽[1] ⍵}

∇ X ← GenIndTest Input;Dim;Panes;First;HorMov
(Dim Panes) ← Input
First ← (2/Dim) ⍴ ¯1 + ? 16 ⍴ 2
HorMov ← GenMov ⍴ First
VerMov ← GenMov ⍴ First
X ← {VerMov (AppVer⍣⍵) HorMov (AppHor⍣⍵) First} ¨ ¯1 + ⍳Panes

Now, the result is just the right kind of amusing.

APL Ball Toy

April 23, 2012

Much in the spirit of the Diamond and APL Text toy, this toy is also a text matrix.
It produces a table with a ball in the center

{‘⎕⊖'[1+{⍵⍪⊖⍵}{⍵,⌽⍵} (⍵*1.4)≤(⍳⍵)∘.×⍳⍵]}

Invoking it on each of ⍳5 gives

Let me know if you have (you probably do) some awesome shorter versions!

APL APL text toy

April 21, 2012

On the topic of APL toys, I had to test how to write a small one liner that prints the text APL in a seemingly impressive way on the screen.
In pursuing this I got the following as good enough. As for the diamond toy, this is also a text matrix.

‘ ⎕'[1+↑(11/2)∘⊤¨1300+608 64 608 51]

The result is

⎕⎕⎕ ⎕⎕⎕ ⎕                                                                       
⎕ ⎕ ⎕ ⎕ ⎕                                                                       
⎕⎕⎕ ⎕⎕⎕ ⎕                                                                       
⎕ ⎕ ⎕   ⎕⎕⎕ 

But of course Morten Kromberg had a suggestion of how to make it even more compact

‘ ⎕'[1+4 12⍴11⎕DR’諮躊 ‘]

of course, the result is still the same.

APL Diamond Toy

April 21, 2012

I notice that I play a lot more with APL than I do with other languages. The way you work so explicitly with data shape makes it hard not to play around with it.
If your programming language allowed you to treat data as play-dough, would you not play with it?

While exploring a different way to make the code more compact, I have to restrain myself so that I do not end up in a long sub branch of thoughts totally unrelated to the original problem. Last time, the original sub problem I wanted to solve was: How do I rotate each row of the matrix M an equal number of items as the row index? Solution: (⍳1⊃⍴M)⌽M
But I got stuck in a 1-2 hour spree of trying to make some matrix art with 1’s and 0’s. This is Fun! I promise!

Eventually I ended up with this little piece of code

‘⋄⎕'[1+{⍵,⌽⍵} {⍵⍪⊖⍵} 8 ≥ (⍳7) ∘.+ (⍳7)]

Of course, this can not be something new. And I am not delusional enough to think this is something new. C and Perl has well known obfuscated code contests, and other languages their own toys and challenges. But this surely demonstrates how beautiful APL is, and how you can play with it.

Just in case you can’t see this with a mental image, I supply the result of the evaluation in Dyalog APL 13.0 (of course the diamonds ⋄ look a lot better in the Dyalog interpreter)

Dyalog APL interpreter result for the Diamond toy

For some reason I tend to become entranced by the esoteric, old and forgotten. There is a special charm to each of these languages. Rarely do I see any beauty in the bloated corporate programming language/platforms.

I seem to have a pathological disgust to the things that try to be a jack of all trades, expanding year after year, like a greedy child looking at other languages, picking the chocolate crisps out of every cookie. Realizing to late that the swollen belly prevents them from putting on that sexy shirt they used to wear a while back.

At the Software Passion 2012 summit in Gothenburg, I met Morten Kromberg from Dyalog. He demonstrated the raw power you can harness from APL. I fell in love there, on the spot. I just felt that I _had_ to do some APL.

Coming from a functional programming background, I felt at home with the compactness and compositionality of the language. The mind of thought where data flows explicitly and you operate on it like an artisan glass master, it is so beautiful. Kind of like a yoga for the programming mind.

APL is quite old and currently belongs to the history books, but it might yet make a strong come back! As long as there is interest and passion, any piece of culture can be passed down in generations.

Follow

Get every new post delivered to your Inbox.