Category Archives: Code sample

Translating SQL into Andl.NET

I have just completed translating a series of SQL exercises into Andl.NET. It really is rather straightforward, surprisingly so.

Here is the source code. It should be self-explanatory. The original is here: https://web.njit.edu/~hassadi/Dbase_Courses/CIS631/Ex_03.html

      //----------------------------------------------------------------------
      // Q1. Get suppliers names who supply part 'P2'.
      // SQL>select sname from s, sp where s.s#=sp.s# and sp.p#='P2'; 
      // SQL>select distinct s.sname from s where s.s# IN (select sp.s# from sp where sp.p# ='P2'); 

      Show("Q1. Get suppliers names who supply part 'P2'",
        SP.Where(sp => sp.Pno == "P2")
          .Join(S, (sp, s) => new { s.Sname })
          .AsEnumerable()
          .Select(s => $"{s.Sname}")
        );

      Show("Q1. Get suppliers names who supply part 'P2'",
        S .Where(s => SP
            .Where(sp => sp.Pno == "P2")
            .Select(sp => new { sp.Sno })
            .Contains(new { Sno = s.Sno }))
          .AsEnumerable()
          .Select(s => $"{s.Sname}")
        );

Continue reading

Leave a Comment

Filed under Andl.Net, Code sample

Announcing: Andl Syntax 6

Andl syntax has been evolving over time. The original syntax was somewhat cryptic and a bit of a challenge for anyone to understand. Also some parts of the syntax could be ambiguous in some situations, or at least would limit the generality of some constructs. It has also become increasingly clear that much of the syntax has direct parallels in SQL, and that it is people who already know SQL who are most likely to be interested.

Introducing Syntax 6. The key development is that each of the parts of a query takes a form that is closely parallel to a corresponding construct in SQL. So, for example:

  • The function .where(expression) performs the same function as an SQL WHERE clause.
  • The function .order(field, field...) performs the same function as an SQL ORDER BY clause.
  • The function .set{ field, field:=value } performs the same function as an SQL SELECT.

And there are lots more. These are postfix functions (with a dot) that follow a relational expression. On the other hand JOIN and UNION are infix operations, placed between two values. When you put them together you get something like this SQL query and the corresponding Andl code.

// SQL>select distinct sname from s, sp, p where p.color='Red' and s.s#=sp.s# and p.p#=sp.p#
(S .set{ S#, SNAME } join SP .set{ S#, P#} join P .set{ P#,COLOR }) .where(COLOR='Red') .set{ SNAME }

The order is not quite the same but all the same elements are there. If you know SQL, now with Syntax 6 you can learn Andl easily!

Latest releases are on the Downloads page. There are lots more examples of SQL and matching Andl syntax in the sample file SPPSample1.andl.

Leave a Comment

Filed under Code sample, Language, Release

Recursive Queries: Sudoku Solver

This Sudoku solver uses a number of recursive queries, first to generate fixed data structures and then to drive the search for a solution.

This solver simply applies the rules.

  1. Every row, column and box (3×3) must have the digits 1 to 9 once and once only.
  2. If only one digit can go in a cell then it must go there.
  3. If a digit can go in only one place in a row, column or box then it must go there.

Here is the code.

First, the fixed data structures.

digits := {{ sdigit:= '1', ndigit := 1}} recurse( {{ sdigit:=text(ndigit+1), ndigit:=ndigit+1 }} [?(ndigit <= 9)] )
digitsx := digits union {{ sdigit := '.', ndigit := 0 }}
units := {{ index := 0, row := 0, col := 0, box := 0 }} recurse( 
        {{ index := index + 1, 
          row := (index + 1) div 9, 
          col := (index + 1) mod 9, 
          box := (index + 1) div 3 mod 3 + (index + 1) div 27 * 3 }} [?(index <= 80)] )
poss := units [{ index }] join digits [{ ndigit }]
possu := units join digits [{ ndigit }]

Now some useful functions.

showb(t:text) => do {
     seq(11)[{ N, line:=
         if(N mod 4 = 3,
            fill('-', 9),
            right(left(t, 9 + (N - N div 4) * 9), 9))}]
 }  
// Show a set of knowns. First fill out all index values, then convert to text
 showunk(k:poss) => do {
 t := (k union (units ajoin k)[{ index, ndigit := 0}]) join digitsx[{ ndigit, sdigit }]
 showb(t [$(index)][{ fold(&, sdigit) }])
}

The original raw data

inp := {{ sud := '53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79' }}
 //inp := {{ sud := '1....7.9..3..2...8..96..5....53..9...1..8...26....4...3......1..4......7..7...3..' }}
 inp
 board := ((units join inp) [{ * sud, sdigit := right(left(sud, index + 1), 1) }] compose digitsx) [{ index, ndigit }]
 knowns := board [?( ndigit <> 0)]
 'Knowns=' & knowns.count
 showunk(knowns)

This the solver, which recurses as long as it can make progress. After this you have to guess.

solution := knowns recurse(
 do {
 // start with the 729 possiblities, progressively remove conflicts with knowns
 knownsu := knowns join units
 allowedu := possu ajoin knownsu[{ index }]
 ajoin knownsu[{ row, ndigit }]
 ajoin knownsu[{ col, ndigit }]
 ajoin knownsu[{ box, ndigit }]

// algorithm 1 - a cell with only one possible digit must be that digit
 new1 := allowedu [{ index, tot:=fold(+,1) }] [?(tot=1)] join allowedu

// algorithm 2 - a digit with only one place in a unit must go there
 new2a := allowedu [{ ndigit, row, tot:=fold(+,1) }] [?(tot=1)] join allowedu
 new2b := allowedu [{ ndigit, col, tot:=fold(+,1) }] [?(tot=1)] join allowedu
 new2c := allowedu [{ ndigit, box, tot:=fold(+,1) }] [?(tot=1)] join allowedu

 new1[{ index, ndigit }] union new2a union new2b union new2c
 }
)
showunk(solution)
And here is the grid, before and after solving.
N      | line
------------------
     0 | 53..7....
     1 | 6..195...
     2 | .98....6.
     3 | ---------
     4 | 8...6...3
     5 | 4..8.3..1
     6 | 7...2...6
     7 | ---------
     8 | .6....28.
     9 | ...419..5
    10 | ....8..79

N      | line
------------------
     0 | 534678912
     1 | 672195348
     2 | 198342567
     3 | ---------
     4 | 859761423
     5 | 426853791
     6 | 713924856
     7 | ---------
     8 | 961537284
     9 | 287419635
    10 | 345286179

			

Leave a Comment

Filed under Code sample, Language

Recursive Queries: the Mandelbrot Set

Recursive queries can be used to solve a variety of problems that require multiple passes, not just recursive data structures. Here is an implementation of the Mandelbrot set as ASCII art.

xaxis := {{ x:=-2.0 }} recurse( {{ x:=x+0.05 }} [?(x<1.2)] )
yaxis := {{ y:=-1.0 }} recurse( {{ y:=y+0.1 }} [?(y<1.1)] )
m := ({{ iter:=0, x:=0, y:=0 }} join xaxis[{ cx:=x }] join yaxis[{ cy:=y }]) 
    recurse( {{ iter:=iter+1, x := x*x-y*y+cx, y:=2*x*y+cy, cx, cy, }} [?(x*x+y*y<4.0 and iter<28)] )
m.count
m2 := m[{ iter := fold(max,iter), cx, cy }] [$(cy,cx)]
m2.count
a := m2 [ { cy, t := fold(&, right(left(' .+*#', 1 + iter div 6), 1)) }]
a

This query iterates each point up to 28 times to find those that lie on the border of the Mandelbrot set. Each point that is still within range is iterated until no more can be found. The number of iterations selects which character to display.

Here is the result.

cy     | t
--------------------------------------------------------------
  -1.0 |                                     ....#            
  -0.9 |                                    ..#*..            
  -0.8 |                                  ..+####+.           
  -0.7 |                             .......+####+...   +     
  -0.6 |                            ..##+###########*.++++    
  -0.5 |                           .+.##################*.    
  -0.4 |               .............*###################+.*   
  -0.3 |               ..+*.+#+....*#####################+.   
  -0.2 |              ...+#######++#######################.   
  -0.1 |           ...++*################################.    
   0.0 |  #############################################...    
   0.1 |           ...++*################################.    
   0.2 |              ...+#######++#######################.   
   0.3 |               ..+*.+#+....*#####################+.   
   0.4 |               .............*###################+.*   
   0.5 |                           .+.##################*.    
   0.6 |                            ..##+###########*.++++    
   0.7 |                             .......+####+...   +     
   0.8 |                                  ..+####+.           
   0.9 |                                    ..#*..            
   1.0 |                                     ....#

 

Leave a Comment

Filed under Code sample, Language

Recursive Queries

Andl now supports recursive queries. This can be seen as a direct extension of the Relational Algebra for the situation where a query cannot be satisfied in a single operation.

Imagine a company organisation chart. One query will find every employee who reports to a given boss, but it takes a succession of queries to find out who reports to that employee, and who reports to them, until at last we reach the workers and no-one reports to them.

Here is the data for a simple orgchart (data thanks to SQLite).

name  | boss
-------------
Alice |
Bob   | Alice
Cindy | Alice
Dave  | Bob
Emma  | Bob
Fred  | Cindy
Gail  | Cindy

Here is the Andl code to query it.

def orgchart:db(csv)
orgchart
ua := {{ name:= 'Alice', level := 0 }} recurse( {{ boss := name, level := level+1 }} compose orgchart)
ua
ua [{ t:=fill('.', level*3) & name }]

And here is the result. The recurse function repeatedly evaluates the join with successive tuples starting from the seed, until no more can be found.

name  | level
--------------
Alice |      0
Bob   |      1
Cindy |      1
Dave  |      2
Emma  |      2
Fred  |      2
Gail  |      2
t
----------
Alice
...Bob
...Cindy
......Dave
......Emma
......Fred
......Gail

Leave a Comment

Filed under Code sample, Language

Sample Andl code: the Dbix CD Sample

One thing I knew and has only been reinforced in early feedback about Andl: you can’t just talk about a new language, you have to show it.

So here is my version of a sample application I found on CPAN: http://search.cpan.org/dist/DBIx-Class/lib/DBIx/Class/Manual/Example.pod. They’re not strictly comparable, so just take this is as a sample of what Andl code looks like for creating some data and executing some simple queries.

// translation of sample application from http://search.cpan.org/dist/DBIx-Class/lib/DBIx/Class/Manual/Example.pod

// create the database relvars
artist  := {{ artistid:0, name:'' }}
cd      := {{ cdid:0, artistid:0, title:'', year:0 }}
track   := {{ trackid:0, cdid:0, title:'' }}

// some data in temporary relvars, with auto-generated ordinals
$artist_data := {{name:''}
  ( 'Michael Jackson' ), 
  ( 'Eminem' ),
} [{ *artistid := ord() }]

$cd_data := {{ title:'', name:'' }
  ( 'Thriller',                'Michael Jackson' ),
  ( 'Bad',                     'Michael Jackson' ),
  ( 'The Marshall Mathers LP', 'Eminem' ),
} [{ *cdid := ord() }]

$track_data := {{ title:'', cd:'' }
   ( 'Beat It'         , 'Thriller' ),
   ( 'Billie Jean'     , 'Thriller' ),
   ( 'Dirty Diana'     , 'Bad' ),
   ( 'Smooth Criminal' , 'Bad' ),
   ( 'Leave Me Alone'  , 'Bad' ),
   ( 'Stan'            , 'The Marshall Mathers LP' ),
   ( 'The Way I Am'    , 'The Marshall Mathers LP' ),
 } [{ *trackid := ord() }]

// update the database relvars
artist := union $artist_data
cd := union ($cd_data join artist) [{ title, cdid, artistid, year:=0}]
track := union ($track_data join cd [{ *cd := title }]) [{ trackid, title, cdid }]

// functions to answer various queries
get_tracks_by_cd(t) => cd[ ?(title = t) { *title } ] join track
get_tracks_by_artist(a) => (artist[ ?(name = a) { *name }] join cd) [{ cdid }] join track
get_cd_by_track(t) => track [ ?(title = t) { cdid } ] join cd
get_cds_by_artist(a) => artist [?(name = a) { artistid } ] join cd
get_artist_by_track(t) => (track [?(title = t) { cdid }] join cd) [{ artistid }] join artist
get_artist_by_cd(t) => (cd [?(title = t) { cdid }] join cd) [{ artistid }] join artist

// first show the raw data

crlf := h'd a'
output := crlf & "=== Sample data ===" & crlf
output := artist.pp
output := crlf.pp
output := cd.pp
output := crlf.pp
output := track.pp

// now do the queries

show(title, data:{{ str:'' }}) => do {
    output := title & crlf & data[ {fold(&, "  " & str & crlf)} ]
}

output := crlf & "=== Query results ===" & crlf
show("Track title:", get_tracks_by_cd('Bad') [ {str:=title} ])
show("Track title:", get_tracks_by_artist('Michael Jackson') [ {str:=title} ])
show("CD title:", get_cd_by_track('Stan') [ {str:=title} ])
show("CD title:", get_cds_by_artist('Michael Jackson') [ {str:=title} ])
show("Artist:", get_artist_by_track('Dirty Diana') [ {str:=name} ])
show("Artist:", get_artist_by_cd('The Marshall Mathers LP') [ {str:=name} ])

The output from compiling and running this script looks as follows.

=== Sample data ===

artistid | name           
--------------------------
       0 | Michael Jackson
       1 | Eminem         

text: '
'
cdid   | artistid | title                   | year  
----------------------------------------------------
     0 |        0 | Thriller                |      0
     1 |        0 | Bad                     |      0
     2 |        1 | The Marshall Mathers LP |      0

text: '
'
trackid | cdid   | title          
----------------------------------
      0 |      0 | Beat It        
      1 |      0 | Billie Jean    
      2 |      1 | Dirty Diana    
      3 |      1 | Smooth Criminal
      4 |      1 | Leave Me Alone 
      5 |      2 | Stan           
      6 |      2 | The Way I Am   


=== Query results ===

Track title:
  Dirty Diana
  Smooth Criminal
  Leave Me Alone

Track title:
  Beat It
  Billie Jean
  Dirty Diana
  Smooth Criminal
  Leave Me Alone

CD title:
  The Marshall Mathers LP

CD title:
  Thriller
  Bad

Artist:
  Michael Jackson

Artist:
  Eminem

Leave a Comment

Filed under Code sample

Sample Code 2

This is a page of sample code, showing basic relational capabilities. It will be part of the download, when available.

// Andl samples -- relational
// Aim is to show an example of every feature
// Also useful as a quick smoke test

// ===== Relational types =====

// Tuples - not used much
{}
{name := 'Smith', age := 17}
{age := 17, name := 'Smith'}

// Relations - used heavily
{{:}}       // empty relation, no attributes, no tuples
{{:}{}}     // ditto, one tuple
{{}}        // same, with type derived by inference
{{},{},{},{},{},{},{},{}}        // exactly the same value (duplicates discarded)

// all the same -- order does not matter
{{name := 'Smith', age := 17}}
{{name:'',age:0}{'Smith', 17}}
{{name:text,age:number}{'Smith', 17}}

// all the same -- differences in order and syntax
{{name := 'Smith', age := 17},{name := 'Jones', age := 35},{age :=199,name:='Frankenstein' }}
{{name := 'Smith', age := 17},{age :=199,name:='Frankenstein' },{age := 35, name := 'Jones'}}
{{name:,age:0}{'Smith', 17}{'Jones', 35}{'Frankenstein',199 }}

// Built in functions
sequence(5)     // relation of N integers {{ N:number }}

r1 := {{name:,age:0}{'Smith', 17}{'Jones', 35}{'Frankenstein',199 }}
r1.schema       // relation of attributes
r1.count        // cardinality
r1.degree       // degree

//===== Basic operations =====

// Load some data from CSV files
S := source('csv:', 'S.csv')
P := source('csv:', 'P.csv')
SP := source('csv:', 'SP.csv')
S
S.schema
S.count
S.degree

//==== monadics =====
// all monadic operations are inside [], in a fixed sequence ?$%{}

// restriction - remove rows
S [ ?(CITY = 'Paris') ]
S [ ?(STATUS > 15 or CITY = 'London') ]

// --- rename - change column names
// rename all
S [ { F1 := S#, F2 := SNAME, F3 := STATUS, F4 := CITY }]
// rename some, the * means keep the rest unchanged
S [ { * F1 := SNAME }]

// --- projection - remove columns
// name all to be kept
S [ { S#, SNAME, CITY }]
// now * means keep all but the ones named
S [ { * STATUS }]

// --- extension - add new columns
// Here * means keep all, add new ones
S [ { * Initial := left(SNAME, 1) }]

// --- combine all three
S [ { CITY, F := STATUS, Initial := left(SNAME, 1) }]
S [ { * SNAME, Initial := left(SNAME, 1) }]

// --- aggregated projection - projection with totalling
S [ { CITY, 
    total := fold(+,STATUS), 
    average := fold(+,STATUS)/fold(+,1) 
} ]

// Note: fold() is only allowed in projection, but looks nicer in a function
sum(n:0) => fold(+,n)
ave(n:0) => fold(+,n)/fold(+,1)
S [ { CITY, total := sum(STATUS), average := ave(STATUS) } ]

// --- ordered extension - means extension with access to other rows
// ordered on CITY but no grouping, so all in one group
S [ $(CITY) { *  
    ord:=ord(),     // unique index based on input, not output
    ordg:=ordg(),   // ord value for first member of group
    lag:=lag(STATUS,1),     // previous value in group, or default
    lead:=lead(STATUS,1),   // next value in group, or default
    nth:=nth(STATUS,1),     // nth value in group, or default
} ]
// ordered and grouped on CITY
S [ $(%CITY) { *  
    ord:=ord(),
    ordg:=ordg(),
    lag:=lag(STATUS,1), 
    lead:=lead(STATUS,1), 
    nth:=nth(STATUS,1), 
} ]
// ordered and grouped on CITY descending, with subtotalling/running sum
S [ $(%-CITY) { *  
    ord:=ord(),
    ordg:=ordg(),
    lag:=lag(STATUS,1), 
    lead:=lead(STATUS,1), 
    nth:=nth(STATUS,1), 
    sum:=fold(+,STATUS),    // running sum within group
    ave:=fold(+,STATUS)/fold(+,1),
} ]

// Ordered used just for display sort
P[$(WEIGHT)]
P[$(COLOR,-WEIGHT)]     // descending

// --- lift anonymous value out of singleton relation
S [ { sum(STATUS) } ]
S [ { ave(STATUS) } ]

//--- nested relation
nr1 := {{ name := 'S', contents := S }}
nr1
nr2 := {
    { name := 'S1', contents := S [?( CITY = 'London')] },
    { name := 'S2', contents := S [?( CITY = 'Paris')] },
    { name := 'S2', contents := S [?( CITY = 'Athens')] } }
nr2
// retrieve one row as relation
nr2 [?(name='S1') { contents }]
// put the relation back together again using fold and union
nr2 [ { fold(union,contents) } ]

//==== dyadics =====

// prepare some subsets
S3 := S [?( S# = 'S3')]    // one single supplier S3
SX := S [?( S# <> 'S3')]   // all suppliers except S3 to make this work better
SY := S [?( S# <> 'S1')]   // all suppliers except S1

// set membership -- all true
S3 sub S        // subset
S sup SX        // superset
S3 sep SX       // separate

// joins

S join SP       // natural join preserves all columns for matching tuples
S compose SP    // projects onto non-common attributes
S semijoin SP   // projects onto left and common attributes
S divide SP     // projects onto left only attributes
S rsemijoin SP  // projects onto right and common attributes
S rdivide SP    // projects onto right only attributes

// antijoins

SX ajoin SP      // antijoin preserves all left attributes for non-matching
SX ajoinl SP     // projects onto left only attributes
SX rajoin SP     // reverse antijoin has right and common attributes
SX rajoinr SP    // projects onto right only attributes

// set operations

SX union SY      // combines all tuples
SX intersect SY  // keep common tuples
SX symdiff SY    // keep non-common tuples
SX minus SY      // keep left minus right
SX rminus SY     // keep right minus left

// all set operations project onto common attributes
SZ := {{ S#:='S99', STATUS:= 999, CITY:='Paris' }}
S union SZ
S minus SZ

// ===== Advanced Usage =====

// --- Nest: replace each tuple of S by one converted into a singleton relation
// {{*}} means 'the current tuple as a singleton relation'
ES1 := S [{ embed := {{*}} }]
ES1

// --- Unnest: using fold union and lift -- advanced usage!
ES1 [{ fold(union,embed) }]

// --- Image relation -- extend S with a partion of SP, removing common fields
// note that S5 gets a partition that is an empty relation
ES2 := S [{ * partition := ( {{*}} rdivide SP) }]
ES2

// Report total no of parts and qty (including S5 which supplies no parts)
ES2 [{ S#, parts:=partition.count, qtys:=partition[{sum(QTY)}] }]

// --- Transitive closure
// MM has tuples reprenting part/subpart assemblies
MM := source('csv:','MM.csv')
MM

// define a relational type
xyt := {{x:='',y:=''}}
// define a recursive function that takes a relation of that type as an argument
tranclo:xyt(xy:xyt) => do {
        ttt := xy[{*z := y}] compose xy[{*z := x}] union xy
        if(ttt = xy, ttt, tranclo(ttt))
    }
// call it with MM as argument, renaming attributes to match
tranclo(MM[ {x:=MAJOR_P#, y:= MINOR_P# } ]) [{MAJOR_P#:=x, MINOR_P#:=y }]

// ===== Updates =====

// Define the 3 updates

// Insert: argument is relation of same heading
up1 => S := union {{ S#:='S9', SNAME:='Moriarty', STATUS:=99, CITY:='Timbuktu' }}
// Delete: read as replace matching rows by nothing
up2 => S := [ ?(S#='S3') ]
// Update: make changes to matching rows
up3 => S := [ ?(S#='S4') { *STATUS:= -10 } ]
// Now perform each update in turn
S // original
up1
S // add S9
up2
S // delete S3
up3
S // update STATUS of S4

// Persistence
// any relvar starting with ^ is persisted

^S := S

// end

Leave a Comment

Filed under Code sample

Sample Code 1

This is a page of sample code, showing basic non-relational capabilities. It will be part of the download, when available.

// Andl samples -- scalar
// Aim is to show an example of every feature
// Also useful as a quick smoke test

//=== Tokens ===
// A free expression is evaluated immediately
'hello world!'
// Single and double quoted strings are concatenated immediately
"Hel""lo" ' World' "!"
// d-string is decimal Unicode char
"Hello" & d'32 87' & "orld"
// h-string is hex Unicode char
"Hello" & h'20 57' & "orld"
// t-string is a Time
t'2015/12/31'
// i-string is an identifier
i'x( )' := 'hello world!'
i'x(' h'20' ')'

//=== Expressions ===
// Logical: all true
true = not false
3 <> 4
'ABC' > 'abc'
t'2015/12/31' < t'01/01/2016'
'Z' >= 'A' and (3<=4 or (true xor false))

// Numeric: all 42
5-8*6/4+7**2
(58 div 8) * (86 mod 8)
(9 or 7) and (9 xor 3) xor 32

// String: mostly Hello World!
'Hello' & " " & 'World!'
trim('Hello ') & ' ' & trim(' World') & trim('   !   ')
fill(' ',10) & fill('Hello Planet???', 5) & fill(' World!', 35)
before(after("@@@>>>Hello World!<<<@@@", '>>>'), '<<<')
toupper('h') & tolower('ELLO') & toupper(' w') & tolower('ORLD!')
// String: other
'hello'.length = length('world')
"Interpolate date: " & t'2015/12/31' & " number:" & 12345 & " logical:" & (2=2)

// Date: 
d1 := dateymd(2015,1,31)
"Date: " & d1 & " Year:" & d1.year & " month:" & d1.month & " day:" & d1.day & " dow:" & d1.dow

// Special: if(), only evaluates one of its arguments
if(true,'Hello World!', "goodbye!")
if(2>2,1/0, 7*6)

//=== Statements ===
// Assignment -- evaluated once
v1 := 'Hello World!'
v1
// Assignment to out sends direct to output
out := v1
// Deferred assignment -- evaluated every time
v2 => v1
v2

// do block creates a local scope and returns value of last expression (statement returns void)
do {
  v2 := v1
  v2
}
// Deferred evaluation wiht do block
v3 => do {
  v2 := v1
  v2
}
v3
// Deferred evaluation with do block and arguments
v4(a) => do {
  v2 := a
  v2
}
// Arguments with literal types (default is text)
v5(a:'',b:0,c:false,d:d1) => do {
  a & b & c & d
}
v5(v1,42,true,dateymd(2015,1,1))
// Arguments with named types
v6(a:text,b:number,c:bool,d:date) => do {
  a & b & c & d
}
v6(v1,42,true,dateymd(2015,1,1))
// Recursion, function name must be typed
fact:0(n:0) => if(n<=1,1,n*fact(n-1))
fact(20)

// ===== Types =====

u1 := ut1 { n:=42, t:=v1, d:= d1 }
"n:" & u1.n & " t:" & u1.t & " d:" & u1.d
u2 := ut1 { n:=41, t:="!"&v1, d:= dateymd(d1.year+1,d1.month,d1.day) }
"n:" & u2.n & " t:" & u2.t & " d:" & u2.d
// Comparison left-to-right
u1 > u2
// Deferred function
f7(u:ut1) => do {
    "n:" & u.n & " t:" & u.t & " d:" & u.d
}
f7(u1)
f7(u2)

// done

Leave a Comment

Filed under Code sample

The Artist-CD-Track sample – version 2

This is an updated version of the Artist-CD-Track sample previously posted.

  1. The syntax for defining a relvar has changed, so that only the heading need be defined as attribute names and types. The type can be inferred from a literal or variable of that type.
  2. The output format has been improved. Note the use of fold(&,) to format lines of output text.
  3. The code has been annotated to assist in understanding.
// translation of sample application from http://search.cpan.org/dist/DBIx-Class/lib/DBIx/Class/Manual/Example.pod
#noisy 0

// create the database relvars
artist  := {{ artistid:0, name:'' }}
cd      := {{ cdid:0, artistid:0, title:'', year:0 }}
track   := {{ trackid:0, cdid:0, title:'' }}

// some data in temporary relvars, with auto-generated ordinals
artist_data := {
  { name := 'Michael Jackson'}, 
  { name := 'Eminem' }
} [{ *artistid := ord() }]

cd_data := {
  { title := 'Thriller', name := 'Michael Jackson' },
  { title := 'Bad', name := 'Michael Jackson' },
  { title := 'The Marshall Mathers LP', name := 'Eminem' }
} [{ *cdid := ord() }]

track_data := {
   { title := 'Beat It'         , cd := 'Thriller' },
   { title := 'Billie Jean'     , cd := 'Thriller' },
   { title := 'Dirty Diana'     , cd := 'Bad' },
   { title := 'Smooth Criminal' , cd := 'Bad' },
   { title := 'Leave Me Alone'  , cd := 'Bad' },
   { title := 'Stan'            , cd := 'The Marshall Mathers LP' },
   { title := 'The Way I Am'    , cd := 'The Marshall Mathers LP' }
 } [{ *trackid := ord() }]

 // update the database relvars
artist := union artist_data
cd := union (cd_data join artist)[{ title, cdid, artistid,year:=0}]
track := union (track_data join cd[{ *cd := title }]) [{ trackid, title, cdid }]

// functions to answer various queries
get_tracks_by_cd(t) => cd[ title = t {*title} ] join track
get_tracks_by_artist(a) => (artist[ name = a {*name}] join cd) [{cdid}] join track
get_cd_by_track(t) => track [ title = t {cdid} ] join cd
get_cds_by_artist(a) => artist [name = a {artistid} ] join cd
get_artist_by_track(t) => (track [title = t { cdid }] join cd) [{artistid}] join artist
get_artist_by_cd(t) => (cd [title = t { cdid }] join cd) [{artistid}] join artist

// first show the raw data

crlf := h'd a'
out := crlf & "=== Sample data ===" & crlf
out := artist
out := crlf
out := cd
out := crlf
out := track

// now do the queries

show(title, data:{{str:''}}) => do {
    out := title & crlf & data[ {fold(&, "  " & str & crlf)} ]
}

out := crlf & "=== Query results ===" & crlf
show("Track title:", get_tracks_by_cd('Bad') [ {str:=title} ])
show("Track title:", get_tracks_by_artist('Michael Jackson') [ {str:=title} ])
show("CD title:", get_cd_by_track('Stan') [ {str:=title} ])
show("CD title:", get_cds_by_artist('Michael Jackson') [ {str:=title} ])
show("Artist:", get_artist_by_track('Dirty Diana') [ {str:=name} ])
show("Artist:", get_artist_by_cd('The Marshall Mathers LP') [ {str:=name} ])

The output now looks like this.

 == Sample data ===

artistid | name
--------------------------
       0 | Michael Jackson
       1 | Eminem


cdid     | artistid | title                   | year
--------------------------------------------------------
       0 |        0 | Thriller                |        0
       1 |        0 | Bad                     |        0
       2 |        1 | The Marshall Mathers LP |        0


trackid  | cdid     | title
-------------------------------------
       0 |        0 | Beat It
       1 |        0 | Billie Jean
       2 |        1 | Dirty Diana
       3 |        1 | Smooth Criminal
       4 |        1 | Leave Me Alone
       5 |        2 | Stan
       6 |        2 | The Way I Am

=== Query results ===

Track title:
  Dirty Diana
  Smooth Criminal
  Leave Me Alone

Track title:
  Beat It
  Billie Jean
  Dirty Diana
  Smooth Criminal
  Leave Me Alone

CD title:
  The Marshall Mathers LP

CD title:
  Thriller
  Bad

Artist:
  Michael Jackson

Artist:
  Eminem

Leave a Comment

Filed under Code sample

The Artist-CD-Track sample

The original Artist-CD-Track sample can be found here:  http://search.cpan.org/dist/DBIx-Class/lib/DBIx/Class/Manual/Example.pod.

The Muldis-D translation can be found here: http://muldis.com/CD.html.

The Andl implementation is below. Note that they are not strictly comparable because:

  1. Andl provides no persistence. The results are only in memory.
  2. Andl provides limited output formatting.
artist  := {{ artistid:=0, name:='' }} [false]
cd      := {{ cdid:=0, artistid:=0, title:='', year:=0 }} [false]
track   := {{ trackid:=0, cdid:= 0, title:='' }} [false]

artist_data := {
  { name := 'Michael Jackson'}, 
  { name := 'Eminem' }
} [{ *artistid := ord() }]
artist := artist union artist_data

cd_data := {
  { title := 'Thriller',                name := 'Michael Jackson' },
  { title := 'Bad',                     name := 'Michael Jackson' },
  { title := 'The Marshall Mathers LP', name := 'Eminem' }
} [{ *cdid := ord() }]
cd := cd union (cd_data join artist)[{ title, cdid, artistid, year:=0}]

track_data := {
   { title := 'Beat It'         , cd := 'Thriller' },
   { title := 'Billie Jean'     , cd := 'Thriller' },
   { title := 'Dirty Diana'     , cd := 'Bad' },
   { title := 'Smooth Criminal' , cd := 'Bad' },
   { title := 'Leave Me Alone'  , cd := 'Bad' },
   { title := 'Stan'            , cd := 'The Marshall Mathers LP' },
   { title := 'The Way I Am'    , cd := 'The Marshall Mathers LP' }
 } [{ *trackid := ord() }]
track := track union (track_data join cd[{ *cd := title }]) [{ trackid, title, cdid }]

get_tracks_by_cd(t) => cd[ title = t {*title} ] join track
get_tracks_by_artist(a) => (artist[ name = a {*name}] join cd) [{cdid}] join track
get_cd_by_track(t) => track [ title = t {cdid} ] join cd
get_cds_by_artist(a) => artist [name = a {artistid} ] join cd
get_artist_by_track(t) => (track [title = t { cdid }] join cd) [{artistid}] join artist
get_artist_by_cd(t) => (cd [title = t { cdid }] join cd) [{artistid}] join artist

get_tracks_by_cd('Bad') [{ i'Track title' := title }]
get_tracks_by_artist('Michael Jackson') [{ i'Track title' := title }]
get_cd_by_track('Stan') [{ i'CD title' := title }]
get_cds_by_artist('Michael Jackson') [{ i'CD title' := title }]
get_artist_by_track('Dirty Diana') [{ i'Artist name' := name }]
get_artist_by_cd('The Marshall Mathers LP') [{ i'Artist name' := name }]

The output looks like this.

Track title
---------------
Dirty Diana
Smooth Criminal
Leave Me Alone

Track title
---------------
Beat It
Billie Jean
Dirty Diana
Smooth Criminal
Leave Me Alone

CD title
-----------------------
The Marshall Mathers LP

CD title
----------
Thriller
Bad

Artist name
---------------
Michael Jackson

Artist name
-----------
Eminem

Leave a Comment

Filed under Code sample