Skip to content

objectionary/eo2py

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

eo2py

build codecov

A Python implementation of EO.

This repository contains:

  • Transcompiler
    • Maven plugin that transforms .eo programs into .py programs using XSLT.
  • Python runtime environment
    • Implementation of atoms and data objects as a pip package.
  • Examples of translated programs in eo-python-runtime/tests/example_programs as pytest unit tests.
  • Sandbox to compile and execute your own EO programs!

How to use

Check out README.md in sandbox directory.

Supported features:

  • Abstraction
  • Positional application
  • Decoration (nested decoration, free decoratees)
  • Dataization
  • Inner objects, both closed and abstract
  • Varargs
  • Arrays

Unsupported features:

  • Metas
  • Dataize Once (!)
  • memory atom
  • Regexes
  • Arbitrary partial application, argument labels
  • @ with free attributes
  • Anonymous abstract objects (as arguments to copying)
  • Maybe some more (whenever you experience a bug, feel free to submit an issue)

Code mappings

Abstraction

At the time of declaration, abstract and closed objects map to classes. In __init__() method, special (@, $, and ^) attributes are created, and free attributes are initialized to DataizationError() (more about this). Bound attributes become methods decorated with @property. Projections of object and attribute names get prefixes EO and attr_ correspondingly to avoid name collisions between user-defined and inner entities.

[isbn] > book
  "Object Thinking" > title
class EObook(ApplicationMixin, Object):
    
    def __init__(self):
        # corresponds to `$`
        self.attr__self = self 
        
        # corresponds to `^`
        self.attr__parent = DataizationError()
        
        # corresponds to `@`
        self.attr__phi = DataizationError()
        
        # corresponds to `isbn`
        self.attr_isbn = DataizationError()
        
        self.attributes = ["isbn", ]
    
    # corresponds to `title` bound attribute
    @property
    def attr_title(self):
        return (String("Object Thinking"))

Inner Objects

Attribute might be tied to an abstract object. Our translation model utilizes classes and partial() method defined in functools (Python standard library package) to specify ^ a.k.a. attr__parent object:

[x y] > point
  [to] > distance
    length. > @
      vector
        to.x.sub (^.x)
        to.y.sub (^.y)
class EOpoint(ApplicationMixin, Object):

    def __init__(self):
        # omitted for brevity 
        self.attributes = ["x", "y", ]

    @property
    def attr_distance(self):
        return partial(EOpointEOdistance, self)
        

class EOpointEOdistance(ApplicationMixin, Object):

    def __init__(self, attr__parent):
        # omitted for brevity
        self.attributes = ["to", ]

    @property
    def attr__phi(self):
        return (Attribute(
                   (EOvector()
                       (Attribute((Attribute((self.attr_to), "x")), "sub")
                            (Attribute((self.attr__parent), "x")))
                       (Attribute((Attribute((self.attr_to), "y")), "sub")
                            (Attribute((self.attr__parent), "y")))),
                "length"))

Decoration

Functionality of decoration is achieved via attr__phi attribute combined with class-wrapper Attribute. Whenever specific attribute is needed, dataize() in Attribute searches for the attribute in object itself; upon failure recursively searches for it in object bound to attr__phi.

[] > a
  "nothing else matters" > a_message

[] > b
  a > @
  
[] > c
  b.a_message > c_message
class EOa(ApplicationMixin, Object):
    
    def __init__(self):
        # special & free attributes handling
    
    @property
    def attr_a_message(self):
        return (String("nothing else matters"))
    

class EOb(ApplicationMixin, Object):
    
    def __init__(self):
        # special & free attributes handling
        
    @property
    def attr__phi(self):
        return (EOa())
        


class EOc(ApplicationMixin, Object):
    
    def __init__(self):
        # special & free attributes handling
    
    @property
    def attr_c_message(self):
        return (Attribute((EOb()), "a_message"))

Application

Being a copy of objects in EO with some arguments, application is class instantiation in Python. Current implementation supports only full positional application (in absence of free attributes in decoratee) as overwritten __call__() methods. In order to apply some argument to an object you need to __call__() an object with this argument. __call__() return an object itself, which means that these 'applications' can be chained as follows:

obj(arg1)(arg2)(arg3)...

The particular implementation of __call__() is injected into each Object with the ApplicationMixin class defined in atoms.py, whom all classes inherit from:

[truth_value] > answer
  truth_value.if "yes" "no" > @
class EOanswer(ApplicationMixin, Object):
    
    def __init__(self):
        # special attributes
        
        self.attributes = ["truth_value", ]
    
    @property
    def attr__phi(self):
        return (Attribute((self.attr_truth_value), "if")
                   (String("yes"))
                   (String("no")))

Varargs

As per EO paper, all the varargs are packaged into an Array atom and can be accessed by index using get attribute:

[arg1, arg2, varargs...] > obj
  stdout > @
    sprintf
      "%d %d %d"
      get.
        varargs
        3
      arg1
      arg2
class EOobj(ApplicationMixin, Object):
    def __init__(self):
        # Free attributes
        self.attr__parent = DataizationError()
        self.attr__self = self

        self.attributes = ["arg1", "arg2", "varargs"]
        self.attr_arg1 = DataizationError()
        self.attr_arg2 = DataizationError()
        self.attr_varargs = Array()
        self.varargs = True

    @property
    def attr__phi(self):
        return Stdout()(
                   Sprintf()
                       (String("%d %d %d"))
                       (Attribute(self.attr_varargs, "get")
                           ((Number(3)))
                       (self.attr_arg1)
                       (self.attr_arg2)
        )

Dataization

All classes (think, abstract objects), both auto-generated and atomic, implement an Object interface with only one method - dataize(). This method is responsible for reducing a complex object to some Atom object. The Atom object, in turn, can not be dataized any further and will simply return itself whenever it receives a dataization request.

class Atom(Object):

    def dataize(self):
        return self
        
    @abstractmethod
    def data(self):
        raise NotImplementedError()

You can query the actual data associated with the Atom object by calling its data() method. This method will return the actual Python data (str, int, object or None), whereas dataize() only returns an Atom object.

The dataization process can be summed up as follows:

  • If an auto-generated Object is dataized, it returns self.attr__phi.dataize()
  • If an atomic Object (e.g. Attrubute) is dataized, it returns some Atom based on its internal implementation, potentially with some side effects (Stdout).
  • If an Atom is dataized, it returns itself.

If for some reason an object does not define attr__phi attribute, then its dataization would result in an AttributeError.

DataizationError

All the uninitialized free attributes are initialized as a special DataizationError object. As its name suggests, an attempt to dataize() it would yield a runtime exception in Python, resulting in immediate termination of the program. Attributes initialized with DataizationError are intended to be assigned some value at runtime.

Further considerations

We are yet to come up with examples of:

  • Partial application
  • Application of abstract object
  • Accessing the free attributes of decoratees (O_o)

If you have an example of programs using such constructs of EO, feel free to submit an issue, then we'll see what we can do about it.

A note on partial application

We are thinking of a different way to achieve functionality of the partial application other than object instantiation. The reason is that instantiation in Python makes an object out of class, which is structurally different, whilst objects in EO, as a set, 'are closed' under operation of application. So idea is to either work with objects or classes (proof of concept drafts for classes)