From darren at darrenduncan.net Wed Apr 8 01:08:14 2009 From: darren at darrenduncan.net (Darren Duncan) Date: Wed, 08 Apr 2009 01:08:14 -0700 Subject: [VPM] FYI - modern Muldis D code examples Message-ID: <49DC5B6E.3000809@darrenduncan.net> Hello, If any of you have been confused in regards to understanding my periodically touted Muldis D language, which is probably nearly everyone, I've got something here today that may make understanding it a lot easier: some realistic example code, of which I've got about two dozen examples translated from SQL. To recap, Muldis D is my new programming language part of whose purpose is to eventually succeed SQL as the query+DDL language of choice for relational DBMSs, in the same manner that Perl 6 is intended to eventually supplant Perl 5. (And yes, I am being pragmatic and realize it won't happen overnight.) The design of Muldis D is like a cross between SQL and Perl 6 and it should be easy to learn. Muldis D is also intended for use today as a source of design ideas for building toolkits that interface between a DBMS and an application, particularly ones that want to go beyond current solutions and offer portable parsing and generation of SQL stored routines and schemas including such, and Muldis D can be a primary inspiration for the design of intermediate representations of both SQL data and code that are widely portable. Muldis D is rigorous enough that one could just implement it literally (which I intend to do separately), but you don't actually have to understand even most of it to glean useful ideas from it and save yourself from reinventing the wheel in inferior ways. For context, http://search.cpan.org/dist/Muldis-D/ is where I've published the rigorous definition of the language, which you can use as a reference later. I consider the language spec to be maybe 90% done, and the single largest remaining thing to rigorously define is its concrete syntax for everything except value literals, which are done. So most of this email consists of a number of SQL examples taken from the new manual for the SQL::Abstract 2.0 Perl module, and for each a translation into concrete PTMD_Tiny dialect Muldis D code (currently going beyond the scope of what is rigorously defined). For the original SQL examples, go to http://github.com/ashb/sql-abstract/tree/0f93e5f7e64a55f293efd70c2769d1fdbe22da38/lib/SQL/Abstract/Manual and look at Examples.pod. As a simple point of explanation, at the top level all Muldis D code takes the form of either a value literal or an invocation of a routine or a definition of a routine. So if you were using the Perl DBI module to perform a query or run transient DML code, passed to its prepare() method, what you pass would be typically a procedure definition, and DBI's bind parameters for the query would bind to the declared parameters of that procedure, which are typically named (rather than positional question marks), like some SQL DBMSs spell out :param or @param etc. Another detail is that for a routine representing a SELECT, the result comes back via a subject-to-update/INOUT parameter; it does not come back out of band, and so is more like how a normal programming language works. Now this syntax isn't fully complete, but I figure it is maybe 80% like what the final version would be. Also let me emphasize that these examples are in canonical character string form, same as SQL code. While the Muldis D spec also specifies a version in terms of Perl structures ala SQL::Abstract et al, that isn't illustrated here; however that version would closely resemble a concrete syntax tree from parsing the version below. The true AST of Muldis D, which is fully (or 95+%) specced, is its system catalog, analogous to the SQL INFORMATION_SCHEMA but that all code is decomposed, including routine and view etc definitions. That said, the canonical Muldis D code intentionally has very simple grammar, so it should be easy to parse or generate; it should also resemble its lower level AST form enough that details like named expression nodes and the separation of pure from impure code shows up often. -- Darren Duncan ========== SQL: SELECT 1 Muldis D Text: function func1 (NNInt <--) { main { 1; } } or: function func1 (Relation <--) { main { Relation:{ { attr1 => 1 } }; } } ========== SQL: SELECT NOW() AS time FROM dual AS duality Muldis D Text: procedure proc1 (Instant &$time) { main { fetch_current_instant( target => $time ); } } or: procedure proc1 (Relation &$r) { main { var Instant $now; fetch_current_instant( target => $now ); inn.wrapup( &r => $r, now => $now ); } inner_updater wrapup (Relation &$r, Instant $now) { $r := Relation:{ { time => $now } }; } } ========== SQL: SELECT 1 FROM foo LEFT OUTER JOIN bar ON ( foo.col1 = bar.col2 ) Muldis D Text: # Note: This example assumes neither of foo|bar have col named colJ. # procedure proc1 (Relation &$result) { main { inn.query( &result => $result, db => $fed.data.db1 ); } inner_updater query (Relation &$result, Database $db) { $primary := ($db.foo{col1=>colJ}); # aka rename() # $secondary := ($db.bar{col2=>colJ}); $outer_join := outer_join_with_maybes( primary => $primary, secondary => $secondary ); $null_proj := ($outer_join{}); $result := static_extension( topic => $null_proj, attrs => Tuple:{ attr1 => 1 } ); } } or, if we simply want to know if there were matching rows in foo and bar: procedure proc1 (Bool &$result) { main { inn.query( &result => $result, db => $fed.data.db1 ); } inner_updater query (Bool &$result, Database $db) { $primary := ($db.foo{col1=>colJ}); # aka rename() # $secondary := ($db.bar{col2=>colJ}); $join := ($primary join $secondary); $result := is_not_empty( topic => $join ); } } ========== SQL: SELECT * FROM foo WHERE name = 'John' Muldis D Text (with the data to match on hard-coded): procedure proc1 (Relation &$result) { main { inn.query( &result => $result, db => $fed.data.db1 ); } inner_updater query (Relation &$result, Database $db) { $filter := Relation:{ { name => 'John' } }; $result := semijoin( source => $db.foo, filter => $filter ); } } or, with the data to match on in a bind var: procedure proc1 (Relation &$result, Text $name) { main { inn.query( &result => $result, db => $fed.data.db1, name => $name ); } inner_updater query (Relation &$result, Database $db, Text $name) { $filter := Relation:{ { name => $name } }; $result := semijoin( source => $db.foo, filter => $filter ); } } or: procedure proc1 (Relation &$result, Relation $filter) { main { inn.query( &result => $result, db => $fed.data.db1, filter => $filter ); } inner_updater query (Relation &$result, Database $db, Relation $filter) { $result := semijoin( source => $db.foo, filter => $filter ); } } ========== SQL: SELECT COUNT(*) FROM foo WHERE name = 'John' AND ( title = 'Mr' OR abbrev = 'Dr' ) Muldis D Text: procedure proc1 (NNInt &$count) { main { inn.query( &count => $count, db => $fed.data.db1 ); } inner_updater query (NNInt &$count, Database $db) { $filtered := restriction( topic => $db.foo, func => F->inn.filter ); $count := cardinality( topic => $filtered ); } inner_function filter (Bool <-- Tuple $topic) { (($topic.name === 'John') and (($topic.title === 'Mr') or ($title.abbrev === 'Dr'))) } } ========== SQL: SELECT COUNT(DISTINCT(*)) FROM foo WHERE ( name = 'John' AND title = 'Mr' ) OR abbrev = 'Dr' Muldis D Text: # Note: Above DISTINCT not translated as all ops are set-based. # procedure proc1 (NNInt &$count) { main { inn.query( &count => $count, db => $fed.data.db1 ); } inner_updater query (NNInt &$count, Database $db) { $filtered := restriction( topic => $db.foo, func => F->inn.filter ); $count := cardinality( topic => $filtered ); } inner_function filter (Bool <-- Tuple $topic) { ((($topic.name === 'John') and ($topic.title === 'Mr')) or ($title.abbrev === 'Dr')) } } ========== SQL: SELECT foo, bar, baz FROM foo ORDER BY bar, baz DESC GROUP BY 1,3,2 Muldis D Text: # Note: Above GROUP BY not translated as it's a no-op. # procedure proc1 (Array &$result) { main { inn.query( &result => $result, db => $fed.data.db1 ); } inner_updater query (Array &$result, Database $db) { $proj := ($db.foo{foo,bar,baz}); # aka projection() # $result := QArray_from_wrap( topic => $proj, ord_func => F->inn.qsort ); } inner_function qsort (Order <-- Tuple $topic, Tuple $other) { $e1 := ($topic.bar <=> $other.bar); $e2 := ($other.baz <=> $topic.baz); $e3 := Array:[ $e1, $e2 ]; $'' := Order_reduction( topic => $e3 ); } } ========== SQL: SELECT * FROM ( SELECT 1 ) AS foo Muldis D Text: function func1 (Relation <--) { main { $foo := Relation:{ { attr1 => 1 } }; $'' := $foo; } } ========== SQL: INSERT INTO foo ( col1, col2 ) VALUES ( 1, 3 ) Muldis D Text (with data to insert hard-coded): procedure proc1 () { main { var Tuple $t; inn.init_t( &t => $t ); assign_insertion( &r => $fed.data.db1.foo, t => $t ); } inner_updater init_t (Tuple &$t) { $t := Tuple:{ col1 => 1, col2 => 2 }; } } or, with the data to insert as a bind var: procedure proc1 (type.tuple_from.fed.data.db1.foo $t) { main { assign_insertion( &r => $fed.data.db1.foo, t => $t ); } } ========== SQL: INSERT INTO foo ( col1, col2 ) VALUES ( 1, 3 ), ( 2, 4 ) Muldis D Text (with data to insert hard-coded): procedure proc1 () { main { var Relation $r; inn.init_r( &r => $r ); assign_union( &topic => $fed.data.db1.foo, other => $r ); } inner_updater init_r (Relation &$r) { $r := Relation:[col1,col2];{ [1,3], [2,4] }; } } or, with the data to insert as a bind var: procedure proc1 (type.fed.data.db1.foo $r) { main { assign_union( &topic => $fed.data.db1.foo, other => $r ); } } ========== SQL: UPDATE foo SET col1 = 1 Muldis D Text (with data to substitute hard-coded): procedure proc1 () { main { var Tuple $attrs; inn.init_attrs( &attrs => $attrs ); assign_static_substitution( &topic => $fed.data.db1.foo, attrs => $attrs ); } inner_updater init_attrs (Tuple &$attrs) { $attrs := Tuple:{ col1 => 1 }; } } or, with the data to substitute as a bind var: procedure proc1 (Tuple $attrs) { main { assign_static_substitution( &topic => $fed.data.db1.foo, attrs => $attrs ); } } or: procedure proc1 (Int $col1) { main { var Tuple $attrs; inn.init_attrs( &attrs => $attrs, col1 => $col1 ); assign_static_substitution( &topic => $fed.data.db1.foo, attrs => $attrs ); } inner_updater init_attrs (Tuple &$attrs, Int $col1) { $attrs := Tuple:{ col1 => $col1 }; } } ========== SQL: UPDATE foo SET col1 = 1, col2 = 6 Muldis D Text (with data to substitute hard-coded): procedure proc1 () { main { var Tuple $attrs; inn.init_attrs( &attrs => $attrs ); assign_static_substitution( &topic => $fed.data.db1.foo, attrs => $attrs ); } inner_updater init_attrs (Tuple &$attrs) { $attrs := Tuple:{ col1 => 1, col2 => 6 }; } } or, with the data to substitute as a bind var: procedure proc1 (Tuple $attrs) { main { assign_static_substitution( &topic => $fed.data.db1.foo, attrs => $attrs ); } } or: procedure proc1 (Int $col1, Int $col2) { main { var Tuple $attrs; inn.init_attrs( &attrs => $attrs, col1 => $col1, col2 => $col2 ); assign_static_substitution( &topic => $fed.data.db1.foo, attrs => $attrs ); } inner_updater init_attrs (Tuple &$attrs, Int $col1, Int $col2) { $attrs := Tuple:{ col1 => $col1, col2 => $col2 }; } } ========== SQL: DELETE FROM foo WHERE col1 = 10 Muldis D Text (with data to filter by hard-coded): procedure proc1 () { main { var Relation $filter; inn.init_filter( &filter => $filter ); assign_semidifference( &source => $fed.data.db1.foo, filter => $filter ); } inner_updater init_filter (Relation &$filter) { $filter := Relation:{ { col1 => 10 } }; } } or, with the data to filter by as a bind var: procedure proc1 (Relation $filter) { main { assign_semidifference( &source => $fed.data.db1.foo, filter => $filter ); } } or: procedure proc1 (Int $col1) { main { var Relation $filter; inn.init_filter( &filter => $filter, col1 => $col1 ); assign_semidifference( &source => $fed.data.db1.foo, filter => $filter ); } inner_updater init_filter (Relation &$filter, Int $col1) { $filter := Relation:{ { col1 => $col1 } }; } } ========== SQL: INSERT INTO foo ( col1, col2 ) SELECT col1, col2 FROM bar; Muldis D Text (if bar has more than 2 attributes): procedure proc1 () { main { var Relation $bar_proj; inn.projcol12( &bar_proj => $bar_proj, bar => $fed.data.db1.bar ); assign_union( &topic => $fed.data.db1.foo, other => $bar_proj ); } inner_updater projcol12 (Relation &$bar_proj, Relation $bar) { $bar_proj := ($bar{col1,col2}); } } or, if bar has only the 2 attributes: procedure proc1 () { main { assign_union( &topic => $fed.data.db1.foo, other => $fed.data.db1.bar ); } } ========== From darren at darrenduncan.net Thu Apr 9 03:59:12 2009 From: darren at darrenduncan.net (Darren Duncan) Date: Thu, 09 Apr 2009 03:59:12 -0700 Subject: [VPM] FYI - modern Muldis D code examples In-Reply-To: <49DC5B6E.3000809@darrenduncan.net> References: <49DC5B6E.3000809@darrenduncan.net> Message-ID: <49DDD500.80205@darrenduncan.net> Hello, This message is an update to my post from yesterday of the same subject. If you don't have it handy, then a copy is archived at http://mm.darrenduncan.net/pipermail/muldis-db-devel/2009-April/000023.html . I have significantly updated the Muldis D code samples of yesterday and the new versions are included at the end of this message. The new versions have exactly the same meaning (that is, translate the same SQL) but are much better formed; please ignore the old versions in favor of the new. The first main difference is correcting an unintentional large error. In all places except the NOW() example where I used a "var Type $foo", such as: procedure proc1 () { main { var Tuple $t; inn.init_t( &t => $t ); assign_insertion( &r => $fed.data.db1.foo, t => $t ); } inner_updater init_t (Tuple &$t) { $t := Tuple:{ col1 => 1, col2 => 2 }; } } ... I actually meant to not use a var, such as: procedure proc1 () { main { inn.do_insert( &foo => $fed.data.db1.foo ); } inner_updater do_insert (Relation &$foo) { assign_insertion( &r => $foo, t => Tuple:{ col1 => 1, col2 => 2 } ); } } So for those cases please treat the latter style as what I intended in the first place. Aside from that, what was posted before is what I intended to say; however I've now made a variety of improvements: 1. No "main { }" is necessary when the routine has no inner routines. 2. An arg name can be omitted when invoking a routine with just one param. 3. The ":=" syntax that used to mean bind a name/alias to a value expression (a "with" clause) is now spelled "=", same as it is in the Haskell language. 4. Now ":=" only is an alt syntax for invoking the "assign" updater routine. 5. Added alternate / terser syntax for several more built-in functions; for example, "semijoin" now has an infix alternate named "matching", and "restriction" now has an infix alternate named "where". 6. Inlined most value exprs so very few explicitly named ones are left. I also expect many more such improvements, particularly like #5, will come about, but I haven't figured out yet what format they would take. Now, following some feedback I got mainly from one person, I'll offer a few FAQ items in case you're confused by some things: 1. In regards to how compact these Muldis D code examples look compared to the original SQL, code compactness varies depending on use cases, and sometimes the Muldis D code would be smaller than the SQL code. You would notice this more as the SQL examples get more complicated (submissions of sample SQL code to translate welcome); SQL would no longer seem smaller. The Muldis D code is also huffmanized more around better practices, for example using bind parameters versus hard-coding your data. 2. Muldis D tries to be more authentic with its terminology, so words like "relation" are used here in the way that DBAs and makers of DBMSs use them, or people who are familiar with the logic or math behind the relational model. See http://en.wikipedia.org/wiki/Relational_model if it helps. So a "relation" *is* a value, same as an "array" is a value, and a relation type can be the type of a parameter or result or variable. When you see "relation" think "rowset", or when you see "relvar / relation-typed variable" think "table". Note that to try and head off such understandings, the Basics.pod of the Muldis D spec starts off with a glossary of common or commonly misinterpreted terms. (Don't be confused by terminology used with some ORMs that give a different meaning to "relation".) 3. Muldis D does not have any reserved words at all, and doesn't stop you from naming your types/routines/vars/attrs/etc whatever you want (with very few exceptions). However, all named things live in namespaces, and thus this freedom doesn't cause any interference between things a user defines and things that are built-in to the language. In many cases, spelling out the names of things in full is optional, and so the code examples just use the unqualified versions for brevity. To be specific, names must be invoked in their full form except in two specific cases; the first is if they are system-defined entities that aren't variables, and the other is if they are lexicals (that are variables). So, wherever you see a plain "$foo" it can be read as "$lex.foo", and any plain "op()" can be read as "sys.std.*.op()". And so, for example this: procedure proc1 (Instant &$time) { fetch_current_instant( &$time ); } ... with fully spelled out names is: procedure proc1 (sys.std.Core.Type.Instant &$time) { sys.std.Temporal.Instant.fetch_current_instant( $lex.time ); } 4. The concept of "inner routines" is just an artifact of Muldis D's design, which enforces clear separation of pure and impure code (functions and updaters are pure, deterministic, and atomic; procedures are not), and how it represents nested distinct lexical scopes/blocks, or conceptually inline-defined closures etc, as well as how it supports internal recursion, as well as one way to keep the grammar/parsers/generators simpler. So when you see "inner routine", think "inlined closure" or "nested block" or "pure section". Now, you don't actually have to use inner routines; those items could be declared as full routines instead, but inners save you from polluting public namespaces, and really are the closest analogy to languages with actual inlining. 5. Muldis D can inference types, but it still uses explicit type declarations in code partly to aid type checking of code in isolation as well aid in self-documentation. Types like "Relation" and "Tuple" are actually quite broad and in practice you may often declare with explicit subset types of those. 6. Muldis D code *is* declarative. While routines might look sometimes like an ordered sequence of steps to follow, they really are just a description of what result is desired. In particular, Muldis D is designed such that most code would be forced into pure sections, and so a DBMS is very much empowered to optimize it. 7. As a general explanation for why the Muldis D examples looks the way they do ... One of Muldis D's main features is that it is homoiconic, meaning that its code is also data, and you can introspect or alter code at runtime. This manifests mainly with the special global variables called the system catalog, which serve the same function as SQL's INFORMATION_SCHEMA but that mine breaks down everything and doesn't just store routines etc as code strings. Now I designed Muldis D starting at the AST level, meaning as it is represented in the system catalog, figuring out how to effectively represent all types and data and routines as an abstract syntax tree, which can also be the target format of parsers and the source format for code generators. Using this syntax tree form as a starting point, I have then been creating a concrete Muldis D syntax, "PTMD_Tiny" that can map to the AST / system catalog more or less 1:1, and this concrete syntax is what the code examples are in. So the concrete syntax is really just a layer of sugar over the AST. Over time I will continue to add more sugar to make the concrete syntax sufficiently terse and easier to use. Meanwhile, what you see is how far along that road of adding sugar I got to. 8. Keep in mind that my example code is meant to be a more literal translation of what the SQL is saying. If the SQL spelled out columns to return, so does my example. If the SQL said return all columns, then mine doesn't spell them out. Anyway, thank you for your time. -- Darren Duncan ========== SQL: SELECT 1 Muldis D Text: function func1 (NNInt <--) { 1; } or: function func1 (Relation <--) { Relation:{ { attr1 => 1 } }; } ========== SQL: SELECT NOW() AS time FROM dual AS duality Muldis D Text: procedure proc1 (Instant &$time) { fetch_current_instant( &$time ); } or: procedure proc1 (Relation &$r) { main { var Instant $now; fetch_current_instant( &$now ); inn.wrapup( &r => $r, now => $now ); } inner_updater wrapup (Relation &$r, Instant $now) { $r := Relation:{ { time => $now } }; } } ========== SQL: SELECT 1 FROM foo LEFT OUTER JOIN bar ON ( foo.col1 = bar.col2 ) Muldis D Text: # Note: This example assumes neither of foo|bar have a col named colJ, and moreover that foo|bar have no other col names in common. # procedure proc1 (Relation &$result) { main { inn.query( &result => $result, db => $fed.data.db1 ); } inner_updater query (Relation &$result, Database $db) { $outer_join = outer_join_with_maybes( primary => ($db.foo{col1=>colJ}), secondary => ($db.bar{col2=>colJ}) ); $result := static_extension( topic => ($outer_join{}), attrs => Tuple:{ attr1 => 1 } ); } } or, if we want the result of the outer join itself: procedure proc1 (Relation &$result) { main { inn.query( &result => $result, db => $fed.data.db1 ); } inner_updater query (Relation &$result, Database $db) { $result := outer_join_with_maybes( primary => ($db.foo{col1=>colJ}), secondary => ($db.bar{col2=>colJ}) ); } } or, if we simply want to know if there were matching rows in foo and bar: procedure proc1 (Bool &$result) { main { inn.query( &result => $result, db => $fed.data.db1 ); } inner_updater query (Bool &$result, Database $db) { $result := is_not_empty( (($db.foo{col1=>colJ}) join ($db.bar{col1=>colJ})) ); } } ========== SQL: SELECT * FROM foo WHERE name = 'John' Muldis D Text (with the data to match on hard-coded): procedure proc1 (Relation &$result) { main { inn.selfoo( &result => $result, foo => $fed.data.db1.foo ); } inner_updater selfoo (Relation &$result, Relation $foo) { $result := ($foo matching Relation:{ { name => 'John' } }); } } or, with the data to match on in a bind var: procedure proc1 (Relation &$result, Text $name) { main { inn.selfoo( &result => $result, foo => $fed.data.db1.foo, name => $name ); } inner_updater selfoo (Relation &$result, Relation $foo, Text $name) { $result := ($foo matching Relation:{ { name => $name } }); } } or: procedure proc1 (Relation &$result, Relation $filter) { main { inn.selfoo( &result => $result, foo => $fed.data.db1.foo, filter => $filter ); } inner_updater selfoo (Relation &$result, Relation $foo, Relation $filter) { $result := ($foo matching $filter); } } ========== SQL: SELECT COUNT(*) FROM foo WHERE name = 'John' AND ( title = 'Mr' OR abbrev = 'Dr' ) Muldis D Text: procedure proc1 (NNInt &$count) { main { inn.selfoo( &count => $count, foo => $fed.data.db1.foo ); } inner_updater selfoo (NNInt &$count, Relation $foo) { $count := (cardinality of ($foo where F->inn.filter)); } inner_function filter (Bool <-- Tuple $topic) { (($topic.name === 'John') and (($topic.title === 'Mr') or ($topic.abbrev === 'Dr'))); } } ========== SQL: SELECT COUNT(DISTINCT(*)) FROM foo WHERE ( name = 'John' AND title = 'Mr' ) OR abbrev = 'Dr' Muldis D Text: # Note: Above DISTINCT not translated as all ops are set-based. # procedure proc1 (NNInt &$count) { main { inn.selfoo( &count => $count, foo => $fed.data.db1.foo ); } inner_updater selfoo (NNInt &$count, Relation $foo) { $count := (cardinality of ($foo where F->inn.filter)); } inner_function filter (Bool <-- Tuple $topic) { ((($topic.name === 'John') and ($topic.title === 'Mr')) or ($topic.abbrev === 'Dr')); } } ========== SQL: SELECT foo, bar, baz FROM foo ORDER BY bar, baz DESC GROUP BY 1,3,2 Muldis D Text: # Note: Above GROUP BY not translated as it's a no-op. # procedure proc1 (Array &$result) { main { inn.selfoo( &result => $result, foo => $fed.data.db1.foo ); } inner_updater selfoo (Array &$result, Relation $foo) { $result := (array from ($foo{foo,bar,baz}) ordered using F->inn.sortf); } inner_function sortf (Order <-- Tuple $topic, Tuple $other) { Order_reduction( Array:[ ($topic.bar <=> $other.bar), ($other.baz <=> $topic.baz) ] ); } } ========== SQL: SELECT * FROM ( SELECT 1 ) AS foo Muldis D Text: function func1 (Relation <--) { $foo = Relation:{ { attr1 => 1 } }; $'' = $foo; } ========== SQL: INSERT INTO foo ( col1, col2 ) VALUES ( 1, 3 ) Muldis D Text (with data to insert hard-coded): procedure proc1 () { main { inn.do_insert( &foo => $fed.data.db1.foo ); } inner_updater do_insert (Relation &$foo) { assign_insertion( &r => $foo, t => Tuple:{ col1 => 1, col2 => 2 } ); } } or, with the data to insert as a bind var: procedure proc1 (type.tuple_from.var.fed.data.db1.foo $t) { assign_insertion( &r => $fed.data.db1.foo, t => $t ); } ========== SQL: INSERT INTO foo ( col1, col2 ) VALUES ( 1, 3 ), ( 2, 4 ) Muldis D Text (with data to insert hard-coded): procedure proc1 () { main { inn.do_insert( &foo => $fed.data.db1.foo ); } inner_updater do_insert (Relation &$foo) { assign_union( &topic => $foo, other => Relation:[col1,col2];{ [1,3], [2,4] } ); } } or, with the data to insert as a bind var: procedure proc1 (type.var.fed.data.db1.foo $r) { assign_union( &topic => $fed.data.db1.foo, other => $r ); } ========== SQL: UPDATE foo SET col1 = 1 Muldis D Text (with data to substitute hard-coded): procedure proc1 () { main { inn.do_update( &foo => $fed.data.db1.foo ); } inner_updater do_update (Relation &$foo) { assign_static_substitution( &topic => $foo, attrs => Tuple:{ col1 => 1 } ); } } or, with the data to substitute as a bind var: procedure proc1 (Tuple $attrs) { assign_static_substitution( &topic => $fed.data.db1.foo, attrs => $attrs ); } or: procedure proc1 (Int $col1) { main { inn.do_update( &foo => $fed.data.db1.foo, col1 => $col1 ); } inner_updater do_update (Relation &$foo, Int $col1) { assign_static_substitution( &topic => $foo, attrs => Tuple:{ col1 => $col1 } ); } } ========== SQL: UPDATE foo SET col1 = 1, col2 = 6 Muldis D Text (with data to substitute hard-coded): procedure proc1 () { main { inn.do_update( &foo => $fed.data.db1.foo ); } inner_updater do_update (Relation &$foo) { assign_static_substitution( &topic => $foo, attrs => Tuple:{ col1 => 1, col2 => 6 } ); } } or, with the data to substitute as a bind var: procedure proc1 (Tuple $attrs) { assign_static_substitution( &topic => $fed.data.db1.foo, attrs => $attrs ); } or: procedure proc1 (Int $col1, Int $col2) { main { inn.do_update( &foo => $fed.data.db1.foo, col1 => $col1, col2 => $col2 ); } inner_updater do_update (Relation &$foo, Int $col1, Int $col2) { assign_static_substitution( &topic => $foo, attrs => Tuple:{ col1 => $col1, col2 => $col2 } ); } } ========== SQL: DELETE FROM foo WHERE col1 = 10 Muldis D Text (with data to filter by hard-coded): procedure proc1 () { main { inn.do_delete( &foo => $fed.data.db1.foo ); } inner_updater do_delete (Relation &$foo) { assign_semidifference( &source => $foo, filter => Relation:{ { col1 => 10 } } ); } } or, with the data to filter by as a bind var: procedure proc1 (Relation $filter) { assign_semidifference( &source => $fed.data.db1.foo, filter => $filter ); } or: procedure proc1 (Int $col1) { main { inn.do_delete( &foo => $fed.data.db1.foo, col1 => $col1 ); } inner_updater do_delete (Relation &$foo, Int $col1) { assign_semidifference( &source => $foo, filter => Relation:{ { col1 => $col1 } } ); } } ========== SQL: INSERT INTO foo ( col1, col2 ) SELECT col1, col2 FROM bar; Muldis D Text (if bar has more than 2 attributes): procedure proc1 () { main { inn.do_insert( &foo => $fed.data.db1.foo, bar => $fed.data.db1.bar ); } inner_updater do_insert (Relation &$foo, Relation $bar) { assign_union( &topic => $foo, other => ($bar{col1,col2}) ); } } or, if bar has only the 2 attributes: procedure proc1 () { assign_union( &topic => $fed.data.db1.foo, other => $fed.data.db1.bar ); } ========== From darren at darrenduncan.net Fri Apr 24 02:31:18 2009 From: darren at darrenduncan.net (Darren Duncan) Date: Fri, 24 Apr 2009 02:31:18 -0700 Subject: [VPM] FYI - Muldis D whole app code example Message-ID: <49F186E6.6030803@darrenduncan.net> Hello, This message is a follow-up to my post of a couple weeks ago, that showed examples of individual Muldis D routine definitions side by side with SQL statements (from the SQL::Abstract 2 module docs) whose work they did. Today's message is a demonstration of the fact that (loosely SQL-alike) Muldis D is computationally complete, that you actually could write a standalone program in it if you wanted to, though in practice you would just use it for parts of programs like you do SQL, which AFAIK is *not* computationally complete. Below the dashed line in this message is a 3K example program which prompts the user to enter the name and address of 3 people, with which it populates a (lexical) relvar/table, and then it prompts the user to enter a name to search for, and then it searches for and prints out the address that went with that name, if it was found, and a not-found message otherwise. All of the features that this program uses are already specced (see http://search.cpan.org/dist/Muldis-D/ ) as usual, but that most of the concrete syntax it uses is not formally specced yet. A few notes: 1. This demo consists of exactly one program routine, called mymain, which due to artifacts of the language's design such as separating pure code from impure code (all value literals or value expressions must be in the pure section, while all user interaction must be in the impure section), is internally segmented into 5 inner routines; in most programming languages that would all just be a single routine mixing pure and impure code. An "updater" is pure (and atomic) while a "procedure" is impure. 2. Declaring the mymain routine requires mounting (analogous to SQL's CONNECT TO) a database/depot, named myapp, to store it in (all Muldis D routines are stored routines save the bootloader, which is mainly limited to stuffing things in a depot and invoking them). In this case, the depot used is declared temporary and so it is just kept in RAM, no disk file being created. Some other time I'll demonstrate using a persisting depot like one would normally use for data. In practice, an application would use 2 depots (each being their own namespaces), one persistent for mainly data like a normal database, and one possibly temporary for mainly code specific to that app; the example in this email is like the latter. 3. One demonstrated improvement from the last email is that when invoking a routine, up to 2 arguments may be passed without names (rather than just one shown before), which is one readonly argument and one subject-to-update argument, when just one of their respective kinds of parameters exists; they are told apart in that subject-to-update are marked with an ampersand and readonly doesn't have one. 4. Unlike the previous emails, this one includes examples of: user I/O (simple command line STDIN/OUT), generic if/else expressions, factoring out an otherwise-repeated part of a value expression into a named sub-expression ("matched_people"), and procedural iteration (but most iteration in a Muldis D program would be functional). -- Darren Duncan ---------- Muldis_D:"http://muldis.com":"0.65.0":PTMD_Tiny:bootloader create depot_mount myapp { is_temporary => true we_may_update => true } within depot myapp create { #=========================================================================# procedure mymain () { # This is the program's main procedure. # main { var Text $msg_gather_name var Text $msg_gather_addr var Text $msg_search_name var Text $msg_result_addr var Relation $people var Text $name var Text $addr var NNInt $loop_count inn.init_vars( &msg_gather_name => $msg_gather_name, &msg_gather_addr => $msg_gather_addr, &msg_search_name => $msg_search_name, &msg_result_addr => $msg_result_addr, &people => $people, &loop_count => $loop_count ) loop $loop_count { inn.gather_person( &$people, msg_gather_name => $msg_gather_name, msg_gather_addr => $msg_gather_addr ) } prompt_Text_line( &$name, $msg_search_name ) inn.search_for_address( &$addr, people => $people, name => $name ) write_Text( $msg_result_addr ) write_Text_line( $addr ) } inner_updater init_vars (Text &$msg_gather_name, Text &$msg_gather_addr, Text &$msg_search_name, Text &$msg_result_addr, Relation &$people, NNInt &$loop_count ) { # Initializes main routine's variables. # $msg_gather_name := 'Enter a person\as name: ' $msg_gather_addr := 'Enter that person\as address: ' $msg_search_name := 'Enter a name to search for: ' $msg_result_addr := 'Found address for that person is: ' $people := Relation:{ name, addr } $loop_count := 3 } inner_procedure gather_person (Relation &$people, Text $msg_gather_name, Text $msg_gather_addr) { # Gathers person info from user. # var Text $name var Text $addr prompt_Text_line( &$name, $msg_gather_name ) prompt_Text_line( &$addr, $msg_gather_addr ) inn.add_person( &$people, name => $name, addr => $addr ) } inner_updater add_person (Relation &$people, Text $name, Text $addr) { # Adds a person to our db of people. # assign_insertion( &$people, Tuple:{ name => $name, addr => $addr } ); } inner_updater search_for_address (Text &$addr, Relation $people, Text $name) { # Look up person by name, get their address. # $matched_people = ($people matching Relation:{ { name => $name } }) $addr := ( if ((cardinality of $matched_people) === 1) then ((tuple from $matched_people){A:addr}) else '' ) } } #=========================================================================# } # within depot myapp create # boot_stmt fed.lib.myapp.mymain() From Peter at PSDT.com Tue Apr 28 16:50:35 2009 From: Peter at PSDT.com (Peter Scott) Date: Tue, 28 Apr 2009 16:50:35 -0700 Subject: [VPM] Perl rant Message-ID: <6.2.3.4.2.20090428165001.03fda130@mail.webquarry.com> This rant - profane, but delightfully so - has been getting some attention: http://www.shadowcat.co.uk/blog/matt-s-trout/iron-man/ -- Peter Scott Pacific Systems Design Technologies http://www.perldebugged.com/ http://www.perlmedic.com/ http://www.informit.com/store/product.aspx?isbn=0137001274 From darren at darrenduncan.net Tue Apr 28 19:16:11 2009 From: darren at darrenduncan.net (Darren Duncan) Date: Tue, 28 Apr 2009 19:16:11 -0700 Subject: [VPM] Perl rant In-Reply-To: <6.2.3.4.2.20090428165001.03fda130@mail.webquarry.com> References: <6.2.3.4.2.20090428165001.03fda130@mail.webquarry.com> Message-ID: <49F7B86B.9000006@darrenduncan.net> Peter Scott wrote: > This rant - profane, but delightfully so - has been getting some attention: > > http://www.shadowcat.co.uk/blog/matt-s-trout/iron-man/ Ah yes, I know Matt Trout, and talk with him regularly, at least over the last 4+ years. See also http://ironman.enlightenedperl.org/ for the actual result of this whole thing, about 40 participants so far or something. -- Darren Duncan From darren at darrenduncan.net Thu Apr 30 14:05:20 2009 From: darren at darrenduncan.net (Darren Duncan) Date: Thu, 30 Apr 2009 14:05:20 -0700 Subject: [VPM] Tue, 2009 May 5 - RCSS May meeting Message-ID: <49FA1290.8060204@darrenduncan.net> Hello, this is a forwarded announcement of the Tue, 2009 May 5 meeting of the Recreational Computer Science Society. Hope to see you. -- Darren Duncan -------- Original Message -------- Subject: [reccompsci] May Meeting Announcement Date: Thu, 30 Apr 2009 13:53:42 -0700 From: Peter van Hardenberg Reply-To: reccompsci at googlegroups.com To: reccompsci at googlegroups.com After two months of dormancy. (Sorry!) This month's RCSS will be held at UVic on the usual time in the usual place. May 5th, 7:00PM ECS building ground floor (look for a small group of people with laptops) This month's topics will be database related. Noel will be giving a talk about his recent use of Perl's Parse::RecDescent to make a little SQL-like language parser for a database report generator. Peter will talk about SQLite and how it executes SQL queries as a form of byte code. Afterwards we'll head over to Maude Hunter's. See you all this Tuesday, Peter -- Peter van Hardenberg Victoria, BC, Canada "Everything was beautiful, and nothing hurt." -- Kurt Vonnegut --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Recreational Computer Science Society" group. To post to this group, send email to reccompsci at googlegroups.com To unsubscribe from this group, send email to reccompsci+unsubscribe at googlegroups.com For more options, visit this group at http://groups.google.com/group/reccompsci?hl=en -~----------~----~----~----~------~----~------~--~---