Muldis Data Systems
About Muldis D

Muldis D is an industrial-strength computationally complete high-level application programming language with fully integrated database functionality; you can use it to define, query, and update ("object") relational databases, as well as write general purpose applications. The language's paradigm is a mixture of declarative, homoiconic, functional, imperative, and object-oriented. Muldis D is currently under development, and the first working version should be available in April of 2017.

OUTDATED - See a complete Muldis D example here, consisting of a CD database schema plus test application.

See the end of this page for a complete simple "hello world" program, which interacts with the user.

See for the latest Muldis D specification and implementations (some outdated) as it has so far been written. In particular see

OUTDATED - See for an outdated formal specification rendered in HTML.

For all intents and purposes you can consider Muldis D to be what SQL could have been; it can express anything useful that SQL can, but in a much improved manner. So it should be easier to write more expressive and less kludgy code in Muldis D than in SQL. The following simple code comparisons (and aforementioned code examples) demonstrate this:

Muldis DSQL (parenthesis means as a scalar query)
  (SELECT 'x')

  (SELECT 'x' FROM dual)
  tab1 on \@(col1,col2)

  tab1 %= \@(col1,col2)

  tab1 π \@(col1,col2)
  SELECT DISTINCT col1, col2
  FROM tab1
Input: tab(col1,col2,col3,bigcol4,col5,col6,col7,col8)
  tab but \bigcol4

  tab %- \bigcol4
  SELECT DISTINCT col1, col2, col3, col5, col6, col7, col8
  FROM tab
  tab1 matching ?%{
    ( col1 : 'hello', col2 : 5 ),
    ( col1 : 'world', col2 : 7 ),

  tab1 ⋉ ?%{
    ( col1 : 'hello', col2 : 5 ),
    ( col1 : 'world', col2 : 7 ),


  FROM tab1
  WHERE (col1, col2) IN (
    SELECT 'hello' AS col1, 5 AS col2
    SELECT 'world' AS col1, 7 AS col2

  FROM tab1
  WHERE col1 = 'hello' AND col2 = 5
     OR col1 = 'world' AND col2 = 7
Inputs: tab1(col1,col2,col3), tab2(col2,col3,col4)
Result: tab(col1,col2,col3,col4)
  tab1 join tab2

  tab1 ⋈ tab2

  FROM tab1 INNER JOIN tab2 USING (col2, col3)

  SELECT DISTINCT tab1.*, tab2.col4
  FROM tab1 INNER JOIN tab2
    ON tab2.col2 = tab1.col2 AND tab2.col3 = tab1.col3
Input: tab1(col1,col2,col3)
  tab1 rename \@:(col1->foo,col3->bar)

  tab1 @:= \@:(col1->foo,col3->bar)

  tab1 ρ \@:(col1->foo,col3->bar)
  SELECT DISTINCT col1 AS foo, col2, col3 AS bar
  FROM tab1
Summarize by named attributes giving count:
  if ?people
    then (
      map \(( group : args:.\0 %= \@(age,ctry), member : args:.\0 ))
      pipe group
      map \( args:.\0:.\group %+ ( count_by_age_ctry : #args:.\0:.\members, ))
    else ?%@\@( age, ctry, count_by_age_ctry )
  SELECT age, ctry, COUNT(DISTINCT *) AS count_by_age_ctry
  FROM people
  GROUP BY age, ctry
  if ?sales
    then (
      map \(
        sale ::= args:.\0;
        returns ( group : sale:.\region, member : sale );
      pipe group
      map \(
        region ::= args:.\0:.\group;
        sales_for_region ::= args:.\0:.\members;
        revenue ::= sales_for_region attr_to_Bag \price reduce \plus::();
        qty ::= sales_for_region attr_to_Bag \units reduce \plus::();
        avg_price_per_unit ::= revenue / qty;
        returns ( :region, :revenue, :qty, :avg_price_per_unit );
    else ?%@\@( region, revenue, qty, avg_price_per_unit )
  SELECT region,
    SUM(price) AS revenue,
    SUM(units) AS qty,
    AVG(price/units) AS avg_price_per_unit,
  FROM sales
  GROUP BY region
  count := #(people:& where \(
    person ::= args:.\0;
    returns person:.\name = 'John' and (person:.\title = 'Mr' or person:.\abbrev = 'Dr')

  count := #(people:& σ \(
    person ::= args:.\0;
    returns person:.\name = 'John' ∧ (person:.\title = 'Mr' ∨ (person:.\abbrev = 'Dr'))
  WHERE name = 'John' AND ( title = 'Mr' OR abbrev = 'Dr' )
  foo := bar:&;
  foo := foo:& union bar:&;

  foo := foo:& ∪ bar:&;
  foo := foo:& union (\@:(col1<-,col2<-) renaming ?%{ (1,3), (2,4) });

  foo := foo:& ∪ (\@:(col1<-,col2<-) renaming ?%{ (1,3), (2,4) });
  INSERT INTO foo ( col1, col2 ) VALUES ( 1, 3 ), ( 2, 4 )
  foo := foo:& not_matching ?%{(col1:10,),(col1:20,)});

  foo := foo:& ⊿ ?%{(col1:10,),(col1:20,)});
  DELETE FROM foo WHERE col1 = 10 OR col1 = 20
  foo := foo:& update (col1 : 1, col2 : 6);
  UPDATE foo SET col1 = 1, col2 = 6

Muldis D provides an effective means to reduce or avoid entirely the problem of "object-relational impedance mismatch". The primary way that Muldis D deals with the above problem is that it natively supports user-defined types at the database level, which can be arbitrarily complex such as to support collection values as attribute values. And so users can effectively use their same type definitions in both the database and in their application, with no non-trivial mapping required. This is in contrast with typical database ORM tools which only remap application objects into simpler built-in database types like plain numbers and strings.

The syntax and feature set of Muldis D is like that of a general purpose programming language, and so shouldn't be too difficult to learn. Multiple other languages influence its design in various ways, not just SQL but also a variety of general purpose application languages, such as Perl 6.

In contrast to a typical SQL DBMS which uses multiple programming languages together, where SQL is used for queries and something else (possibly SQL-alike) for defining stored procedures, a Muldis D DBMS would use the same Muldis D language to assume both roles. Moreover, Muldis D can also be used to write your main application, if you wanted to; in fact, this kind of usage is encouraged for many common cases of simple database-using web applications.

See a simple (outdated) example of actual Muldis D code, which defines both a database schema and some routines which query or update the database. The example is derived from the "simple CD database example" that comes with the manual/cookbook of the DBIx::Class Perl module. The example is essentially complete, with scaffolding, such that you could almost just run it as is through a Muldis D compiler/interpreter and it would run.

Muldis D is intended for at least 2 primary uses; in the long term, it is intended to supplant SQL as the access language of choice for relational DBMS vendors in their products; in the short term, it is intended to be the most effective intermediate representation for code that either is intended to have SQL (or a host application language in the case of less capable or privilege-restricted DBMSs, including non-relational or "No-SQL" ones) generated from it, or that SQL would be parsed into it, and so it would be the tool of choice for making widely-DBMS-portable schemas and applications, that leverage a high fraction of a DBMSs native features rather than a lowest common denominator, or for porting such; moreover, it is meant to abstract away where code actually runs, so an implementation can choose to either have parts of it run on a server or on a client (similarly to how tools like LLVM can transparently make code run in the CPU or the GPU, as is best or is even possible at the time). The intermediate representation usage is supported in particular by Muldis D having both regular string forms, for when you manually write code, as well as host language data structure forms, for when you are generating code, good for preventing "SQL injection attacks"; and being homoiconic, Muldis D can manipulate and run Muldis D code at runtime.

Muldis D is an Acmeist programming language for writing portable database modules, that work with any DBMS and with any other programming language, for superior database interoperability.

Use Cases


While the specification of Muldis D is essentially complete and so can mostly be implemented as is, no Muldis D implementation actually exists yet. A complete reference implementation is expected in the next year or so, but it isn't here yet. Actually, at this point it is expected that the reference implementation (now under development) will lead the spec in the near future, in that it will implement a substantially updated Muldis D syntax and standard library, before the specification then catches up to it. But despite this, Muldis D still can be put to work for you today, in a limited fashion.

Practical benefits that you can have right now include:

  1. Use Muldis D as a more-expressive prototyping language; you can write out your database-concerning code in Muldis D first to get a good feel for your database schema and queries, and then write it out in analogous SQL afterwards when you have your planning worked out.

  2. Makers of SQL-generating toolkits (such as the Perl modules Fey or SQL::Abstract or Rose::DB::Object::QueryBuilder) can use Muldis D's Perl data structure form as a guide to improve their APIs so that their users can then express what they want more easily and cleanly, while gaining SQL's full feature-set without having to write any string-SQL snippets, because Muldis D's coverage is fairly thorough. Similarly, various database access toolkits (such as DBIx::Class or SQL::Translator or DBIx::Perlish) or ORMs can be updated to use the code representation that Muldis D specs as their internal representation, or provide some of that in their API, as they go about their database-mapping duties.

    Also, such toolkits can, when used to deploy or update a generated database schema, also deploy a lot of their now-more-portable query code or data-manipulation code as database stored routines, so that there is less fretting and contrivance at normal application runtime where they try to load user client code lazily or conditionally for performance because it would be expensively regenerated every time; the greater use of database stored procedures would mean that more of the relevant code is pre-compiled and there is less of it in always-regenerated form to fuss about on the client side.

    Note that, while Muldis D should be able to represent and map to/from any database schema, it works best with a well-designed schema. In this way I have made the same decision as some other database tools. If your schema is well-designed, it should map cleanly, make beautiful code, and perform well; if your schema is badly designed, it would map more roughly, with more kludgey looking code, and performance can suffer. Muldis D is designed intentionally such that good practices are given the easiest path to them, and poor practices have a less-desirable path. Focusing on supporting good designs is better for sanity and for keeping the bug count down; trying to make poor designs perform well is like trying to make a program that returns the right results when given the wrong inputs. An example good practice is writing generic queries with bind parameters, which can be pre-compiled, rather than writing a separate data-specific query for each database invocation.

  3. Designers of other programming languages, especially ones still under active design and development such as Perl 6, can look at Muldis D and see if any of its ostensibly novel design aspects or features might influence them as they evolve their own languages. For example, how it actually is practical and useful to have relational types and operators integrated into your language as much as the likes of arrays, sets, and dictionaries/hashes are.

Features of Muldis D


Currently fully-specified features include:

Currently partially-specified features include:

Currently mostly-unspecified but intended features include:

The Next Steps


While essentially complete, the Muldis D language specification is officially considered "pre-alpha" quality, because it is mostly untested in execution. (Except for an (outdated) adapted subset, by the Set::Relation Perl module, which does execute now.)

The next major milestone for Muldis D will be when a working self-contained reference implementation of the language has been made, in the form of a pure-Perl DBMS, Muldis::D::RefEng. At this point, both the language and said implementation will officially be considered "alpha" quality.

Following that, perhaps the next major milestone will include any of having a decent TAP-based test suite, or having multiple implementations including a Parrot-hosted language, or implementations over SQL DBMSs (starting with PostgreSQL, which seems to be the most conducive environment for it, considering its existing good feature set, solid quality, freedom, and its developers' priorities; other early hosting targets include Sybase, DB2, Oracle, and SQLite, where Perl or another HLL will be used to support stored procedures for the latter), at which point the language can officially be considered "beta" quality.

Note that Muldis D is designed such that it can be mostly self-hosted (written in itself) meaning once you have one functional implementation, you have most of another one, and the only reason to be less self-hosted is to aid performance, rather than just getting it to work at all; MREE will be written in that way.


See also for the 4 official public email lists that are specific to Muldis D and its implementations: "-announce", "-users", "-language", and "-devel"; just the latter 3 are for discussions.

Muldis D and its official implementations are commercially supported by Muldis Data Systems.

If you want a "homepage" url to link to, you can use concerning this project or particularly its commercial support.

And is its main public GIT version control repository.

Hello World

Here's a bonus Muldis D example, a complete simple program that first gathers info on people from the user, and then lets the user search that information; it only uses lexical variables.

    Muldis_D Plain_Text '' '0'
    meta script 'Unicode 9.0 UTF-8'

    (\Package : (
        identity : (
            package_base_name : [\My_App],
            authority : '',
            version_number : '0',
        foundation : (
            authority : '',
            version_number : '0',
        uses : (
            MD : (
                package_base_name : [\Core],
                authority : '',
                version_number : '0',
        entry : \@package::main,
        floating : {\@package, \@used::MD},

        materials : (
            main : (\Procedure : (
                matches : (),
                performs : [
                    `This is the program's main procedure.`
                    write_Text_line( 'Hello!' );
                    declare people: ?%@\@(name,addr);
                    declare name: '';
                    declare addr: '';

                    write_Text_line( 'First add some info.  Enter blank line when done.' );
                    gather_person block
                        `Gathers person info from user.`
                        prompt_Text_line( name, '\Enter a person\as name: ' );
                        if name:& = '' then leave gather_person;
                        prompt_Text_line( addr, '\Enter that person\as address: ' );
                        if addr:& = '' then leave gather_person;
                        `Adds a person to our db of people.`
                        people := people:& insert (:&name,:&addr);
                        iterate gather_person;

                    write_Text_line( 'Now search for info.  Enter blank line when done.' );
                    lookup_person block
                        prompt_Text_line( name, 'Enter a name to search for: ' );
                        if name:& = '' then leave lookup_person;
                        `Look up person by name, get their address.`
                            given #(matched_people ::= people:& matching (:&name,))
                                when 0 then 'No person matched'
                                when 1 then guard 'Found address for that person is: '
                                    ~ (%matched_people):.\addr
                                default 'More than one person matched'
                        iterate lookup_person;

                    write_Text_line( 'Goodbye!' );

Copyright © 2007-2017, Muldis Data Systems, Inc.
MULDIS and MULDIS MULTIVERSE OF DISCOURSE are trademarks of Muldis Data Systems, Inc.

Muldis Data Systems is not affiliated with Muldis Consultants & Engineers (, which has its own distinct MULDIS trademark.