Thursday, November 6, 2014

Go Generics - array of subclasses


A basic generic pattern is to use an array of objects and store in it different types of subclasses that all share a common base class. In regular Go you can do this with an array of empty interfaces, and then in your for loop you type switch and copy your code into each case - this is ugly and should be done instead with a code generator like Gython.

Gython Input

class A:
    def foo(self) -> string:
        return 'xxxx'
class B(A):
    def foo(self) -> string:
        return 'hello'
class C(A):
    def foo(self) -> string:
        return 'world'


def push( arr:[]int, x:int ):
    arr.append( x )

def push2( arr:[]*A, x:*A ):
    arr.append( x )

def my_generic( s:A ):
    print( s.foo() )

def main():
    b1 = B()
    barr = []B( b1, )
    c1 = C()
    barr.append( c1 )

    bb = barr[0]
    print(bb.foo())

    cc = barr[1]
    print(cc.foo())

    print('----testing generic----')
    for subclass in barr:
        print('subclass in bar:', subclass)
        my_generic(subclass)

Gython Output

...

type A struct {
    __object__
}
func (self *A)  foo() string {

 return "xxxx"
}
func __new__A() *A {
  ob := A{}
  ob.__class__ = "A"
  return &ob
}
func (self *B)  A_foo() string {

 return "xxxx"
}

type B struct {
    A
}
func (self *B)  foo() string {

 return "hello"
}
func __new__B() *B {
  ob := B{}
  ob.__class__ = "B"
  return &ob
}
func (self *C)  A_foo() string {

 return "xxxx"
}

type C struct {
    A
}
func (self *C)  foo() string {

 return "world"
}
func __new__C() *C {
  ob := C{}
  ob.__class__ = "C"
  return &ob
}
func push(arr *[]int, x int) {

 __8 := append(*arr,x); *arr = __8;
}
func push2(arr *[]*A, x *A) {

 __9 := append(*arr,x); *arr = __9;
}
func my_generic(__gen__ interface{}) {

 __type__ := "INVALID"
 __super__, __ok__ := __gen__.(object)
 if __ok__ { __type__ = __super__.getclassname();
 } else { fmt.Println("Gython RuntimeError - struct must implement the `object` interface"); }
 switch __type__ {
  case "C":
   s,__ok__ := __gen__.(*C)
   if __ok__ {
   fmt.Println(s.foo());
   } else {
    switch __gen__.(type) {
     case *B:
      s := C( *__gen__.(*B) )
      fmt.Println(s.foo());
     case *A:
 fmt.Println("Generics RuntimeError - can not cast base class to a subclass type", s);
    }
   }
  case "B":
   s,__ok__ := __gen__.(*B)
   if __ok__ {
   fmt.Println(s.foo());
   } else {
    switch __gen__.(type) {
     case *C:
      s := B( *__gen__.(*C) )
      fmt.Println(s.foo());
     case *A:
 fmt.Println("Generics RuntimeError - can not cast base class to a subclass type", s);
    }
   }
  case "A":
   s,__ok__ := __gen__.(*A)
   if __ok__ {
   fmt.Println(s.foo());
   } else {
 fmt.Println("Generics RuntimeError - generic argument is not a pointer to a struct", s);
 fmt.Println("struct: ",__gen__);
   }
 }
}
func main() {

 b1 := __new__B();
 barr := &[]*B{b1};
 c1 := __new__C();
 __addr11 := B(*c1);__11 := append(*barr,&__addr11); *barr = __11;
 __subclass__ := (*barr)[0]
 switch __subclass__.__class__ {
  case "C":
   __addr := C(*__subclass__)
   bb := &__addr
   fmt.Println(bb.foo());
   __subclass__ := (*barr)[1]
   switch __subclass__.__class__ {
    case "C":
     __addr := C(*__subclass__)
     cc := &__addr
     fmt.Println(cc.foo());
     fmt.Println("----testing generic----");
     for _,subclass := range *barr {
      fmt.Println("subclass in bar:", subclass);
      my_generic(subclass)
     }
    case "B":
     __addr := B(*__subclass__)
     cc := &__addr
     fmt.Println(cc.foo());
     fmt.Println("----testing generic----");
     for _,subclass := range *barr {
      fmt.Println("subclass in bar:", subclass);
      my_generic(subclass)
     }
   }
  case "B":
   __addr := B(*__subclass__)
   bb := &__addr
   fmt.Println(bb.foo());
   __subclass__ := (*barr)[1]
   switch __subclass__.__class__ {
    case "C":
     __addr := C(*__subclass__)
     cc := &__addr
     fmt.Println(cc.foo());
     fmt.Println("----testing generic----");
     for _,subclass := range *barr {
      fmt.Println("subclass in bar:", subclass);
      my_generic(subclass)
     }
    case "B":
     __addr := B(*__subclass__)
     cc := &__addr
     fmt.Println(cc.foo());
     fmt.Println("----testing generic----");
     for _,subclass := range *barr {
      fmt.Println("subclass in bar:", subclass);
      my_generic(subclass)
     }
   }
 }
}
type _kwargs_type_ struct {
}

Sunday, October 12, 2014

Go Generics Part2 - Returning Subclasses


Subclass Unions

Go can convert between different struct types if they have the same struct layout, like a union in C. Gython supports generics for methods that return different types of subclasses by generating code to recast the return type if it is different from the method's return type. This allows attributes of the returned object to be used from the caller.

Gython

class A:
 def __init__(self, x:int):
  int self.x = x
  int self.z = 0

 def bar(self) -> int:
  return self.x


class B(A):
 def __init__(self):
  A.__init__(self, 10)
  int self.z = 1

 def bar(self) ->int:
  return self.x + self.z

class C(A):
 def __init__(self):
  A.__init__(self, 100)
  int self.z = 100
  int self.w = 1

 def bar(self) ->int:
  return self.x + self.z + self.w

In the example above the subclass C extends the union by declaring a new integer variable int self.w = 1. In the generated Go code below the variable w is also inserted into the struct typedef of B. The base class A implements __object__ that contains the class name.

Go

type A struct {
 __object__
 x int
 z int
}
func (self *A)  __init__(x int) {

 self.x = x;
 self.z = 0;
}
func (self *A)  bar() int {

 return self.x
}
func __new__A( x int ) *A {
  ob := A{}
  ob.__init__(x)
  ob.__class__ = "A"
  return &ob
}
func (self *B)  A___init__(x int) {

 self.x = x;
 self.z = 0;
}
func (self *B)  A_bar() int {

 return self.x
}


type B struct {
 A
 w int
}
func (self *B)  __init__() {

 self.A___init__(10)
 self.z = 1;
}
func (self *B)  bar() int {

 return (self.x + self.z)
}
func __new__B(  ) *B {
  ob := B{}
  ob.__init__()
  ob.__class__ = "B"
  return &ob
}

func (self *C)  A___init__(x int) {

 self.x = x;
 self.z = 0;
}
func (self *C)  A_bar() int {

 return self.x
}

type C struct {
 A
 w int
}
func (self *C)  __init__() {

 self.A___init__(100)
 self.z = 100;
 self.w = 1;
}
func (self *C)  bar() int {

 return ((self.x + self.z) + self.w)
}
func __new__C(  ) *C {
  ob := C{}
  ob.__init__()
  ob.__class__ = "C"
  return &ob
}

All subclasses that inherit from a common base class are transformed to share the same attributes, they become a union. This makes the subclasses compatible with each other and allows generics by switching on their class name and type casting. This only works with attributes, but breaks with methods; to support methods a generics type switch is needed in the caller.

Subclass Switch Branching

Methods can be used on a subclass instance returned from a function or method, when the result of the call is first assigned to a variable. At this point Gython will generate a switch that checks the class name of the subclass, and then recast the variable, and enter the branch in the switch where it is that type. The function body from that point is copied into each branch in the switch. This makes the caller aware of the real subclass type of the variable, and methods can be correctly called.

Gython

class A:
 def __init__(self, x:int):
  int self.x = x
  int self.z = 0

 def some_subclass( self, o:A, s:bool ) -> self:
  if s:
   return go.type_assert(o, self)
  else:
   return self

...

def main():
 a = A( 1000 )
 b = B()
 c = C()

 ## tests returning self
 bb = b.some_subclass(b, false)
 w = bb.bar()

 cc = c.some_subclass(b, false)
 w = cc.bar()


 ## tests returning other
 ccc = b.some_subclass(c, true)
 w = ccc.bar()

 bbb = c.some_subclass(b, true)
 w = bbb.bar()


Above a method is defined in the base class some_method that returns the special type self. In its subclasses self will become the subclass type. go.type_assert(X, self) is used to cast the return type to the subclass type.

Go

The generated Go code below shows how the main function is branched for each subclass case that is possible.

func main() {

 a := __new__A(1000);
 b := __new__B();
 c := __new__C();
 __subclass__ := b.some_subclass(b, false)
 switch __subclass__.__class__ {
  case "C":
   __addr := C(*__subclass__)
   bb := &__addr
   w := bb.bar();
   __subclass__ := c.some_subclass(b, false)
   switch __subclass__.__class__ {
    case "C":
     __addr := C(*__subclass__)
     cc := &__addr
     w = cc.bar();
     __subclass__ := b.some_subclass(c, true)
     switch __subclass__.__class__ {
      case "C":
       __addr := C(*__subclass__)
       ccc := &__addr
       w = ccc.bar();
       __subclass__ := c.some_subclass(b, true)
       switch __subclass__.__class__ {
        case "C":
         __addr := C(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
        case "B":
         __addr := B(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
       }
      case "B":
       __addr := B(*__subclass__)
       ccc := &__addr
       w = ccc.bar();
       __subclass__ := c.some_subclass(b, true)
       switch __subclass__.__class__ {
        case "C":
         __addr := C(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
        case "B":
         __addr := B(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
       }
     }
    case "B":
     __addr := B(*__subclass__)
     cc := &__addr
     w = cc.bar();
     __subclass__ := b.some_subclass(c, true)
     switch __subclass__.__class__ {
      case "C":
       __addr := C(*__subclass__)
       ccc := &__addr
       w = ccc.bar();
       __subclass__ := c.some_subclass(b, true)
       switch __subclass__.__class__ {
        case "C":
         __addr := C(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
        case "B":
         __addr := B(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
       }
      case "B":
       __addr := B(*__subclass__)
       ccc := &__addr
       w = ccc.bar();
       __subclass__ := c.some_subclass(b, true)
       switch __subclass__.__class__ {
        case "C":
         __addr := C(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
        case "B":
         __addr := B(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
       }
     }
   }
  case "B":
   __addr := B(*__subclass__)
   bb := &__addr
   w := bb.bar();
   __subclass__ := c.some_subclass(b, false)
   switch __subclass__.__class__ {
    case "C":
     __addr := C(*__subclass__)
     cc := &__addr
     w = cc.bar();
     __subclass__ := b.some_subclass(c, true)
     switch __subclass__.__class__ {
      case "C":
       __addr := C(*__subclass__)
       ccc := &__addr
       w = ccc.bar();
       __subclass__ := c.some_subclass(b, true)
       switch __subclass__.__class__ {
        case "C":
         __addr := C(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
        case "B":
         __addr := B(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
       }
      case "B":
       __addr := B(*__subclass__)
       ccc := &__addr
       w = ccc.bar();
       __subclass__ := c.some_subclass(b, true)
       switch __subclass__.__class__ {
        case "C":
         __addr := C(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
        case "B":
         __addr := B(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
       }
     }
    case "B":
     __addr := B(*__subclass__)
     cc := &__addr
     w = cc.bar();
     __subclass__ := b.some_subclass(c, true)
     switch __subclass__.__class__ {
      case "C":
       __addr := C(*__subclass__)
       ccc := &__addr
       w = ccc.bar();
       __subclass__ := c.some_subclass(b, true)
       switch __subclass__.__class__ {
        case "C":
         __addr := C(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
        case "B":
         __addr := B(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
       }
      case "B":
       __addr := B(*__subclass__)
       ccc := &__addr
       w = ccc.bar();
       __subclass__ := c.some_subclass(b, true)
       switch __subclass__.__class__ {
        case "C":
         __addr := C(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
        case "B":
         __addr := B(*__subclass__)
         bbb := &__addr
         w = bbb.bar();
       }
     }
   }
 }
}

Wednesday, September 24, 2014

array and map comprehensions


There was alot of talk on the golang mailing list about adding array comprehensions to Go, but nothing happened, thread. Gython now supports array and map comprehensions.

array comprehension

a = []int( x for x in range(3) )

An array comprehension is like a Python list comprehension, but it is for a fixed data type and looks like a generator expression.

array comprehension - go output

 __comp__0 := []int{};
 idx0 := 0;
 iter0 := 3;
 for ( idx0 ) < iter0 {
  x := idx0;
  __comp__0 = append(__comp__0, x);
  idx0 += 1;
 }
 a := &__comp__0;

map comprehension

m = map[int]string{ a:'xxx' for a in range(10)}

A map comprehension is like a Python dict comprehension.

map comprehension - go output

  __comp__0 := &map[int]string{  };
  idx0 := 0;
  iter0 := 10;
  for ( idx0 ) < iter0 {
    
    a := idx0;
    (*__comp__0)[a] = "xxx";
    idx0 += 1;
  }
  m := __comp__0;

Tuesday, September 23, 2014

Generator Functions


Gython can compile simple generator functions where yield can be used three times, once before the loop, in the loop, and after. Work still needs to be done on the state machine transformer to support more complex yield returns.

Gython input

def fib( n:int ) -> int:
 int a = 0
 int b = 1
 int c = 0

 for x in range(n):
  yield a
  c = b
  b = a+b
  a = c

 yield -1

def main():
 arr = []int()
 for n in fib(20):
  arr.append( n )

Golang output

type fib struct {
  a int
  c int
  b int
  n int
  __iter_end int
  __iter_start int
  __iter_index int
  __done__ int
}
func (self *fib)  __init__(n int) {

  self.n = n;
  self.a = 0;
  self.b = 1;
  self.c = 0;
  self.__iter_start = 0;
  self.__iter_index = 0;
  self.__iter_end = self.n;
  self.__done__ = 0;
}
func (self *fib)  next() int {

  var __yield_return__ int
  if (( self.__iter_index ) < self.__iter_end) {
    __yield_return__ = self.a;
    self.c = self.b;
    self.b = (self.a + self.b);
    self.a = self.c;
    self.__iter_index += 1;
    return __yield_return__
  } else {
    self.__done__ = 1;
    __yield_return__ = -1;
    return __yield_return__
  }
}
func __new__fib( n int ) *fib {
  ob := fib{}
  ob.__init__(n)
  return &ob
}
func main() {

  arr := &[]int{};
  __genfib := __new__fib(20)
  for __genfib.__done__ != 1 {
   n := __genfib.next()
      __0 := append(*arr,n); *arr = __0;
    }
}

Go Generics


Go has no support for generic functions, [1], [2]. This makes it hard to write functions that take arguments that change type, because you need to do the heavy-lifting yourself, testing an empty interface, and type assert it to an explicit type. The Go library Gen works around this problem by parsing your code and generating code to do the heavy-lifting. To support generics Gython takes a similar approach.

Gython supports generics by looking at the base class the user has given as the typedef for the first function argument. It then replaces this with an empty interface interface{}, and generates a switch-type block that switches on all the subclasses of the base class, then type asserts and reassigns the variable, and finally copies the function body into each switch-case.

gython input

class A:
 def __init__(self, x:int):
  int self.x = x

 def method1(self) -> int:
  return self.x

class B(A):

 def method1(self) ->int:
  return self.x * 2

class C(A):

 def method1(self) ->int:
  return self.x + 200


def my_generic( g:A ) ->int:
 return g.method1()

def main():
 a = A( 100 )
 b = B( 100 )
 c = C( 100 )

 x = my_generic( a )
 a.x == x

 y = my_generic( b )
 y==200

 z = my_generic( c )
 z==300


gython output

type A struct {
  x int
}
func (self *A)  __init__(x int) {

  self.x = x;
}
func (self *A)  method1() int {

  return self.x
}
func __new__A( x int ) *A {
  ob := A{}
  ob.__init__(x)
  return &ob
}

type B struct {
  A
}
func (self *B)  __init__(x int) {

  self.x = x;
}
func (self *B)  A_method1() int {

  return self.x
}
func (self *B)  method1() int {

  return (self.x * 2)
}
func __new__B( x int ) *B {
  ob := B{}
  ob.__init__(x)
  return &ob
}

type C struct {
  A
}
func (self *C)  __init__(x int) {

  self.x = x;
}
func (self *C)  A_method1() int {

  return self.x
}
func (self *C)  method1() int {

  return (self.x + 200)
}
func __new__C( x int ) *C {
  ob := C{}
  ob.__init__(x)
  return &ob
}

func my_generic(__gen__ interface{}) int {

  switch __gen__.(type) {
    case *A:
      g,_ := __gen__.(*A)
      return g.method1()
    case *C:
      g,_ := __gen__.(*C)
      return g.method1()
    case *B:
      g,_ := __gen__.(*B)
      return g.method1()
  }
  return 0
}

func main() {

  a := __new__A(100);
  b := __new__B(100);
  c := __new__C(100);
  x := my_generic(a);
  ( a.x ) == x
  y := my_generic(b);
  ( y ) == 200
  z := my_generic(c);
  ( z ) == 300
}