Search code examples
classoopobjectforthgforth

How do I create a basic class and instance of that class in Forth?


I would like to create an object to represent some electrical readings, such as input voltage. To do this, I want to create a basic class structure to handle different types of readings -- say, current as well as voltage.

The pseudo-code (well, Python actually) for what I want to do is this:

# Create base class as a subclass of a common class to all other classes
class PowerReading(object):
    # Defining word to  initialize instance variables using the given input
    def __init__(self, current_value, units):
        # instance variables 
        self.value = current_value
        self.units = units

# Define new class based on our generic class above
class Voltage(PowerReading):
    # Call the parent class word with an input value, and constant units string 
    def __init__(self, current_value):
        super(Voltage, self).__init__(current_value, 'volts')

# Create another class based on the same parent class as Voltage 
class Current(PowerReading):
    def __init__(self, current_value):
        # Call the parent word with current units
        super(Voltage, self).__init__(current_value, 'amps')

# input_voltage_atod() is defined elsewhere: gives an instant reading
# from the ATOD pin on the power input rail, already converted to units of volts.

# Create instance object variable using our new Voltage class. 
input_voltage = Voltage(input_voltage_atod())
# Use the object's instance variables
print input_voltage.value, input_voltage.units
# 3.25 volts

I'm using Gforth and the oof.fs extension.


Solution

  • With some simple gforth symbols:

    : symbol ( "name" -- )
      create lastxt , does> ( -- xt ) @ ;
    : .symbol ( xt -- )
      >name name>string 1 /string type ;
    
    symbol 'volts
    symbol 'amps
    

    Here's an oof.fs equivalent of your Python:

    require oof.fs
    
    object class power-reading
      float var value
      cell var units
      method .
    how:
      : init ( r-value units -- )  units !  value f! ;
      : . ( -- ) value f@ f.  units @ .symbol space ;
    class;
    
    power-reading class voltage
    how:
      : init ( r-value -- )  'volts super init ;
    class;
    
    power-reading class current
    how:
      : init ( r-value -- )  'amps super init ;
    class;
    
    3.25e voltage : input-voltage
    input-voltage .  \ Output: 3.25 volts
    

    That's pretty similar, isn't it?

    These days I use mini-oof2.fs, which is much lower level than oof.fs, and which does a lot less. In that:

    object class
      ffield: value
      field: units
      method init ( value units -- )
      method show ( -- )
    end-class power-reading
    
    [: ( r-value units -- )  units !  value f! ;] power-reading to init
    [: ( -- ) value f@ f.  units @ .symbol space ;] power-reading to show
    
    power-reading class
    end-class voltage
    
    [: ( r-value -- )  value f! 'volts units ! ;] voltage to init
    
    power-reading class
    end-class current
    
    [: ( r-value -- )  value f! 'amps units ! ;] current to init
    
    voltage new constant input-voltage
    3.25e input-voltage .init
    input-voltage .show  \ Output: 3.25 volts
    

    The [: ... ;] aren't special mini-oof2.fs syntax. They're just gforth quotations, used here as synonyms of :NONAME .

    The .init and .show macro-expand (sort of) into >o init o> and >o show o>, respectively.

    Rather than have an INIT method, I often have a non-OO word that constructs the object:

    : >current ( r -- o )
      current new >o  value f!  'amps units !  o o> ;
    : current, ( r -- )
      here >o [ current >osize @ ]L allot  value f!  'amps units !  o> ;
    

    But of course this doesn't play as well with complex object orientation and SUPER methods and such. It's a 90% solution as opposed to the 100% OO solutions provided by oof.fs and SWOOP and FMS.