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
}