Some pretty way to properly bring back a ~Tag from Java into Yeti as tagged type?

18 views
Skip to first unread message

czap...@gmail.com

unread,
Sep 12, 2014, 8:12:17 AM9/12/14
to yeti...@googlegroups.com
Is there a way to "properly" bring back a yeti.lang.Tag from the Java world to Yeti world & typesystem?

Doing "x unsafely_as UnknownTag ()" seems to "work" in practice, but also seems to be a gross hack IIUC, because until resolved, the typesystem seems to be schisophrenic about the actual type - i.e. thinking it is "UnknownTag ()", but then properly matching and retrieving the "Foo 10" when confronted:

> import yeti.lang.Tag;
> oo = Foo 10 as ~Tag;
oo is ~yeti.lang.Tag = Foo 10
> peekObject oo;
Variant {tag="Foo", value=Number 10} is ('a is Boolean boolean
| Hash hash<'a, 'a>
| List list<'a>
| Number number
| Object ~java.lang.Object
| String string
| Struct {fields is list<string>, value is string -> 'a}
| Variant {tag is string, value is 'a})
> case (oo unsafely_as UnknownTag ()) of UnknownTag (): -1; Foo n: n; _: -2 esac
10 is number
> oo unsafely_as UnknownTag ();
Foo 10 is UnknownTag ()
> Foo 10;  // for comparison with above
Foo 10 is Foo number
> f _ is () -> UnknownTag () = oo unsafely_as UnknownTag ();
f is () -> UnknownTag () = <code$f>
> case f () of UnknownTag (): -1; Foo n: n; _: -2 esac
10 is number
> peekObject (f ())
Variant {tag="Foo", value=Number 10} is ('a is Boolean boolean
| Hash hash<'a, 'a>
| List list<'a>
| Number number
| Object ~java.lang.Object
| String string
| Struct {fields is list<string>, value is string -> 'a}
| Variant {tag is string, value is 'a})

My core problem is that I need to somehow store a tagged type in some Java class/object/container (because Android), and then would like to retrieve it back as a tagged type into Yeti code in some totally different place (from just the Java class/object/container). If there's some smarter way to do this, I'd be very grateful to learn about it!

Thanks,
/Mateusz.

Madis

unread,
Sep 12, 2014, 9:03:32 AM9/12/14
to yeti...@googlegroups.com

On Fri, 12 Sep 2014, czap...@gmail.com wrote:

> Is there a way to "properly" bring back a yeti.lang.Tag from the Java world
> to Yeti world & typesystem?
>
> Doing "x unsafely_as UnknownTag ()" seems to "work" in practice, but also
> seems to be a gross hack IIUC, because until resolved, the typesystem seems
> to be schisophrenic about the actual type - i.e. thinking it is "UnknownTag
> ()", but then properly matching and retrieving the "Foo 10" when confronted:
>
> ....
>
> My core problem is that I need to somehow store a tagged type in some Java
> class/object/container (because Android), and then would like to retrieve it
> back as a tagged type into Yeti code in some totally different place (from
> just the Java class/object/container). If there's some smarter way to do
> this, I'd be very grateful to learn about it!

I don't know what the API in question is, but general advice would be the
following:

Write in Yeti a Java class wrapping the variant type, or even the whole
Java container needed for Android (of course, if it's not a class from Android
API).

Basically the idea behind Yeti-Java interfacing is that you should
practically never touch the primitive yeti.lang.* types from Java code.
Instead of that Yeti should be used to write classes that provide the
minimal interface needed by the Java code. This way the intrinsics of Yeti
types will be handled by the Yeti compiler (using them correctly by hand
can be quite tricky sometimes).

czap...@gmail.com

unread,
Sep 12, 2014, 10:07:38 AM9/12/14
to yeti...@googlegroups.com, ma...@cyber.ee
Indeed I tried to do this "Yeti-side only", but unfortunately I'm a beginner in this and couldn't wrap my head around how to do that this way here.

So, to be more specific: I want to write custom Android layout; i.e., I'm writing a Yeti class "MyLayout" extending android.view.ViewGroup [1].

Now, from what I understand, in Android, layout classes are usually accompanied by corresponding "LayoutParams" -- for that I'm building a class MyLayoutParams, which has to be extending ViewGroup$LayoutParams. Those can be then passed into MyLayout's ViewGroup#addChild(view,layoutParams) [2], as a means to store per-child constraints for guiding the particular layout. Then, when resolving the layout geometry dynamically, in MyLayout#onMeasure(), for each child (added via addView()) I can call: this#getChildAt(i)#getLayoutParams() [3][4], which gives me a ViewGroup$LayoutParams reference, which is in fact instanceof MyLayoutParams...

  [1]: http://developer.android.com/reference/android/view/ViewGroup.html
  [2]: http://developer.android.com/reference/android/view/ViewGroup.html#addView%28android.view.View,%20android.view.ViewGroup.LayoutParams%29
  [3]: http://developer.android.com/reference/android/view/ViewGroup.html#getChildAt%28int%29
  [4]: http://developer.android.com/reference/android/view/View.html#getLayoutParams%28%29

So, back to Yeti and tagged types: in MyLayoutParams (which must extend android.view.ViewGroup$LayoutParams), I want to store some tagged value (let's say, "Big () | Small () | Fixed number"). Then, later, I want to be able to nicely retrieve the tagged value when inside MyLayout#onMeasure(), and the input I have for this is the MyLayoutParams object I was able to get from: this#getChildAt(i)#getLayoutParams().

And this is the point, where I don't know how to do this Yeti-friendly?

After what you said, I'm starting to think whether I could get rid of the whole addView(...,...) and getChildAt(...) channel, but I'm afraid that Android could frown upon me (or otherwise explode when I least expect it) in such situation. I'd like to try being a Good Citizen as much as I can...

On the other hand, in the meantime I seem to have kinda resolved my original casting problem:
> {tto, tfrom} = (typedef tagged = A number | B number; tto x is tagged -> ~Object = x as ~Object; tfrom o is ~Object -> tagged = o unsafely_as tagged; {tto, tfrom})
tto is A. number | B. number -> ~java.lang.Object = <code$tto>
tfrom is ~java.lang.Object -> A number | B number = <code$tfrom>

with the caveat, that although seems much prettier than original "UnknownTag ()" approach, I'd surely much prefer some solution "fully in Yeti" and not having to force stuff unsafely through ~Object. Only I still can't invent it, unfortunately.

(As a side note, for now I want to build the final Anrdoid layout "programmatically", i.e. instantiating classes by hand in Yeti, instead of via XML resource files. I prefer Yeti to both Java and XML here.)

Not sure if any of this makes sense to you, sorry, I'm just starting to learn Android, so I'm not very knowledgeable in it, and it still feels quite complicated to me.

Thanks,
/M.

Madis

unread,
Sep 12, 2014, 1:01:32 PM9/12/14
to yeti...@googlegroups.com, ma...@cyber.ee

On Fri, 12 Sep 2014, czap...@gmail.com wrote:

typedef param = Big () | Small () | Fixed number

class MyLayoutParams extends ViewGroup$LayoutParams
abstract void doSomething(Whatever what),
abstract boolean isBig()
end;

createLayoutParams param is param -> ~MyLayoutParams =
(class Params extends MyLayoutParams
boolean isBig()
param == Big (),

void doSomething(Whatever what)
// do something using the param and what
end;
new Params());

Now all you have to do is casting the LayoutParams into MyLayoutParams and
invoke the doSomething to use the param in the given closure. While it
doesn't save totally from the casts (this Android API seems to expect some
kind of cast for the LayoutParams object, when custom fields are needed),
it allows the Yeti compiler to typecheck the Yeti types like param.
For example:

myParam = view#getLayoutParams() unsafely_as ~MyLayoutParams();
if myParam#isBig() then
...
else
myParam#doSomething(what)
fi;

The only trick here is that while you cannot use Yeti types in class
method/constructor argument and return types, it is still perfectly ok to
use closure variables that have Yeti types in the class methods.

I don't how your code exactly uses those params, but in some cases it
might even mean that you can completely bypass the expected param passing
mechanism by simply defining View subclass inside closure that has the
required parameters as local bindings (thereby visible to all methods
in that class).

czap...@gmail.com

unread,
Sep 12, 2014, 3:24:35 PM9/12/14
to yeti...@googlegroups.com, ma...@cyber.ee
Ok, I think I get the idea; that seems similar to something I thought about initially, only your's is prettier. Also, I now better understand how this is type-safe. (As to defining classes inside closures, I saw that already in the tutorial or in the sources; quite a few lovely patterns there, so I'm totally reading them from time to time for learning; also didn't know I can use the 'abstract' keyword in Yeti class declarations!)

But then, IIUC, I have two issues with this approach, both quite major for me:
1. I'm effectively losing the compile-time check for exhaustion of enum cases for a tagged type (i.e. I won't get the "member missing" error in the "if myParam#isBig()..." block);
2. As I see it, I'd have much, much more writing to do now, and I actually must remember to do "triple bookkeeping": whenever I'd like to add an additional tag to the "typedef param", I must remember to add it also in "class Params", and thirdly, to add a branch to the "if myParam#isBig() then..." block.

I notice how this approach has advantage of being fully classic-Java-compatible. As such, I'll now try to keep it in mind as a very important pattern for situations where I'd need to write a library exposing a totally regular Java interface on surface, while underneath stealthily having fun with Yeti. But as long as I don't have to hide from outside, only from inside (for Android), I think I prefer the approach of casting "typedef param" -> ~Tag -> "typedef param"; somewhat unsafe and risky as it is, seems still simpler conceptually and simpler to execute for me than the one above, and also seems not to take from me the very useful "member missing" check...

Thanks,
/Mateusz.

Madis

unread,
Sep 12, 2014, 4:17:51 PM9/12/14
to yeti...@googlegroups.com

On Fri, 12 Sep 2014, czap...@gmail.com wrote:

> Ok, I think I get the idea; that seems similar to something I thought about
> initially, only your's is prettier. Also, I now better understand how this
> is type-safe. (As to defining classes inside closures, I saw that already in
> the tutorial or in the sources; quite a few lovely patterns there, so I'm
> totally reading them from time to time for learning; also didn't know I can
> use the 'abstract' keyword in Yeti class declarations!)
>
> But then, IIUC, I have two issues with this approach, both quite major for
> me:
> 1. I'm effectively losing the compile-time check for exhaustion of enum
> cases for a tagged type (i.e. I won't get the "member missing" error in the
> "if myParam#isBig()..." block);
> 2. As I see it, I'd have much, much more writing to do now, and I actually
> must remember to do "triple bookkeeping": whenever I'd like to add an
> additional tag to the "typedef param", I must remember to add it also in
> "class Params", and thirdly, to add a branch to the "if myParam#isBig()
> then..." block.

The example kind-of-contained two different examples - the isBig method
was providing direct access to the value.

The doSomething method on the other hand was an example of a bit
different pattern - moving the actual logic from View methods into the
implementation of the MyLayoutParams class.

In this case you can do the pattern match (inside doSomething), and
avoid duplicating the param type in the class. It might convulate the
code in other ways, though, so which is better depends on the actual code
you're writing.

There isn't one true way to solve it, and there might be other even better
approaches.

chrisichris

unread,
Sep 13, 2014, 1:31:50 AM9/13/14
to yeti...@googlegroups.com

My core problem is that I need to somehow store a tagged type in some Java class/object/container (because Android), and then would like to retrieve it back as a tagged type into Yeti code in some totally different place (from just the Java class/object/container).


Maybe you store the yeti Tag outside of the Java Object in a WeakHashMap where the AndroidJavaObject is the key and the Yeti-Tag the value:

----
typedef tagParam = Big() | Smale () | Fixed number;

store = customHash \(new java.util.WeakHashMap());

newLayoutParam tagParam is tagParam -> 'a = 
    (lpa = new YourLayoutParam(...);
    store[lpa] := tagParam;
    lpa);

lp = newLayoutParam (Big ());

case store[lp] of
Big (): println "Big ()";
Smale(): println "Smale()";
Fixed n: println "Fixed \(n)";
esac;

----

czap...@gmail.com

unread,
Sep 15, 2014, 7:39:01 AM9/15/14
to yeti...@googlegroups.com

Hm! Very, very interesting, thanks! The disadvantage here is that it in effect adds new fields to a class only externally, thus impacting cognitive locality, but on the other hand this avoids the ugly casting hack and totally keeps everything in the typesystem all the time.
Now this one leaves me torn; fortunately I've already coded my earlier solution, so I can go on with real work and not be stopped by decision paralysis, but next time I'll need to do this I'm gonna have hard time ;)

Anyway, very, very interesting thread, thanks a lot Madis and chrisichris for all your hints, it feels so good to learn stuff that widens perspective.

/M.
Reply all
Reply to author
Forward
0 new messages