Scripts/Costream

From J Wiki
Jump to: navigation, search

Original prototype 14 Mar 2007.

In object-oriented programming (OOP), the principle of encapsulation is understood as object being a holder of state contained within its properties. A related issue is how to preserve that state when object goes out of memory, such as between program executions.

Such external persistence of object state is commonly referred to serialization, an ability to store the object state in an external storage and later recreate the object from that storage.

It is interesting to note that J already has native persistence mechanism as used in configuration files.

Because of object-oriented nature, serialization is different from flat file or record based persistence, applying the same encapsulation principle in a way that an object "knows" how to serialize itself. On the other hand, discovering the object features through common interface, the system can homogenously serialize lists and hierarchies of objects, leveraging another principle of polymorphism.

Serialization was one of the most compelling use cases when OOP was popularized with the relases of Turbo Pascal 5.5 back in 1989. There each object registered with stream having and implemeted method Store and constructor(!) Load to store itself on a stream, whereas stream had Put and Get methods, such that Put stored a class identifier and Get initialized an object from stream with its Load constructor. As a result it has been very convenient to have any kind of hierarchy in memory, and then simply zoom and dump it all on disk. Although all serializer methods had to be implemented manually.

Now in frameworks like Java and .NET reflection is used to automate the serialization and descriptive attributes to declare which attributes and how are affected.

Here we will look at how hierarchical object serialization can be represented in J, where dynamically typed and interpreted environment can be leveraged.

We start with an example of typical use cases for property serialization. [{{#file: "example.ijs"}} Download script: example.ijs ]

require 'costream myclasses'

'Root'cobegin'Class1'        NB. or locally r=. cobegin''conew'Class1'
  PROP1=: 1
  PROP2=: <'!@#'
  'OBJPROP1'cobegin'Class2'
    PROP4=: 4 5 6
    LIT1=: 'one two'
    LIT2=: 0 : 0
werwerwer
werwerwer
)
    LIT3=: }:0 : 0
werwerwer
werwerwer
)
    'LIST1'cobegin'Class3'
      P1=: 1
      P2=: 2
    coend''
    'LIST1'coadd'Class3'
      P1=: 1
    coend''
    'LIST1'coadd'Class3'
      P2=: 1
    coend''
  coend''
coend''

Here the following features are illustrated

  • properties are represented as assigned names, the same as configuration pattern
  • J native types: atoms and arrays, are assign just it their string representation with multiline strings using the 0 : 0 notation for readability
  • objects are represented as a set of properties in their locales where locale switching perfomed by a pair of cobegin/coend verbs
  • lists of object are supported
  • syntax sugar of dyadic cobegin and coadd compactly does sevaral things in one call: object instantiation, assignment to parent property, switching to child locale

With such format we attain sevaral goals

  • it is natively executed as a J script file
  • it uses natural mechanism for object instantiation and restoration of state
  • it is humaly readable

Classes that are instantiated are defined in a separate script. [{{#file: ""}} Download script: ]

coclass'Class1'
  COREP=: ;:'PROP1 PROP2 _OBJPROP1'
create=: 3 : 0
  TYPE=: 'Class1'
)

coclass'Class2'
  COREP=: ;:'PROP4 LIT1 LIT2 LIT3 _LIST1'
create=: 3 : 0
  TYPE=: 'Class2'
  PROP5=: 'unused'
)

coclass'Class3'
create=: 3 : 0
  y
)

Finally, the syntax sugar verbs to define the locale switching brackets are defined in a colib-complementary script [{{#file: "costream.ijs"}} Download script: costream.ijs ]

NB. costream - object serialization

coclass 'z'

cobegin=: ([ cocurrent f.)@(3 : 0)
  if. 0>nc <'COSTACK_j_' do. COSTACK_j_=: '' end.
  COSTACK_j_=: COSTACK_j_,coname''
  y
:
  cobegin (x)=: '' conew y
)

coadd=: ([ cocurrent f.)@(4 : 0)
  cobegin {:(x)=: x~,'' conew y
)

coend=: ([ cocurrent f.)@(3 : 0)
  w=. {:COSTACK_j_
  COSTACK_j_=: }:COSTACK_j_
  w
)

NB. corep v locale representation as string
NB.   str=. 'RootName' corep Root
corep=: 3 : 0
  'Root' corep y
:
  r=. ''
  if. 0>nc <'COREPIND_j_' do. COREPIND_j_=: '' end.      NB. global recursive indent
  if. a: -: c=. {.(copath ::(a:"_) y)-.<,'z' do.
     COREPIND_j_,x,'=: ',(5!:5<'y'),LF return.           NB. if not object represent as value
  end.
  if. '+'={.x do. x=.}.x[s=.'add' else.s=.'begin'end.    NB. for list items use coadd
  r=. r,COREPIND_j_,'''',x,'''co',s,'''',(>c),'''',LF    NB. cobegin
  COREPIND_j_=: COREPIND_j_,'  '
  if. a=.0>nc <'COREP__y' do. l=.nl__y'' else. l=.COREP__y end.
  l=. l-.<'COCREATOR'
  for_i. l do.
    m=. >i
    n=. m-.'_'
    p=. n,'__y'
    if. 0>nc< p do. continue. end.
    v=. p~
    if. nc< p do.
      r=. r,COREPIND_j_,n,'=: ',(5!:5 < p),LF
    elseif. ('_'={.m) do.                              NB.  +.a*.((,1)-:~.;#@$&.>v)*.(2>#@$v)*.1=L.v
      r=. r,n corep {.v
      for_j. }.v do.
        r=. r,('+',n) corep j
      end.
    elseif. 1 do.
      r=. r,COREPIND_j_,n,'=: '
      if.  0:`((10&e. *. 31&< *. <&127)@:(a.&i.))@.((0<#)*.2=3!:0) v do.
        r=. r,(k#'}:'),'0 : 0',LF,v,(LF#~k=.LF~:{:v),')',LF
      else.
        r=. r,(5!:5 < p),LF
      end.
    end.
  end.
  COREPIND_j_=: _2}.COREPIND_j_
  r=. r,COREPIND_j_,'coend''''',LF                       NB. coend
)

The verb corep above gerates string representation similar to example.ijs above:

   corep Root
'Root'cobegin'Class1'
  'OBJPROP1'cobegin'Class2'
    PROP4=: 4 5 6
    LIT1=: 'one two'
...
coend''

See Also

  • "Be Objective.", Turbo Pascal 5.5, May 1989. One of the first OOP mainstream offerings for PC, it provided object persistence. Compare Turbo C++ 1.01, Feb 1991.