[Dbix-class] FYI: Muldis D code example, 2008 April 30 edition
Darren Duncan
darren at darrenduncan.net
Thu May 1 09:13:34 BST 2008
All,
This message is a follow-up both to my April 28th post of subject "ANNOUNCE
- Muldis D v0.27.0 released", wherein I promised this message would soon
follow, and also follows up a few other posts over the past year where I
gave some pseudo-code examples.
At the end of this email is an example program written in Muldis D, which
you can peruse to get familiar with what the language is like than just
descriptions would do.
The example program will create a temporary depot/database, install a
stored procedure in it, and invoke that stored procedure. The stored
procedure will prompt the user to enter details for 3 people, specifically
their names and addresses, which it will store in a relvar/table. It will
then prompt the user to enter a person's name to search for, look for a
matching person's details in the relvar/table, and then inform the user of
that person's address. Note that for brevity this example program is naive
does not check that the search name actually matched 1 person, and simply
tries to use the result, meaning it will die if that expectation isn't met;
a real program would do more testing.
The program is written in the PTMD_Tiny dialect of Muldis D, which is
designed to exactly match the system catalog in structure. The majority of
it, the stored procedure definition, consists of a single tuple/record for
a catalog relvar/table, and some attributes/fields of that tuple/record
have collection-typed values, several levels deep. This Muldis D code
should resemble the AST you get after parsing a typical language's code and
so is fairly verbose. Though verbose, it should work well in a situation
where such code is mainly being generated or parsed or modified by a
program, though doing it by hand should be tolerable. The dialect is
designed to be parseable with a tiny grammar, which is where its name comes
from. Muldis D is homoiconic, meaning Muldis D code fundamentally is a low
level Muldis D data type, and you do all coding in it by manipulating data.
I assume that other Muldis D dialects will come about later where coding in
it is a lot less verbose, at the cost of the grammar being more
complicated, and code will translate between dialects. Or alternately the
Tiny dialect may just be used as an intermediate language for a programming
environment that, say, takes some specialized DWIM language as input, and
by way of the intermediate, generate SQL for any SQL DBMS to do the work.
So if there are any complaints about verbosity, I can probably do more to
get that down in these dialects, but after a point it would probably be
better to use some translation layer and write to that instead.
Now the PTMD_Tiny dialect happens to be well handled by syntax colorers for
the Perl language, but it isn't Perl. If you are a Perl programmer, you
would probably be more interested in the HDMD_Perl_Tiny dialect, which
looks very much like PTMD_Tiny, but consists of actual Perl arrays of
arrays. In fact, to translate from what you see below to the Perl version,
basically just do this:
- any "Foo:'bar':'baz'" gets changed to "[ 'Foo', 'bar', 'baz' ]"
- any "\q" gets changed to "\'", or use alternate string quoting
characters for string literals
- for payloads of Relation and Set, change the delimiters to "[]" from
"{}" since Perl 5 doesn't have set syntax so use array syntax instead
- the framing code, the 'boot_call' etc need to be replaced with API
calls to some Muldis D implementing Perl module such as Muldis Rosetta
- for a further guide, just compare the docs for the 2 dialects, ad all
the code examples directly correspond
Of course you don't have to use Perl, you could use some other host
language instead as is your preference, as the structures map to any decent
language easily enough. Both functional and imperative languages should
work with it.
Now the program example you see below is complete, with all framing code
needed to compile the plain text dialect, probably.
In the near future, I'll follow with another example email that just shows
snippits rather than a complete program, and the snippits can focus on
particular tasks such as doing joins or unions or defining database schemas
etc.
On another note, considering my OSCON talk is July 23, I've set myself up
with a hard deadline of July 1, and a soft deadline of June 1, to have an
actual working implementation of the language. The only main prerequisite
to making that is defining how to declare data types and schemas in the
language, but that should be done in a few days I expect.
-- Darren Duncan
--------------
Muldis_D:'http://muldis.com':'0.28.0':'PTMD_Tiny':{};
boot_call:'sys.std.Core.Cat.create_temp_empty_depot':{
'mount_name' => 'app',
};
boot_call:'sys.std.Core.Cat.create_depot_procedure':{
'depot' => 'app',
'parent' => Cat.DeclNameChain:'.',
'name' => 'main',
'comment' => Cat.Comment:'This is the program\qs main procedure.',
'head' => Tuple:{},
'body' => Tuple:{
'main_body' => Tuple:{
'vars' => Relation:[ 'name', 'type', ]:{
[ 'msg_gather_name', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'msg_gather_addr', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'msg_search_name', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'msg_result_addr', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'people', Cat.NameChain:'sys.std.Core.Type.Relation', ],
[ 'person_name', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'person_addr', Cat.NameChain:'sys.std.Core.Type.Text', ],
},
'stmts' => Seq:[
Tuple:{
'procedure' => Cat.NameChain:'inn.init_vars',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'msg_gather_name', Cat.NameChain:'lex.msg_gather_name', ],
[ 'msg_gather_addr', Cat.NameChain:'lex.msg_gather_addr', ],
[ 'msg_search_name', Cat.NameChain:'lex.msg_search_name', ],
[ 'msg_result_addr', Cat.NameChain:'lex.msg_result_addr', ],
[ 'people', Cat.NameChain:'lex.people', ],
},
},
Tuple:{
'procedure' => Cat.NameChain:'inn.gather_person',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'people', Cat.NameChain:'lex.people', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'msg_gather_name', Cat.NameChain:'lex.msg_gather_name', ],
[ 'msg_gather_addr', Cat.NameChain:'lex.msg_gather_addr', ],
},
},
Tuple:{
'procedure' => Cat.NameChain:'inn.gather_person',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'people', Cat.NameChain:'lex.people', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'msg_gather_name', Cat.NameChain:'lex.msg_gather_name', ],
[ 'msg_gather_addr', Cat.NameChain:'lex.msg_gather_addr', ],
},
},
Tuple:{
'procedure' => Cat.NameChain:'inn.gather_person',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'people', Cat.NameChain:'lex.people', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'msg_gather_name', Cat.NameChain:'lex.msg_gather_name', ],
[ 'msg_gather_addr', Cat.NameChain:'lex.msg_gather_addr', ],
},
},
Tuple:{
'procedure' => Cat.NameChain:'sys.std.Core.STDIO.prompt_Text_line',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'target', Cat.NameChain:'lex.person_name', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'prompt', Cat.NameChain:'lex.msg_search_name', ],
},
},
Tuple:{
'procedure' => Cat.NameChain:'inn.search_for_address',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'person_addr', Cat.NameChain:'lex.person_addr', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'people', Cat.NameChain:'lex.people', ],
[ 'person_name', Cat.NameChain:'lex.person_name', ],
},
},
Tuple:{
'procedure' => Cat.NameChain:'sys.std.Core.STDIO.write_Text',
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'v', Cat.NameChain:'lex.msg_result_addr', ],
},
},
Tuple:{
'procedure' => Cat.NameChain:'sys.std.Core.STDIO.write_Text_line',
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'v', Cat.NameChain:'lex.person_addr', ],
},
},
],
},
'inner_procs' => Relation:{
{
'name' => 'gather_person',
'comment' => Cat.Comment:'Gathers person info from user.',
'head' => Tuple:{
'upd_params' => Relation:[ 'name', 'type', ]:{
[ 'people', Cat.NameChain:'sys.std.Core.Type.Relation', ],
},
'ro_params' => Relation:[ 'name', 'type', ]:{
[ 'msg_gather_name', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'msg_gather_addr', Cat.NameChain:'sys.std.Core.Type.Text', ],
},
},
'body' => Tuple:{
'vars' => Relation:[ 'name', 'type', ]:{
[ 'person_name', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'person_addr', Cat.NameChain:'sys.std.Core.Type.Text', ],
},
'stmts' => Seq:[
Tuple:{
'procedure' =>
Cat.NameChain:'sys.std.Core.STDIO.prompt_Text_line',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'target', Cat.NameChain:'lex.person_name', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'prompt', Cat.NameChain:'lex.msg_gather_name', ],
},
},
Tuple:{
'procedure' =>
Cat.NameChain:'sys.std.Core.STDIO.prompt_Text_line',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'target', Cat.NameChain:'lex.person_addr', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'prompt', Cat.NameChain:'lex.msg_gather_addr', ],
},
},
Tuple:{
'procedure' => Cat.NameChain:'inn.add_person',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'people', Cat.NameChain:'lex.people', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'person_name', Cat.NameChain:'lex.person_name', ],
[ 'person_addr', Cat.NameChain:'lex.person_addr', ],
},
},
],
},
},
},
'inner_upds' => Relation:{
{
'name' => 'init_vars',
'comment' => Cat.Comment:'Initializes main routine\qs variables.',
'head' => Tuple:{
'upd_params' => Relation:[ 'name', 'type', ]:{
[ 'msg_gather_name', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'msg_gather_addr', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'msg_search_name', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'msg_result_addr', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'people', Cat.NameChain:'sys.std.Core.Type.Relation', ],
},
},
'body' => Tuple:{
'exprs' => Tuple:{
'sca_lit_exprs' => Relation:[ 'name', 'value', ]:{
[ 'emgn', Text:'Enter a person\qs name: ', ],
[ 'emga', Text:'Enter that person\qs address: ', ],
[ 'emsn', Text:'Enter a name to search for: ', ],
[ 'emrn', Text:'Found address for that person is: ', ],
},
'rel_lit_exprs' => Relation:{
{
'name' => 'ep',
'head' => Set:{ 'person_name', 'person_addr', },
'body' => Set:{},
},
},
},
'stmt' => Relation:{
{
'updater' => Cat.NameChain:'sys.std.Core.Universal.assign',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'target', Cat.NameChain:'lex.msg_gather_name', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'v', Cat.NameChain:'lex.emgn', ],
},
},
{
'updater' => Cat.NameChain:'sys.std.Core.Universal.assign',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'target', Cat.NameChain:'lex.msg_gather_addr', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'v', Cat.NameChain:'lex.emga', ],
},
},
{
'updater' => Cat.NameChain:'sys.std.Core.Universal.assign',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'target', Cat.NameChain:'lex.msg_search_name', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'v', Cat.NameChain:'lex.emsn', ],
},
},
{
'updater' => Cat.NameChain:'sys.std.Core.Universal.assign',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'target', Cat.NameChain:'lex.msg_result_addr', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'v', Cat.NameChain:'lex.emra', ],
},
},
{
'updater' => Cat.NameChain:'sys.std.Core.Universal.assign',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'target', Cat.NameChain:'lex.people', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'v', Cat.NameChain:'lex.ep', ],
},
},
},
},
},
{
'name' => 'add_person',
'comment' => Cat.Comment:'Adds a person to our db of people.',
'head' => Tuple:{
'upd_params' => Relation:[ 'name', 'type', ]:{
[ 'people', Cat.NameChain:'sys.std.Core.Type.Relation', ],
},
'ro_params' => Relation:[ 'name', 'type', ]:{
[ 'person_name', Cat.NameChain:'sys.std.Core.Type.Text', ],
[ 'person_addr', Cat.NameChain:'sys.std.Core.Type.Text', ],
},
},
'body' => Tuple:{
'exprs' => Tuple:{
'tup_lit_exprs' => Relation:{
{
'name' => 'person',
'attrs' => Relation:[ 'name', 'expr', ]:{
[ 'person_name', Cat.NameChain:'lex.person_name', ],
[ 'person_addr', Cat.NameChain:'lex.person_addr', ],
},
},
},
},
'stmt' => Relation:{
{
'updater' =>
Cat.NameChain:'sys.std.Core.Relation.assign_insertion',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'r', Cat.NameChain:'lex.people', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 't', Cat.NameChain:'lex.person', ],
},
},
},
},
},
{
'name' => 'search_for_address',
'comment' => Cat.Comment:'Look up person name, get their address.',
'head' => Tuple:{
'upd_params' => Relation:[ 'name', 'type', ]:{
[ 'person_addr', Cat.NameChain:'sys.std.Core.Type.Text', ],
},
'ro_params' => Relation:[ 'name', 'type', ]:{
[ 'people', Cat.NameChain:'sys.std.Core.Type.Relation', ],
[ 'person_name', Cat.NameChain:'sys.std.Core.Type.Text', ],
},
},
'body' => Tuple:{
'exprs' => Tuple:{
'rel_lit_exprs' => Relation:{
{
'name' => 'person_name_r',
'head' => Set:{ 'person_name', },
'body' => Set:{
Relation:[ 'name', 'expr', ]:{
[ 'person_name', Cat.NameChain:'lex.person_name', ],
},
},
},
},
'func_invo_exprs' => Relation:{
{
'name' => 'matched_people',
'function' => Cat.NameChain:'sys.std.Core.Relation.semijoin',
'args' => Relation:[ 'name', 'expr', ]:{
[ 'source', Cat.NameChain:'lex.people', ],
[ 'filter', Cat.NameChain:'lex.person_name_r', ],
},
},
{
'name' => 'matched_person',
'comment' => 'Note, we\qre assuming exactly one match.',
'function' =>
Cat.NameChain:'sys.std.Core.Relation.Tuple_from_Relation',
'args' => Relation:[ 'name', 'expr', ]:{
[ 'topic', Cat.NameChain:'lex.matched_person', ],
},
},
},
},
'stmt' => Relation:{
{
'updater' => Cat.NameChain:'sys.std.Core.Universal.assign',
'upd_args' => Relation:[ 'name', 'expr', ]:{
[ 'target', Cat.NameChain:'lex.person_addr', ],
},
'ro_args' => Relation:[ 'name', 'expr', ]:{
[ 'v', Cat.NameChain:'lex.matched_person.person_addr', ],
},
},
},
},
},
},
},
};
boot_call:'fed.lib.app.main':{};
More information about the DBIx-Class
mailing list