Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Kotlin

The Declarative Approach of the Ring Programming Language

4.67/5 (5 votes)
22 Dec 2017CPOL7 min read 12.7K   38  
We will learn about the innovation in the declarative approach of the Ring programming language.

Introduction

When we look at some popular declarative programming languages like QML, REBOL and Red, we will discover that using nested structures of objects to describe the solution is a practical way to solve some real problems where we can focus on the objects, properties and how these objects are related together. A lot of domain specific languages can be developed using the same programming paradigm, but developing a domain specific programming language needs some time, tools and skills to be done right! and the language limitations could lead to problems in the future where the language extension may require advanced skills.

Towards solving this problem, which is already solved many years ago, a lot of popular general-purpose programming languages provided some ways to develop domain specific languages. A notable and modern language in this area is the Ruby programming language. Ruby comes with nice features like beautiful syntax, blocks and meta-programming where using these features a lot of domain specific languages are developed and used in practice. Also, the Kotlin programming language played a role in this area and provided nice features to achieve this goal. Sure a lot of programming languages did that, but Ruby and Kotlin are what come to my mind for now.

When I designed the Ring programming language, I was aware about this problem, the different successful solutions that are developed. But having a problem that can be solved in different ways doesn't prevent us from providing new solution that may be more useful and better in some way at least in our opinion as a start.

In this article, I will present the innovative features in the Ring programming language that can be used to develop domain-specific declarative programming languages using nested structure. I will show the big picture and how the ideas can be reused to develop any domain specific language that we need.

Background

It would be nice if you can look at the next examples in QML, REBOL, Ruby and Kotlin to know how the declarative code may look like. These examples are already public and can be shared, I'm just using them here in the beginning to give you some practical idea about how things looks in the declarative approach using nested structures.

(1) QML Example

import QtQuick 2.9 #import from Qt 5.9

 Rectangle {
     id: canvas
     width: 250
     height: 200
     color: "blue"

     Image {
         id: logo
         source: "pics/logo.png"
         anchors.centerIn: parent
         x: canvas.height / 5
     }
 }

(2) REBOL Example

view [text "Hello world!" button "Quit" on-action [quit]]

The advantage of this approach is that we can know the relationship between objects, define objects with/without names, set the objects attributes, and concentrate on our description (what we want to do).

Sure, this approach is not suitable for all problems, but it can be used in many useful cases.

I talked about using a general purpose language like Ruby to do something similar to that.

(3) The Ruby Code May Look Like This

Ruby
output = FancyMarkup.new.document do
  body do
    div id: "container" do
      ul class: "pretty" do
        li "Item 1", class: :active
        li "Item 2"
      end
    end
  end
end

Or:

Ruby
output = FancyMarkup.new.document {
  body {
    div id: "container" {
      ul class: "pretty" {
        li "Item 1", class: :active
        li "Item 2"
      }
    }
  }
}

So the Ruby way is to use methods, blocks and its beautiful syntax.

(4) The Kotlin Code May Look Like This

Ruby
val data = mapOf(1 to "one", 2 to "two")

createHTML().table {
    for ((num, string) in data) {
        tr {
           td { +"$num" }
           td { +string }
        }
    }
}

The advantages of using a general-purpose language like Ruby or Kotlin to build a domain specific language could be getting more power (Extensions, Customization and Flexibilty) but this may lead to some complexity or at least a syntax not so clean as we may find in pure domain specific languages.

Using the Code

In the Ring programming language, we provided an innovative approach to provide declarative programming using nested structures but with very clean syntax and advanced level of customization and flexibility.

The basic idea of the declarative approach in the Ring programming language: "Instead of using methods and passing parameters like blocks, anonymous functions or closures. We will just access the object at the client|caller side but using more beautiful syntax based on using braces instead of the dot operator, where at the caller side, we have more flexibility to access any object without modifying its design (Class) and we can execute our code in the object context and use the public methods and attributes provided by that object. Also we have our local scope. We get the power that looks like closures (in this use case only) but in a more faster and simple implementation that doesn't include passing parameters or altering the methods definition. We just modify our classes to define only the methods that we will use to create new objects and set the relationships between these objects".

To understand how this idea is very useful, and to know why it's not used before Ring!

You have to remember an important thing about the real concept of the Object-Oriented Programming paradigm.

" I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages ", Prof. Alan Kay

And since both of Ruby and Kotlin are Object-Oriented languages, their designers are used to apply the paradigm concepts and hide the data (In most cases) and provide communication between system components using method calls. So block, anonymous functions, or closures comes to their mind when they tried to create domain specific languages based on their general purpose languages. Also, the idea of passing functions as parameters is more related to functional programming and first-class functions and these languages take this paradigm too in mind. Sure I'm not talking about how they are thinking, I'm just talking about what I see from Ruby and Kotlin design at the practical side.

The Ring language take in mind these paradigms (Object-Oriented, Functional, etc.) but when we implement a domain specific language, we look at the problem using a different point of view.

In Ring, we decided that:

  1. All attributes are public by default (This break a security rule: principle of least privilege - for flexibility).
  2. You can create private attributes and methods if you want (for safety).
  3. If you have public attributes, you still have the option to create setter and getter methods (safety after flexibility).
  4. When you call a method that return an object, you can use braces to access that object directly.
  5. You don't have to call a method directly. You can try to access an attribute. This will call the getter method that may return the object that you may access. This method will set the relationship between the new object and the current object (Parent).

Using these simple rules/concepts in the Ring programming language, we can go ahead and create a domain-specific language using nested structures.

Now, how this domain specific language will look like?

Example (1) in Ring Programming Language

The next example uses a domain-specific language defined by the web library to create HTML pages.

Tags like div, h1, P, etc. are just attributes. When we try to access these attributes, the getter() method for each attribute will be called (getdiv(), geth1(), getp(), etc). These methods will create the object, Add it to the current object container (list), then return a reference to that object. Using braces { }, we can access that object and we can set the new object attributes and call the object methods.

load "weblib.ring"
import System.Web

func Main

  BootStrapWebPage()
  {
        div
        {
          classname = :container
          div
          {
                classname = :jumbotron
                H1 {   text("Bootstrap Page")   }
          }
          div
          {
                classname = :row
                for x = 1 to 3
                  div
                  {
                        classname = "col-sm-4"
                        H3 { html("Welcome to the Ring programming language") }
                        P  { html("Using a scripting language is very fun!") }
                  }
                next
          }
        }
  } 

Example (2) in Ring Programming Language

The next example from the 2D Game Engine that comes with the Ring language:

oGame {
             title = "Stars Fighter!"
             sprite
             {
               file = "images/menu1.jpg"
               x = 0 y=0 width=800 height = 600 scaled = true animate = false
               keypress = func ogame,oself,nKey {
                     if nkey = key_esc or nKey = GE_AC_BACK
                       ogame.shutdown()
                     but nKey = key_space
                       oGameState.startplay=true
                       ogame.shutdown=true
                     ok
               }
               mouse = func ogame,oself,nType,aMouseList {
                     if nType = GE_MOUSE_UP
                       oGameState.startplay=true
                       ogame.shutdown=true
                     ok
               }
             }
             text {
               animate = false
               size = 35
               file = "fonts/pirulen.ttf"
               text = "Stars Fighter"
               x = 10  y=50
             }
             text {
               animate = false
               size = 25
               file = "fonts/pirulen.ttf"
               text = "Version 1.0"
               x = 80  y=100
             }
             text {
               animate = false
               size = 16
               file = "fonts/pirulen.ttf"
               text = "(C) 2017, Mahmoud Fayed"
               x = 45  y=140
             }

             text {
               animate = false
               size = 25
               file = "fonts/pirulen.ttf"
               text = "Press Space to start"
               x = 190  y=470
             }
             text {
               animate = false
               size = 20
               file = "fonts/pirulen.ttf"
               text = "Press Esc to Exit"
               x = 260  y=510
             }
             Sound {
               file = "sound/music1.wav"
             }
       }

To learn more about the implementation: Check this chapter in Ring documentation

The next example explains the concept - just read the comments to get an idea about how things are implemented.

#=============================================================================================
# Declarative Programming using nested structures
#=============================================================================================

new Container 				    # Create an object from the Container class 
{ 
	Point 				        # Try to use the Point attribute, This will call getPoint() 
	{    				        # Access the new object created by getPoint() method 
		x=10 y=20 z=30        	# Set the Object Attributes (x,y and z) 
	}                           # This will call the braceend() method 
}

#=============================================================================================
# Implementation using Classes
#=============================================================================================

Class Container                  # Our class that will contains many objects
	
	aObjs = []                   # A list to store the new objects (for example Points)
	
	point                        # An attribute 
	
	func getpoint                # A method to be called when we use the Point attribute  
	
		aObjs + new Point        # Create new object from the Point class, Add it to the list 
		return aObjs[len(aObjs)] # Return the new object 

Class Point x y z                # Define new class called Point 

     # A method to be called after accessing the object using braces
     
	 func braceend          
	 
	 	? "3D Point" + nl + x +   
	 	  nl + y + nl + z + nl 

Points of Interest

Using braces to access objects instead of the dot operator provided a different way to interact with objects and see more techniques to support declarative programming and natural programming.

In this article, we talked about using this feature to develop domain specific languages using nested structures.

In another article, I talked about using this feature for natural language programming.

It's common to listen to developers saying that "Ruby blocks is one of the killer features in the language!"

Also in the Ring community, "Ring braces is one of the killer features in the language!"

Ring is an innovative programming language that comes with better support for Natural Language Programming and Declarative Programming. The innovation comes in supporting these paradigms with new practical techniques on the top of Object-Oriented Programming and Functional Programming.

Also, Ring is influenced by the next programming languages:

  • Lua
  • Python
  • Ruby
  • C
  • C#
  • BASIC
  • QML
  • xBase
  • Supernova

History

The Ring is an innovative and practical general-purpose multi-paradigm language:

  • November 2011: Idea of the new language was conceived
  • September 2013: Design and the implementation of the Ring programming language is started
  • April 2015: Language name is selected
  • May 2015: Compiler was implemented
  • September 2015: Documentation was done
  • January 2016: Ring 1.0 was released
  • October 2016: Ring 1.1 was released
  • January 25, 2017: Ring 1.2 was released
  • May 15, 2017: Ring 1.3 was released
  • June 29, 2017: Ring 1.4 was released
  • August 21, 2017: Ring 1.5 was released
  • November 30, 2017: Ring 1.6 was released

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)