Problem creating GenericStruct

11 views
Skip to first unread message

chrisichris

unread,
Jun 9, 2010, 2:19:15 AM6/9/10
to yeti-lang
Hi,

I try to make a smale helper api in Yeti for mongo-db (http://
www.mongodb.org/). What I tried is to load a data item
(com.mongodb.DBObject) and convert it (untyped) into a
yeti.lang.GenericStruct and than cast this struct to the right type.
Ie:

>dbo is ~com.mongodb.DBObject = //somehow query the database
>conv is ~yeti.lang.GenericStruct = fromDBObject dbo; //helper method to convert the mongo DBObject to a ~Struct
>customer = conv unsafely_as {name is string, age is number ...} //casting the struct to what is expected.

I have two questions:
1.) Generally do you think this is a viable approach (making a
GenericStruct and than requiring the user to cast it)
2.) The compiler throws a "java.lang.RuntimeException: No className
for Z" when compiling my "fromDBObject" function:

Code: (using the mongo-db driver 2.0 rc 4 and the latest yeti from
git)

module yongo;

import yeti.lang: AList, Struct, Tag, ByKey,GenericStruct;
import com.mongodb: DB, DBCollection, BasicDBObjectBuilder, DBCursor;
import org.bson: BSONObject;
import java.util: Arrays, Map, HashMap, List, Iterator;


//build a db-object from struct

mapJavaList mapF javaList is (~Object -> 'a) -> ~List -> list<'a> = (
var li = [];
it = javaList#listIterator(javaList#size());
(it#hasPrevious()) loop (
el = mapF it#previous();
li := el :: li;
);
li
);


fromDBObject mapFields dbobject is list<string> -> ~BSONObject ->
~Struct = (

makeValue fieldName mapFields rawValue is string -> list<string> -
> ~Object -> ~Object = (
if rawValue instanceof List then
li = rawValue unsafely_as ~List;
r = mapJavaList do ob: makeValue "" [] ob done li;
r unsafely_as ~Object
elif rawValue instanceof BSONObject then
bsonObj = rawValue unsafely_as ~BSONObject;
if contains fieldName mapFields then
//we have to create a map
map = [:];
it = bsonObj#keySet()#iterator();
(it#hasNext()) loop (
k = (it#next()) unsafely_as string;
rawV = bsonObj#get(k);
v = makeValue k mapFields rawV;
map.[k] := v;);
map unsafely_as ~Object;
else
(fromDBObject mapFields bsonObj) unsafely_as ~Object;
fi
else
rawValue unsafely_as ~Object
fi
);
keySet = dbobject#keySet();
it = keySet#iterator();
hMap = new HashMap();
it#hasNext() loop (
key = (it#next()) unsafely_as string;
rawValue = dbobject#get(key);
value = makeValue key mapFields rawValue;
hMap#put(key,value);
);
//this is realy bad
fa = array ( map do x: false done [0 .. hMap#size() - 1]);
new GenericStruct(hMap, fa);
);

1

Exception:

java.lang.RuntimeException: No className for Z
at yeti.lang.compiler.JavaType.className(JavaType.java:487)
at yeti.lang.compiler.JavaExpr.convert(JavaExpr.java:110)
at yeti.lang.compiler.JavaExpr.convertedArg(JavaExpr.java:271)
at yeti.lang.compiler.JavaExpr.genCall(JavaExpr.java:242)
at yeti.lang.compiler.NewExpr.gen(YetiCode.java:1192)
at yeti.lang.compiler.BindExpr.gen(YetiCode.java:1885)
at yeti.lang.compiler.SeqExpr.gen(YetiCode.java:1678)
at yeti.lang.compiler.BindExpr.gen(YetiCode.java:1885)
at yeti.lang.compiler.BindExpr.gen(YetiCode.java:1885)
at yeti.lang.compiler.BindExpr.gen(YetiCode.java:1885)
at yeti.lang.compiler.BindExpr.gen(YetiCode.java:1885)
at yeti.lang.compiler.JavaExpr.genRawArg(JavaExpr.java:290)
at yeti.lang.compiler.JavaExpr.convertedArg(JavaExpr.java:270)
at yeti.lang.compiler.Cast.gen(YetiCode.java:1395)
at yeti.lang.compiler.Function.prepareMethod(YetiClosure.java:
835)
at yeti.lang.compiler.Function.prepareGen(YetiClosure.java:
853)
at yeti.lang.compiler.Function.prepareConst(YetiClosure.java:
1043)
at yeti.lang.compiler.BindExpr.genBind(YetiCode.java:1857)
at yeti.lang.compiler.BindExpr.gen(YetiCode.java:1884)
at yeti.lang.compiler.BindExpr.gen(YetiCode.java:1885)
at yeti.lang.compiler.RootClosure.gen(YetiClosure.java:1091)
at yeti.lang.compiler.CompileCtx.compile(YetiCode.java:385)
at yeti.lang.compiler.CompileCtx.compile(YetiCode.java:321)
at yeti.lang.compiler.CompileCtx.compileAll(YetiCode.java:266)
at yeti.lang.compiler.YetiTask.execute(YetiTask.java:105)

Madis

unread,
Jun 9, 2010, 4:20:12 PM6/9/10
to yeti-lang

On Tue, 8 Jun 2010, chrisichris wrote:

> Hi,
>
> I try to make a smale helper api in Yeti for mongo-db (http://
> www.mongodb.org/). What I tried is to load a data item
> (com.mongodb.DBObject) and convert it (untyped) into a
> yeti.lang.GenericStruct and than cast this struct to the right type.
> Ie:
>
>> dbo is ~com.mongodb.DBObject = //somehow query the database
>> conv is ~yeti.lang.GenericStruct = fromDBObject dbo; //helper method to convert the mongo DBObject to a ~Struct
>> customer = conv unsafely_as {name is string, age is number ...} //casting the struct to what is expected.
>
> I have two questions:
> 1.) Generally do you think this is a viable approach (making a
> GenericStruct and than requiring the user to cast it)

Well... possible, but it isn't the prettiest one. ;)

One thing - fromDBObject could return 'a (unsafely_as 'a),
at least the code would be nicer.

Alternative would be creating a wrapper that don't pretend knowing the
underlying type statically, similarly to peekObject. It would be
more verbose, but I think also more honest about it's nature.

customer = fromDBObject dbo;
name = customer.str 'name';
age = customer.num 'age';

Possibly unrelated - I have really old unimplemented idea about inbuilt
operator `struct`, that would create yeti structure wrapper for java-bean
like object. Like:

class X {
String getFoo();
}
---
x = new X();
y = struct x;
println y.foo;

> 2.) The compiler throws a "java.lang.RuntimeException: No className
> for Z" when compiling my "fromDBObject" function:

...


> Code: (using the mongo-db driver 2.0 rc 4 and the latest yeti from
> git)

...


> );
> //this is realy bad
> fa = array ( map do x: false done [0 .. hMap#size() - 1]);
> new GenericStruct(hMap, fa);

Seems that the stupid thing doesn't know how to convert array<boolean>
into ~boolean[] (Z is JVM notation for boolean).

I'll look into it, but few workarounds:

fa = new boolean[hMap#size()];
new GenericStruct(hMap, fa);

or...

new GenericStruct(hMap, ());

Also, \false is shorthand for do x: false done.

chrisichris

unread,
Jun 10, 2010, 12:56:45 AM6/10/10
to yeti-lang

Hi,

Thanks once more for your very fast and helpful response.

On 9 Jun., 22:20, Madis <ma...@cyber.ee> wrote:
> On Tue, 8 Jun 2010, chrisichris wrote:
> > Hi,
>
> > I try to make a smale helper api in Yeti for mongo-db (http://
> >www.mongodb.org/). What I tried is to load a data item
> > (com.mongodb.DBObject) and convert it (untyped) into a
> > yeti.lang.GenericStruct and than cast this struct to the right type.
> > Ie:
>
> >> dbo is ~com.mongodb.DBObject = //somehow query the database
> >> conv is ~yeti.lang.GenericStruct = fromDBObject dbo; //helper method to convert the mongo DBObject to a ~Struct
> >> customer = conv unsafely_as {name is string, age is number ...} //casting the struct to what is expected.
>
> > I have two questions:
> > 1.) Generally do you think this is a viable approach (making a
> > GenericStruct and than requiring the user to cast it)
>
> Well... possible, but it isn't the prettiest one. ;)
>
> One thing - fromDBObject could return 'a (unsafely_as 'a),
> at least the code would be nicer.
>
> Alternative would be creating a wrapper that don't pretend knowing the
> underlying type statically, similarly to peekObject. It would be
> more verbose, but I think also more honest about it's nature.
>
> customer = fromDBObject dbo;
> name = customer.str 'name';
> age = customer.num 'age';
>

Yes I also tend to think that the explicit converting is better, no
magic and there is more flexibility if the code and data diverge
through data-versioning etc. I am currenlty plying with both
variations.

For performance reasons I don't know wheter I should use a wrapper or
straight functions, because the wrapper gets quite big. It has 20
functions: 5 functions for the base values (str, num etc) and three
times five functions for list, maybe and map (ie listStr, listNum,
maybeStr, maybeNum, mapStr etc). So I currenlty use plain functins ie:
bsonStr name dbObject.

Unrelated:
When serializing a number I can either serialize it as a long or
double. How do I see wheter a yeti number is an integer or a floating-
point (or a big-decimal or primitive wrapper)?

> Possibly unrelated - I have really old unimplemented idea about inbuilt
> operator `struct`, that would create yeti structure wrapper for java-bean
> like object. Like:
>
> class X {
>         String getFoo();}
>
> ---
> x = new X();
> y = struct x;
> println y.foo;
>
>

I think this is very useful if you work a lot with JavaBeans as of
course most Java coders do (- however I furtunately currently not ;).
Maybe this could be extended to also include (polymophic) methods.
Currently I wrap a java-map implemenation with a struct just to get to
the generic typed methods if this would be more direct that would be
nice:

JavaCode
class FooMap {
public void put(String key, Object value)..
public Object get(String key) ..
}

yeti
map = struct (new FooMap()) {put is string -> 'a -> (), get is string -
> 'a}



>
> > 2.) The compiler throws a "java.lang.RuntimeException: No className
> > for Z" when compiling my "fromDBObject" function:
> ...
> > Code: (using the mongo-db driver 2.0 rc 4 and the latest yeti from
> > git)
> ...
> >    );
> >    //this is realy bad
> >    fa = array ( map do x: false done [0 .. hMap#size() - 1]);
> >    new GenericStruct(hMap, fa);
>
> Seems that the stupid thing doesn't know how to convert array<boolean>
> into ~boolean[] (Z is JVM notation for boolean).
>
> I'll look into it, but few workarounds:
>
> fa = new boolean[hMap#size()];
> new GenericStruct(hMap, fa);
>
> or...
>
> new GenericStruct(hMap, ());
>
> Also, \false is shorthand for do x: false done.

Thanks for the tips and the workaround (which works).
I did not know that you can make arrays like that and always like
shorting the code. \false is elegant. Unfortunatley I often oversse
such nice build-ins.

Thanks,
Christian

Madis

unread,
Jun 10, 2010, 9:13:38 AM6/10/10
to yeti-lang

On Wed, 9 Jun 2010, chrisichris wrote:

> For performance reasons I don't know wheter I should use a wrapper or
> straight functions, because the wrapper gets quite big. It has 20
> functions: 5 functions for the base values (str, num etc) and three
> times five functions for list, maybe and map (ie listStr, listNum,
> maybeStr, maybeNum, mapStr etc). So I currenlty use plain functins ie:
> bsonStr name dbObject.

You have kind of middle ground - use accessors that create the function
value only when asked.

$ yeti -e 'obj y = { get f () x = x + y }; (obj 1).f 2'
3

The compiler always generates a specialized class for structures
containing accessors - and then tries to inline the accessor functions
into it. The y captured by accessor closure will be stored in the
structure, but it will be shared by different accessors.

The function closure construction gets lifted from structure instance
creation time to the time when field value is asked (and it will be
recreated again each time the field is accessed). From performance view
point it's a tradeoff - some code will be faster this way, and some
slower.

chrisichris

unread,
Jun 11, 2010, 1:14:49 AM6/11/10
to yeti-lang
Hi,

Ah. That's exactly what I was looking for! Did not know that accessors
can take arguments - I think they are also not mentioned in the
(otherwise excellent) documentation.

So a lazy accessor would be:
obj y = ( l = lazy \do a: a + y done; {get f() x = (l()) x });

Thats very nice.

Thanks
Reply all
Reply to author
Forward
0 new messages