Monday, July 22, 2013

Component programming for Scala

  Scala is a very good and powerful programming language. But something is missing, something... something like this:

  In this note I will focus on the prototype library, which will increase the potential of component programming in Scala. Here will be a lot of practice and a few theory, since I think it will be more clearer, and write in English it’s really hard for me :)  But if you are feel an acute shortage of theories and reading in Russian, then look to this note.
  Thanks to the Scala flexibility, turned out to make the syntax which not very creepy, but I had to use a little of black magic, so if you want to try applet-examples, you need to allow them work.

Another component programming
  Component programming is a paradigm that says "let's assemble the programs from the prepared blocs - components". This paradigm was invented a long time ago (in 60-ies) and, for this time been a lot of implementation, such as COM or JavaBeans. But almost all existing implementations says  "components is large and thick, complex and static entity, use them only to pack huge pieces of programs", and I think It’s really wrong way.
  I would like to have a set of small and dynamic components for simply design the programs of them, and see the results in real time. Something like OOP objects, but much less the monolithic and with less chaotic structure, friendly to visualization and to realtime management. Something like a Lego-programming.

Intuition
  The proposed paradigm is a mix of component-oriented programming, connection-oriented programming, reactive and agent-oriented programming. This approach suggests one-off writing a code, i.e. if the component for any reason ceased to satisfy the current requirements, rewrite him it is a good way. 
  Well, let's do something!
Assembly & components
  To get started, we need to: First - create a new Scala-project, read the license agreement, download and add the jar-library. Secondly - create some Scala-object with main method, which will be used to run an assembly, and import there seven classes and traits:
   package e_hello
   import skidbladnir.{Assembly,Base,Compo,Handle,Interface,Mono,Multi}
   object Test {def main(args: Array[String]): Unit = {/*TODO*/}}
  Next, we need to define and create the assembly. The assembly is an object where you will create and launch the first components, and where stores the information about the current status and structure of the connections of components:
  ...
  object Test {def main(args: Array[String]): Unit = {val a = new SimpleAssembly}}
  //Class Assembly contains methods for creating and connecting components, and control him behavior
  class SimpleAssembly extends Assembly {
    //Open the visualization window
    visualization           
    //TODO
    //Says to a thread - run assembly and wait for the completion of its work, additionally there is methods go and console 
    gowait    
    //Free resources and complete the program (System.exit(0))         
    end                     
  }
  Next, we define a very simple component:
  //Class Base contains methods and fields for the support the base-component, additionally there is a class Compo
  class Hello extends Base { 
    //Any component may have a main-function, which will be executed in separate thread 
    //after execution gowait, go or console, additionally there is a loop-function  waitloop-function
    main(()=>{          
      println("Hi!")            
      //Thread.sleep()
      sleep(10000)  
      //The destruction of the component. (!) The program ends when all of the components will be destroyed
      selfdestruction   
    }) 
  }
  Ok, let's create an instance of the component and see what happens:
  ...
  visualization          
  //Create a component named hello
  new Hello named "hello"  
  gowait                     
  ...
  Run applet: Sorry, this browser does not understand Java applets or they are not enabled right now. (!)To see the output enable Java-console.
Interfaces & connections
  The interface is a special entity consisting of two halves-objects (Am and Pm), each of which consists of two parts: exports - exported fields and methods, imports - imported fields and methods. Here's a template to define the type of interface:
  object <Type_name> extends Interface {                      
    class Am extends Mono {val imports:Pm = null
      <exports>}                                  
    class Pm extends Mono {val imports:Am = null
      <exports>}                                        
  }
  The components can be connected if implement the interface half. When components connect, the exports of one component is available to another through the imports field. Graphically, it looks like this:
  Let's make a simple definition of the type of interface for component Timer:
  object ITimer extends Interface {                      
    class Am extends Mono {val imports:Pm = null} 
    class Pm extends Mono {val imports:Am = null
      //Timer have one method to import.
      def tick(tn:Int) = {}}                                  
  }
  The interface implementation has the following format:
  protected val <interface_name> = <connector(plug or jack)>(new <Interface_type>.<Am/Pm>{
  <exports_implementation>}
  Let's define a couple of components that implement the interface ITimer:
  class Timer extends Base { 
    //Var
    private var tn = 9
    //Interface implementation, since the first half (Am) export nothing, there is no implementation of export
    protected val iTime = plug(new ITimer.Am)  
    //Loop is like main, but is invoke in cycle so long as returns true
    loop(()=>{                                
      sleep(1000)
      //The invoke of the imported method
      iTime.imports.tick(tn)                  
      tn -= 1
      true                                    
    }) 
  }
  class Main extends Base { 
    //Interface with implementation the exported method tick
    protected val iTime = jack(new ITimer.Pm{       
      override def tick(tn:Int) = {               
        if(tn == 0){println("BOOM"); selfdestruction}else{println("tick No " + tn)}}}) 
  }
  Create and connect the two components:
    ...
    visualization          
    new Main named "main"  
    new Timer named "timer" 
    //Connect function.    
    "iTime" from "main" connect "timer" 
    //This is a short version for the interface half with the same name, the full version is: 
    //"<interface>" from "<component>" connect "<interface>" from "<component>"
    gowait                     
    ...
  Run applet: Sorry, this browser does not understand Java applets or they are not enabled right now. (!)To see the output enable Java-console.
Multiinterfaces
  Often required to give access to one component from several other component, are used for this multiinterfaces, which type definition has next format:
  object <Type_name> extends Interface {                      
    class Am extends <Multi/Mono> {<val imports = Map[Handle, Pm]()/val imports:Pm = null>
      <exports>}                                  
    class Pm extends <Multi/Mono> {<val imports = Map[Handle, Am]()/val imports:Am = null>
      <exports>}                                        
  }
  Let's define a first useful component. It will be a component Timepiece, which every half-second be report the current time. Current time may be need to several components, so we will use multiinterface:
  object ITime extends Interface {    
    //In this case imports is a list of the connected components
    class Am extends Multi {val imports = Map[Handle, Pm]()  
      //Timepiece will export field time that stores the current time,   
      //which be accessible to all of the connected components 
      var time:Long = 0}                                  
    class Pm extends Mono {val imports:Am = null
      //Timepiece will import the method tick from all connected components
      def tick() = {}}                                   
  }
  Properly define a Timepiece:
  class Timepiece extends Base { 
    //Interfaces
    //For multiinterfaces need to use multijack or multiplug
    protected val iTime = multijack(new ITime.Am)  //Что такое multiplug
    //Loop
    loop(()=>{ 
      //Usage the exported field time 
      iTime.time = System.currentTimeMillis()     
      //Invoke a method tick from all connected components
      iTime.imports.foreach(e => {e._2.tick()})   
      sleep(500)
      true
    })   
  }
  And a couple of components that will be use Timepiece:
  class Main extends Base { 
    //Interfaces
    protected val iTime = plug(new ITime.Pm{
      override def tick() = {
        println("system: " + imports.time)}}) 
    //Main
    main(()=>{
      sleep(15000)
      selfdestruction 
    })
  }
  class Show extends Base { 
    //Interfaces
    protected val iTime = plug(new ITime.Pm{
      override def tick() = {
        println("time: " + new SimpleDateFormat("yyyy.MM.dd 'at' HH:mm:ss z").format(imports.time))}}) 
  }
  Now gather it all together and see what happens:
  ...
  visualization          
  new Main named "main"  
  new Show named "show" 
  new Timepiece named "timepiece"   
  "iTime" from "main" connect "timepiece"              
  "iTime" from "show" connect "timepiece" 
  gowait                    
  ...
  Run applet: Sorry, this browser does not understand Java applets or they are not enabled right now. (!)To see the output enable Java-console.

Examples
  Ok, let's do a little the real applications!
Watch
  Earlier, we used only the Bases, now let's try trait Compo. In Compo (unlike Base) one of the interfaces is main and it’s called root-interface, this interface is always connected to some else component, and him disconnection is equivalent to commit selfdestruction. Why it is needed I will explain later. Yet let's do little bit Compo-nents that will be useful to us in the future.  
  The first component is a GUI Frame, for placing visual objects:
  class BorderFrame extends JFrame with Compo { 
    //Self-assembly
    ...
    panel setLayout new BorderLayout
    //Root-interface
    protected val iFrame = root(new IFrame.Am{...})
    //Interfaces
    protected val iNorth:IWidget.Pm = multijack(new IWidget.Pm{...},
      //Each interface implementation (except root) can have a functions, 
      //which be called when this interface will connect and disconnect
      //Parameter - component handle 
      connection = (h)=>{...},    
      //Parameters - component handle, cause of the disconnection
      disconnection = (h,i)=>{...})                       
    ...
  }
  Test component:
  class Hello extends JLabel with Compo {
    //Self-assembly
    setText("Hello world!")
    setPreferredSize(new Dimension(200,20))
    //Interfaces
    protected val iWidget = root(new IWidget.Am{override val widget:Compo = compo})
    //compo - field how contains the pointer to the current component, for use in nested classes
  }
  Assembly and run:
  ...
  visualization
  //Base which will be attached Compo
  new Main named "main"
  //When creating Compo, must be specified the interface which to be connect root
  new BorderFrame connected "iFrame" from "main" named "frame"    
  new Hello connected "iCenter" from "frame" named "hello"
  gowait()       
  ...                                       
  Run applet: Sorry, this browser does not understand Java applets or they are not enabled right now.
  Next to this project will do clock face:
  Run applet: Sorry, this browser does not understand Java applets or they are not enabled right now.
  Now make three small component: Button, Zoomer and Alarm. Together it is an alarm clock that will be ring after 15 seconds the work
  Run applet: Sorry, this browser does not understand Java applets or they are not enabled right now.
  Now we have to make the time picker, but first let's talk about the variety of components and about why we need root-interfaces. There are three types of components:
*. Bases - have no root, and always static.
*. Static Compo - components that creates in the assembly and having a name.
*. Dynamic Compo - components creates by other components and do not have a name. Time picker will be is the same.  
  Figuratively you can imagine that the static component is the skeleton of the application, while the dynamic is its meat. The components are organized into trees where the root is the Base next followed static Compo and in the end is dynamic Compo. Visually it looks like this:
Base and static Compo creates are only at the start of the assembly, and existents all the time work of the program, therefore the destruction of at least one of them destroys all the rest and assembly work will be completed. Dynamic Compo - can be created and destroyed any time and their destruction leads only to destroy the containing branch. Static Compo differs only the fact that been created in the assembly.  
  Ok, let's define our time picker component:
  class TimePicker extends JDialog(new JFrame()) with Compo{
    //Self-assembly
    ...
    //Interfaces
    protected val iTimePicker = root(new ITimePicker.Am)
    //Each component may have constructor and/or deconstructor.
    //In Compo constructor and deconstructor are the functions connection/disconnection for the root interfase.  
    constructor((h)=>{...})    
    deconstructor((h,i)=>{...}) 
    ... 
  }
  And extend the previously defined clock face with new interface iMouseEvent:
  class AClock extends Clock {               
    //Interfaces
    protected val iMouseEvent = multijack(new IMouseEvent.Am)  
    ...
  }
  Finally define component that connects everything together, and contains code that creates dynamic Compo TimePicker:
  class Knot extends Compo {
    //Interfaces
    protected val iClockEvent:IMouseEvent.Pm = root(new IMouseEvent.Pm{
      override def event(e:MouseEvent) = {
        if(e.getID() == MouseEvent.MOUSE_PRESSED && iTimePicker.imports == null){
          ... 
          //Compo TimePicker creats as a connected to the interface iTimePicker from Knot 
          new TimePicker joined "iTimePicker"}}}) 
    protected val iTimePicker = jack(new ITimePicker.Pm{...})                                                       
    ...
  }
  Run applet: Sorry, this browser does not understand Java applets or they are not enabled right now.
Edit and Notes
  More of applications with reusable components:
Simple editor:
  Run applet: Sorry, this browser does not understand Java applets or they are not enabled right now.
The program for notes with reminders:
  Run applet: Sorry, this browser does not understand Java applets or they are not enabled right now.
If you notice in the examples I’m not write components are fully, I do wrap already existing classes, it is a good way to fast compo-creating.
Work with console
  The console gives you the extension of possibility for control assembly in real-time. To display the console, use the keyword console instead go or gowait, for example:
  ...
  visualization
  new Main named "main"  
  ...
  console  
  end  
  ...                                                          
  Run applet to play with console(like in demo video): Sorry, this browser does not understand Java applets or they are not enabled right now.
And so on
  You can continue indefinitely, adding and / or replacing components more and more evolve the application and the components library, without a fear of the rise of complexity and confusions. Thanks to visualize you can quickly determine the appearance of problems, and due to the low confusion of structure, you can easily solve the problem by replacing components.

Gratitude
  Thanks to the people whose work I used:
Jakob Fischer - font;
JUNG-team - graph framework;
BeanShell-team - JConsole;
and the rest.

Download
skidbladnir.jar(~1.4Mb) - library;
CompoDev.zip(~1Mb) - full project(without JUNG and BeanShell libs).

PS
  This prototype is only a pale likeness of that it is possible to do with this paradigm, and it is very crude. Before make it really useful much remains to be work out (in particular logic and multi-threading model, as well as integration with the Scala). But I hope you get the basic idea.

No comments:

Post a Comment