Prograph: A Visual Programming Language

 

 

 

 

 

 

 

 

 

 

David Heise

&

Tomas Joyner

dheise@cs.byu.edu   

 

tomasjoyner@yahoo.com

 

                                               

EECS 330: Introduction to Programming Languages

Fall 2004


Visual Programming Languages:

            For years, programmers have interacted with their computers via a command prompt.  Programs were run, and windows were opened, one line at a time.  Then came the revolution.  When a GUI was applied to an operating system, things began to change.  The visual representation of file structures became more relevant.  Navigating the system and its commands became trivial.  People who ten years before would not even approach the seemingly magical box were now classified as “users”.  As a direct result of the GUI upgrade, the computer has become a staple in every home.

            There is a similar tremor in the waters of the programming world.  Often, when handed a problem, a programmer first determines a sort of flow chart for the program's functionality.  The programmer must then translate the visual concept into textual code.  This mapping typically proves difficult due to “the use of a one-dimensional textual formalism to represent a multi-dimensional process”[1].  The solution in the works is Visual Programming Languages.  Like Windows to DOS, so is Visual Programming to traditional text-based languages. 

            There has been theoretical work on Visual Programming since the late 60's.  Some of the thinking of yesteryear has come to fruition in the form of GPL, LabVIEW, APPWare, and Prograph, among others. 

 

A Brief History:

        As with the creation of most Visual Programming Languages, Prograph was formed with the intent of making programming easier for the programmer.  Prograph graphically shows data movement. In this respect it differs from languages such as C and Java, whose focus is on control passing – that is, stepping through a series of commands.  

        Prograph was conceived in 1983 as the brainchild of Tomasz Pietrzykowski and Philip T. Cox.  While Pietrzykowski soon moved on to other projects, Cox stayed with the language adding improvements over the years.  In '85 Prograph was distributed with the additional feature of a compiler.  When Object-Oriented Programming became the big buzz, Cox announced the advantages of Prograph's modularity and Object abilities. 

        Prograph hit the markets and was used for various applications, though primarily distributed on the Macintosh.  Games, HTML editors, and spreadsheets have all claimed primary usage of the graphical language.  Businesses also turned to Prograph for some in-house applications. 

        Today Prograph is dead.  Programming software for Prograph is still existent, and sold, but the language has grown stagnant.  Pictorius, the primary distributor of Prograph (with Cox at the head), went under in 1996. 

 

The Prograph Language:

Text Box: Figure 1 – A Simple “Hello World” Program in Prograph            Prograph is a functional language.  It's data flow can be compared to Scheme's (or any other functional language) with the primary difference that the programmer does not have to keep track of parenthesis.  Prograph use is simple: data is represented with lines and methods are various boxes.  Each box contains nodes for inputs (terminals) and outputs (roots).  Prograph methods have more than just variable arity; they also have output arity.  The methods have an I->O mapping from one or more inputs (I) to one or more outputs (O). 

            The data in Prograph is passed by value.  This enables several methods the use of a single output from another method.  These receiving methods can then be run in parallel without one method altering the data in another.  This feature will find its usefulness if ever the von Neumann model for parallel processing is upgraded to its more visionary counterparts.

            Heretofore, method inputs and outputs have been referred to as data as opposed to variables.  Strictly speaking, Prograph does not have variables.  Inputs and outputs are named only as a convenience to the programmer.  A consequences of this is lower overhead for a method's environment (if there are no variables, there is no need for an environment).  Scoping, in this context, becomes irrelevant. 

Text Box: Figure 2 – Classes and Inheritance. Classes a through e are depicted and inheritance is shown bottom to top (i.e. class e inherits from class c which inherits from class a)            Object-Oriented Programming comes naturally with Prograph's modularity.   A class is defined just as readily as a persistent, and is still visually expressive.  A class contains methods and variables.  In this case, variables are simply persistents with another name.  Inheritance is easily shown via a line from the parent class to the child class. 

            Learning to use Prograph is time consuming.  After getting acquainted with the graphical nature of the language, the programmer must then prepare for the challenge associated with learning the variety of constructs. On top of that, some universal constructs (such as conditionals) have their own Prograph version that requires attention.  The conditional, for example, has 16 different possible implementations.

            The greatest disadvantage of programming in Prograph is the challenge of all Visual Programming Languages: spaghetti code.  For a simple program with 15 methods, each with three inputs and three outputs, the potential for readability loss is great.  This difficulty is overcome with experience.  In practice, Prograph is quite readable (perhaps “viewable” would be more descriptive).

 

Constructs and Syntax

Text Box: Figure 3 – Some Prograph Constructs            In Prograph there exits 9 basic constructs, 6 external constructs, 4 main constructs, and 10 control constructs. These constructs, in addition to the other symbols and cases, provide the ability to program as in any other language. The four main constructs are named: section, universal, class, and persistent. These are the overall constructs that allow for creation of programs. Sections consist of universals, classes, and persistents. Universals are methods (i.e. functions, procedures, subprograms). Classes, as in other languages, are data structures that allow for the combining of data and the methods that operate on that data. Classes consist of methods and fields. The class methods and fields are essentially the same as universals and persistents respectively. Persistents are most similar to global variables in most other languages; however their data persists from run-time to run-time such that the data retains its value until changed or the entire process is destroyed.

            Methods (whether class methods or universals) consist of the remaining 15 constructs. The basic nine constructs are: simple, constant, match, persistent, instance, get, set, local, and evaluate.  These constructs provide for the main program execution and are at the heart of Prograph.  All of these constructs have some combination of terminals and roots. Not all constructs have both or either, and a construct could have an arbitrary number of roots and/or terminals. For example the + primitive has two terminals and one root (i.e. two inputs and one output).  Methods also have cases. All methods end with either a success, failure, or error message. These messages provide the control logic for the constructs and make the cases possible.

            Cases are similar to the 'if-then-else' construct, and variations thereof, found in most languages. When a method is initially called the first case is run, if, during the course of the first case, a next-case control is found and applied then the second case of the method will be run. The second case is called with the exact same inputs as the previous case. If no second case exists, a run-time error is thrown. This is applicably recursive, in that the second case will call the third case and so on.

Figure 4 – An iterative factorial program

 
 


           

Within a case of a method the nine basic constructs in conjunction with the control operators provide the framework for programming. A simple construct, denoted by the rounded corner rectangle, is simply a call to another method. There are five additional graphical annotations to a simple, not including control symbols, which reveal additional information about the method call. A bar under the method name indicates that the method call is to a primitive. A bar over the method name indicates that it is to an external method call (i.e. from a DLL, or external program of some sort). A single slash before the method name indicates that the method is a class method and Prograph will determine the proper method to call from the instance that is required as the leftmost root. A double slash before the method name indicates that the method is context-determined thus, for instance, the method might be another method within the same class. Also an up arrow next to the simple indicates that it is a call to a class method of its parent class (i.e. super).

            A Constant construct, denoted by the constant value with a bar and a root underneath, allow for constants as in other languages. A Constant value can be any valid value and will be automatically typed.

            A Match construct, denoted by a terminal and bar over a constant value and nearly always with a control construct to the right, allow for if statements. A match will attempt to see if the data input is the same as the constant supplied. Depending on the control operation associated with the match, they may do as the control indicates (controls will be discussed in further detail later in this discussion).

            Persistent constructs access the value of the persistent (i.e. global values). It has at most one terminal and one root, to allow for both reading and writing to the persistent.

            Instance constructs create an instance of the class. It has one terminal and one root. If no values flow into the terminal then the class is initialized with no parameters, however the permission of the terminal indicates that class constructors can take inputs.

            Get and Set constructs allow for reading and writing of class attributes. Both constructs take in an instance into its terminal, and an instance out of its root. The Get has an additional root to provide for the data requested, and the Set has an additional terminal to provide for the storage of the requested data in the requested attribute.

            Local constructs allow for grouping of a section of code into a single graphical element. This provides for a better view of the program as Locals allow for better space management of the development environment, which is identical to the expensive silicon real estate in circuit design.

            The Evaluate construct provides mathematical expressions into the language. An Evaluate has one root, and as many terminals as desired. The expression uses the alphabet (case insensitive a to z only) as designations for terminals within the expression. For example the expression a/b+c would have three terminals, starting on the left with a, then b in the middle, and c on the right. The Evaluate will have as many terminals as letters used, however letters must be used in alphabetical order (i.e. using the letter x will automatically make 24 terminals to the Evaluate construct). The expression may contain the following mathematical operations: @ (exponentiation), + (binary and unary addition), - (binary and unary subtraction), * (multiplication), / (floating point division), // (integer division), % (remainder from integer division), & (bitwise AND), | (bitwise OR), ^ (bitwise XOR), ~ (unary bitwise NOT), << (bit shift left), and >> (bit shift right).

            The External constructs simply allow the Simple, Constant, Match, Persistent, Get, and Set to reference external data stored in a DLL, etc. In addition to these there is also an External Address construct that takes an external structure as input and returns a pointer to that structure. The External Persistent is called External Global to point out the differences between a Prograph Persistent and the idea of Global variables in other languages.

            The Controls in Prograph allow for change of the control flow based on how constructs terminate. The most common use of controls is on a Match construct. The logic for the control is denoted by a check or an x beside the construct. Also a control will dictate what course of action should be taken upon a successful check: Next Case, Terminate, Finish, Continue, and Fail. The check indicates that it is true, while the x indicates it is not true. For example say we have a Match named STRING with a check next to the name. If the input into the Match is also STRING then it would do the appropriate action dictated by the control (i.e. Fail, Continue, etc). The control action directs the program execution. The Next Case action will cause the method to execute the next case with its current inputs and is depicted with a rounded box around the check or x. The Terminate action will cause the method to immediately end execution with a successful result and is depicted with rounded box and a bar above the check or x. The Finish action will cause the method to complete the current case then immediately end execution with a successful result and is depicted with a rounded box and a bar below the check or x. The Fail action will halt the current method and send a fail signal to the calling method and is depicted with a circle around the check or x. The Continue action will cause execution to continue in the current case and is depicted with a rounded box and a bar above and a bar below the check or x.

            In addition to the aforementioned constructs, individual constructs can be connected by a Datalink and/or a Syncro link. A Datalink connects a root and a terminal depicting flow of information. It is this depiction and functionality that make Prograph a functional language. A Syncro link directionally connects two constructs guaranteeing that start node will execute before the end node. Since any construct could execute at any time (and is chosen randomly at design time), the Syncro link provides a way to have the traditional imperative execution to the degree chosen by the developer.

Text Box: Figure 5 – Syncro Links; a will run before both b and c; both b and c will run before d, but no guarantee about the order of execution for b and c            Other constructs not explained here include (but are not limited to): Repeat annotations (construct is repeated until a fail, terminate, or finish signal is sent), List annotation (construct will apply itself to every member of the list), Loop annotations (construct is repetitively called with the outputs sent as inputs to the next iteration), Partition annotations (splitting a list based on Boolean criteria), and the Inject control (allowing for the name of the operation to be supplied at run-time).

 

Types

            In Prograph there exist the following types: boolean, integer, list, none, null, point, real, rectangle, rgb, string, and undefined. Boolean values are denoted and displayed as TRUE or FALSE (case sensitive). Integers consist of traditional signed integers (2^32), a number of any base (i.e. a binary number 2#10010101), and a 1, 2, or 4 byte ASCII character sequence. A list is identical to a list in scheme or lisp with a limited number of elements (2^16 - 1 in the interpreter, and 2^32 -1 in the complier). The none type is used by Prograph to depict a type when a terminal is not connected to a datalink. The Null type indicates no value on a root or a class attribute. The point type is two integers enclosed by curly brackets ({}) and separated by a space; it represents the coordinates for a two dimensional point in space. Reals are the same as traditional real numbers allowing for values from -1.1E+4932 to +1.1E+4932. A Rectangle type depicts the coordinates for two points defining the upper left and lower right corners of a rectangle; this type is like a point but with four values instead of two. The RBG type indicates a color denoted by the red blue and green values that compose it; this is similar to a point but with three values. A string in Prograph consists of any sequence of typeable characters limited in length to the same constraints of the list type. Finally the undefined type is another internal type used by Prograph when an operation/construct is skipped during execution.

            Types may be specified on roots and terminals, but are not required. In addition to the aforementioned types, the specified type may also be: pointer, direct, a class name, a list of types, or <<>> (which indicates that the type must be of the same class the method is in). For more exact specifics on type definitions see the BNF of the data types in the appendices.

 

Conclusion

            Like all languages, Prograph has its positive and negative aspects. It provides an easy way to depict programs following a flow model, and the translation from flow chart to code is very simple, as code is just as graphical as the flow chart. One of the biggest disadvantages of all visual languages is the longer than normal learning curve that a programmer must go through before becoming proficient in the language. Also to Prograph specifically, it is a dead language in that it is not widely used and as a language is not being developed. However Prograph, with other visual languages, provide for a new dimension to traditional programming not found elsewhere.


Appendix A: Type BNF

<simple value>    ::= <delimited value> | <default string>

<string>          ::= <quoted string> | <atomic string> | <default string>

<default string>  ::= any string which is not a <delimited value>

<delimited value> ::= <quoted string> | <atomic string> | <integer> | <real> | <boolean> | <null> | <undefined> | <none>| <list> | <simple ext>

<atomic string>   ::= any <atom> other than FALSE, TRUE, NULL, UNDEFINED, or NONE

<boolean>   ::= FALSE | TRUE

<null>      ::= NULL

<undefined> ::= UNDEFINED

<none>      ::= NONE

<list>      ::= (<value*>)

<value*>    ::= <delimited value> <space><value*> | <delimited value> | <empty>

<simple ext>::= <point> | <rectangle> | <RGB>

<point>     ::= {<integer> <space> <integer>}

<rectangle> ::= {<integer> <space> <integer> <space> <integer> <space> <integer>}

<RGB>       ::= {<integer> <space> <integer> <space> <integer>}

<space>     ::= any nonempty string of characters whose ASCII values are less than or equal to that of blank

<quoted string>     ::= <no double>"<quoted string> | "<no double>"

<no double>         ::= any string of characters, except "

<atom>              ::= <letter atom> | <special atom>

<letter atom>       ::= <letter> <alphanumeric> | <letter>

<special atom>      ::= any nonempty string of characters having ASCII values greater than that of blank except those included in <alphanumeric>

<integer>           ::= <sign> <unsigned integer>

<unsigned integer>  ::= <decimal integer> | <based integer> | <ascii integer>

<decimal integer> ::= <digit> <spaced digits>

<spaced digits>   ::= <digit> <spaced digits> | _<spaced digits> | <empty>

<based integer>   ::= <digit+>#<alphanumeric>

<alphanumeric>    ::= <letter> <alphanumeric> | <digit> <alphanumeric>|    <letter> | <digit>

<letter>          ::= a | b | c | d | e | f | g | h | i | j | k | l |m | n | o | p | q | r | s | t | u | v | w | x | y | z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | _

<ascii integer>  ::= '<no single>' | '<no single><no single>' |

                    '<no single><no single><no single><no single>'

<no single>      ::= any string of characters having ASCII values greater than that of blank, except ' | ' '

<real>           ::= <sign> <mantissa> <exponent>

<mantissa>       ::= <digit+> | <digit+>.<digit*> | <digit*>.<digit+>

<exponent>       ::= <sign> <digit+> | E<sign> <digit+> | <empty>

<sign>           ::= - | - | <empty>

<digit*> ::= <digit+> | <empty>

<digit+> ::= <digit> <digit*>

<digit>  ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

<empty>  ::= the empty string

 

Appendix B: Primitives by Category

Bit

            bit-and             bit-not bit-or   

            bit-shift-l           bit-shift-r          bit-xor

            test-all?            test-bit?            test-one?         

Callbacks

            dispose-method-pointer                        make-method-pointer  

COM

            COM-create-object     COM-dispose-object  

Data

            copy                             get-resource-info          get-resource-list           

            get-string-resource        inst-to-list                     list-to-inst        

            shallow copy                           

File

            close                create               delete  

            file-size             get-file get-position     

            open                 put-file read    

            read-line           rename             resource-file

            set-position       write                 write-line         

Graphics

            find-bounds                  ints-to-point                  ints-to-rect      

            ints-to-rgb                    point-to-ints                  points-to-rect  

            rect-to-ints                   rect-to-points               rgb-to-ints       

Input/Output

            accept              answer                         answer-v         

            ask                   display             print-text         

            select                set-dialog-font show   

Interpreter control

            abort                                        abort-callback             

            compiled?                                 debug              

            call                                           execute                        

            find-method                              halt                  

            lookup-external-constant          open-info-window                   

            open-method-window switch-to-prograph                  

            trace                                         yield-cpu                     

Lists

            (in)                               (join)                (length)            

            attach-l                         attach-r            detach-l           

            detach-nth                    detach-r           find-instance    

            find-sorted                    get-nth insert-nth         

            make-list                      pack                 reverse            

            set-nth                         set-nth!             sort     

            split-nth                        unpack                        

 

 

Load & Save/Data Clustering

            clear-bytes-map           cluster-dispose             cluster-new      

            cluster-size                   from-bytes                    load     

            save                             set-undefined-class       to-bytes           

Logical

            <                      <=                    =         

            >                      >=                    and      

            choose             not                   or        

            switch               xor                   ~=       

Math

            *                      **                    +         

            ++                    +1                    -          

            --                     -1                     abs      

            acos                 annuity asin     

            atan                  atan2                compound       

            cos                   div                    exp      

            idiv                   ln                      log10   

            max                  min                   pi        

            power              rand                 round  

            round-down     round-up          set-seed          

            sign                  sign-extend       sin       

            sqrt                  tan                    trunc    

Memory

            address-to-object         block-address              block-size        

            compact-memory         dispose-pointer from-handle     

            from-pointer                 get-cstring                    get-external-size          

            get-integer                    get-point                       get-process-refcon      

            get-real                         get-rect                        get-string         

            get-text                         heap-size                      lock-block       

            lock-string                    make-direct                  make-handle    

            make-pointer                memory-callback          new-block       

            object-to-address         put-cstring                    put-integer       

            put-point                      put-real                        put-rect           

            put-string                      put-text                        set-process-refcon       

            string-address               to-handle                      to-pointer        

            unlock-block                unlock-string                 valid-heap?      

String

            format                          byte-length       from-ascii        

            from-string                    ‘inÓ                 integer-to-string           

            ‘joinÓ                          ‘lengthÓ           middle

            munge-string                 prefix                string-length     

            string-to-integer            suffix                to-ascii            

            to-string                        tokenize                       

System

            ancestors                      attr-com                       attributes         

            called-from-get             called-from-meth          called-from-set            

            calls-to-get                   calls-to-meth                calls-to-set      

            children                        class-com                     class-section    

            classes              create-class                  create-method

            descendants                  editor-methods             meth-com        

            meth-com-g                  meth-com-s                  meth-io-com    

            meth-io-com-g             meth-io-com-s method-arity    

            method-classes method-section             methods          

            pers-com                      persistent-section          persistents       

            section-com                  section-contents            sections           

            set-attr-com                 set-class-com               set-meth-com  

            set-meth-com-g            set-meth-com-s            set-pers-com   

            set-section-com                                   

Type

            boolean?          instance?                      integer?           

            list?                  external-type                number?          

            real?                 string?                          type     

Win32

            get-application-instances           get-application-main-window   

            get-command-line                     MakeIntResource        

            set-application-window            

 


Appendix C: Optimus Prime

            As part of this project we wrote code in Prograph to implement the Sieve of Eratosthenes. This took quite a bit of work as learning Prograph was not as easy as learning new syntax, but it was an entirely different way of programming. The following image shows the entire program:

            This code will be made available along with the freeware Prolog Windows Interpreter on our website at http://students.cs.byu.edu/~dheise/cs330. The code is run by executing the method “start_Loop”. This will prompt the user for a number and then, using the algorithm, compute all the prime numbers smaller than the given number. One draw back of the freeware interpreter is the limited number of ways to show information to the user. The show primitive has a limited number of lines available to the user and going beyond this limit is possible, but futile since the user cannot see that information. Therefore, although the algorithm correctly computes the information, the user can only see a limited number of results using the show primitive.


Bibliography

[1] Cox, P.T.  and I.J Mulligan "Compiling the Graphical Functional Language PROGRAPH" 1985.

 

[2] Cox, P.T. and Tomasz Pietrzykowski "Advance Programming in PROGRAPH" 1985.

 

[3] Steinman, Scott and Kevin Carver "Visual Programming with Prograph CPX" Manning Publications Co. 3, 1995