Munje
A Logic Programming Language

Table of Contents

Note: This is an old project and is currently not maintained.

Munje is a programming language for advanced human-computer communication. It was designed to use relation logic to represent both data and code. Munje is time-static, which means there is totally no changes inside the language. Instead, time is represented as an addition to the relation logic. The design philosophy of Munje is highly influenced by Prolog and Cosmos. It also has a syntax that is similar to Python and Lisp. This article introduces the design principles of the Munje programming language and its time-static feature.

Keywords: programming language, logical, time-static, munje, prolog, lisp

News

A developping implementation can be found at Github.

Introduction

Programming languages are created to make computers understand human beings. Munje is a logical time-static programming language created by Wei. It has a simple yet powerful syntax. Munje approaches side effects in a different way called time-static compared with I/O Monad in functional programming languages such as Haskell.

Some features of Munje include:

  • Data and code are represented in the same structure called relation.
  • Easy to learn. The syntax is simple, with less than 5 keywords.
  • Logical programming. You define what you want to do, but not how to do it.
  • Time-static. Unlike in functional programming language where although side effects is explicit, it is unmanaged, in Munje, side effects is processed in a managed way.

The name "Munje"

The name "Munje" is a Lojban word meaning "the universe".

Philosophy

Munje is designed to be simple and powerful. It believes that we should tell machines what to do, but not how to do it. The are only 4 core concepts in this language, namely relations, terms, arguments, and paragraphs. The program as follows is a simple program that calculates the factorial:

define factorial
  factorial 0 1
  factorial n y
  (>) n 0
  (-) n 1 n'
  factorial n' y'
  (*) y' n y

Concepts and Syntaxes

In this section, we will introduce some concepts of Munje.

Relations, Arguments and Terms

Relations are declarations claiming something is held. A relation consists of one term, defining the type of the relation, and zero or more arguments, defining the content of the relation.

Terms are represented by symbols or partial relations. Each term has a certain number of arguments. For example, (plus) is a terms that require 3 arguments a, b and c. It defines a relation between a, b and c which in mathematics, known as c (eq) a (plus) b. At the same time, a partial relation also represent a term. For example, (plus) defines a relation between b and c where c (eq) 1 (plus) b, (plus) 1 1 defines a relation with c where c (eq) 1 (plus) 1, that is c (eq) 2, and (plus) 1 1 2 defines a relation with zero arguments that represents 2 (eq) 1 (plus) 1 exists in the world. Note that the last term (plus) 1 1 2 is also a relation.

Arguments are terms that put starting from the second place of relations. In this way, term becomes the first-class citizen in the Munje world that can be easily passed into different relations. For example, if is a term that requires 3 arguments, where the first is a relation that may be held or not, the second is a relation that will be held if the first relation is true, and the third is a relation that will be held if the first relation is false. In the next section, we will see a implementation of if term in Munje.

Munje is made up of declarations of relations. Relations are "declared" in the top level of the program. Those declared relations will be marked as "held" for further computation. Note that if a relation is not in the top level, it is not necessarily held, because there may be and, or, or not relations around them. Terms are implicitly defined by a declaration of a term.

For example, the program as follows defines a null value that we may be able to use in other parts of the program:

null null

The declaration creates a relation null null that is marked as held. It implicitly creates the term null that represents there is no value exists. Here we make the term null an one-argument term because it becomes convenient for us to check whether something is an equivalent of null or not, like the example as follows, where different relations are marked held based on whether some_term is null or not:

or (and (null some_term) [relation when some_term is null])
   (and (not (null some_term)) [relation when some_term is not null])

Pattern Matching of Terms

Determining whether two terms are equal plays an important role in the language Munje. Munje uses a pattern matching way similar to other functional programming languages. Two terms are equal if and only if it represents the same thing. That is, all parts of the terms should be equal.

Paragraphs

Paragraphs are used to separate different functions of a program. It creates an environment that has special properties outside the paragraph. Paragraphs are defined using the define keyword. Note that indentation is used to identify the end of a paragraph. The factorial program is used again as an example:

define factorial
  factorial 0 1
  factorial n y
  (>) n 0
  (-) n 1 n'
  factorial n' y'
  (*) y' n y

Exposing

In the first line of the program, define factorial creates a paragraph. At the same time, it expose the term factorial and make it accessible to the outside world. All other terms inside the paragraph is not accessible to the outside world. This makes sure that a program can be properly arranged and different parts of the program don't influence each other improperly.

Uncertainty

Uncertainty means when a term is not referred, its relations with others will not be determined. This allows different 'instances of a paragraph', which is created each time a exposed value is called, to be implemented. For example, consider the program as follows based on the factorial program.

factorial 5 a
factorial 4 b

Because factorial is defined and exposed. Those two lines refer to the same term factorial. However, because terms n, n', y, and y' are defined inside the paragraph, its value will not be determined until the compiler find factorial 5 a and factorial 4 b. Therefore, n, n', y, and y' have different values for factorial 5 a and factorial 4 b. At the same time, inside the paragraph, since factorial is exposed, each time when a factorial relation is created, a new instance of paragraph will be created. This creates a recursion.

Immutability

After one term is exposed, its value cannot be changed and its relation cannot be further limited.

Data Structure

In Munje, data structures are created totally by relations. Below there will be example codes for some ways to create those data structures.

Core Terms

In this section, we define some core terms to make creating data structure easier.

equals

equals is used to determine whether two arguments are equal:

define equals
  equals x x

similar

similar is used to determine whether two arguments have the same "type". It is held if and only if two arguments are equal, or two arguments are applied with same relations.

define similar
  similar a b
  or (equals a b) (and (rel a) (rel b))

if

if is used to determine which relation should be true based on a statement relation:

define if
  if statement if_true if_false
  or (and statement if_true) (and (not statement) if_false)

Types

Types are implemented as relations inside Munje. The similar function is useful to determine whether two terms have the same type.

Cons

Cons are used to represent a sequence of things, i.e. lists.

Cons requires 2 arguments. The first argument is an item inside the list, and the second one is also a Cons relation. One implementation of Cons in Munje can be represented as follows:

define cons
  cons head tail
  equals tail (cons head' tail')
  similar head head'
  similar tail tail'

head and tail can be used to retrieve the item of a Cons list.

define head
  head list value
  equals list (cons head _)

define tail
  tail list value
  equals list (cons _ tail)

Integer and String

Integer and String are implemented by compiler. Integer are zero-argument terms. String is Cons of Characters.

Boolean

Boolean needs to be explicitly defined inside Munje, for example:

boolean true
boolean false

After boolean type is defined, we can easily convert any relations into boolean:

define to_boolean
  to_boolean rel val
  or (and rel (equals val true)) (and (not rel) (equals val false))

Association List

Association List is a list of keys and values in which a value can be retrieved based on a key. In Munje, association list is based on the pair type:

define pair
  pair a b

An example implementation of association list can be created as follows:

define association_list
  equals (association_list item _) (cons item _)
  pair item

get relation for association list can be created based on the definition.

Immutability

Because terms are immutable, the data structure based on terms are immutable as well. However, side effects is still possible based on the time-tense API.

Time-static

The concept time in Munje is used to represent different states and cycles a program may go through while running. Time-static means in Munje, time is represented as normal terms. There are no time functionalities in the language specification. Instead, time-tense API is implemented in compiler and standard library to provide this functionality.

The idea of time-static is based on the Event-token reification in Temporal Logic. In each state, instead of claiming a term or variable is held, a relation with that variable together with an timepoint is held. This allows "time travel" in some extent. You can refer to a variable in any state anywhere in your code, because in Munje, time is just another dimension like anything else. The compiler will figure out at which time, which relation should be held.

Time-tense API

The time-tense API has the relations as follows.

  • starttimepoint: the timepoint when the program starts running.
  • endtimepoint: the timepoint when the program ends.
  • timepoint: declare a new timepoint.
  • after: declare a statement is held after a certain timepoint.
  • before: declare a statement is held before a certain timepoint.
  • at: declare a statement is held at a certain timepoint.
  • during: declare a statement is held during a certain timepoint. during is the combination of before and after.

Example

Below is an example using time-tense API to read a line from the console:

define readline
  readline result after_timepoint before_timepoint
  at after_timepoint console content_start
  at sometime console content_end
  at sometime' console content_end_nextline
  during after_timepoint before_timepoint sometime
  during after_timepoint before_timepoint sometime'
  before sometime sometime'
  (++) content_end "\n" content_end_nextline
  (--) content_end content_start result

This readline term takes 3 arguments. The first argument is the placeholder for the result. The second and third is usually provided by a do relation. It put the value of contentstart at the beginning time of the readline. And it tries to find a certain time (called sometime) when after a period of that certain time (called sometime), a new line character is written. It calls this value contentend. The difference of contentend and contentstart is the result of readline.

Lojban Terminology

In this section, we provide a reference for the equivalent terminology in Lojban.

  • Relation: bridi
  • Term: selbri
  • Argument: sumti
  • Paragraph: girzu

Implementation of Munje's Time-static Feature

Due to man-power reason, it would be interesting to first implement the time-static feature in other programming languages or a toy programming language, and then back to implement other parts of it.

Required Data Structure

  • Atom
  • List

Methodology

Time-static could be treated as a generalized situation of "dependency resolving". Based on the logic provided by the source code, the compiler will try to resolve all involving parts related, and generate a 'List' as above data structure. The list represent a sequence that should be true during the runtime.

Conclusion

As a logical time-static programming language, Munje allows people to write programs in a simple and powerful way. It can be used both for querying and executing, which allows easy debugging and high stability. For advanced human-computer communication, Munje can be used by voice to make commands and queries.