ANN: hxtern and jsdoc3-hxtern

85 views
Skip to first unread message

Justin Donaldson

unread,
Feb 2, 2014, 4:44:03 PM2/2/14
to Haxe
I started a small repo of js externs here:

Rather than manually writing externs, I'm trying to generate them directly from jsdoc-annotated files.  Furthermore, I'm trying to use the closure compiler-specific additions to the jsdoc syntax, since they add additional type information that is useful in several situations (e.g. callback argument types).  Furthermore, Google has a small repo of these externs that they track for their closure compiler project.  You can see some of them here:
So, for now, I'm only focusing on what they provide as part of their main closure compiler repo.  I'll add more in later on.

To help generate the externs, I'm writing a small template plugin for jsdoc.  Naturally, the plugin is in Haxe, using the js target:  

However, rather than generating html pages, the template writes haxe externs.  Right now, the plugin is the only way I'm planning on updating the externs in hxtern.  So, every time the jsdoc3-hxtern plugin changes, hxtern will update.   I'll add in some testing, etc. that helps me track changes and problems in the plugin.  

The plugin seems to work pretty well, but there's definitely a "good, bad, and ugly" side to this.

The Good:

I've created several enums and typed information for a jsdoc "Doclet", and parsing information from the closure compiler annotations.  The information extracted from the javascript itself is pretty well structured.

The Bad:

Converting certain js types and entities to Haxe can become convoluted.  I talk about some of it in the readme.  The biggest issue (for me) is the problem of "union types" in arguments:

Javascript typically allows for dynamic arguments (e.g. strings, ints, arrays etc).  Haxe can handle this somewhat by using @:overload.  However, if there are multiple dynamic arguments, the number of overload definitions increases exponentially, since you have to specify each possible permutation of arguments.  I was thinking this would be a case for union types.  What's the best way of managing union types in Haxe externs?  I see references to this idea, and even a deprecated library, but nothing recent.
Right now, I'm only using the first type from a javascript union type.

The Ugly:

No tests, and barely readable code in Publish.hx.  I went back and forth between trying to structure everything in terms of enums and typedefs, and then trying to do something quick and dirty with annotating dynamic objects.  It's somewhere in the middle right now.

Best,
-Justin



Andy Li

unread,
Feb 2, 2014, 5:02:18 PM2/2/14
to haxe...@googlegroups.com
Sounds great!

For union types, jQueryExtern uses an abstract, Either. For example if there is a function of (T1|T2|T3)->Void, it can be typed as Either<T1, Either<T2, T3>> -> Void.

Best,
Andy


--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/groups/opt_out.

Justin Donaldson

unread,
Feb 2, 2014, 5:13:28 PM2/2/14
to Haxe
Great, I had thought abstracts might be a good way to go here.  Is there a way to do this without nesting the Either? Or, would I have to add additional abstract definitions?

Thanks,
-Justin

Andy Li

unread,
Feb 2, 2014, 5:49:16 PM2/2/14
to haxe...@googlegroups.com
I cannot think of any easy way to avoid nesting beside creating Either3<T1,T2,T3>, Either4<T1,T2,T3,T4>  etc.
We could use build macro on the extern class to convert Either<[T1, T2, T3]> to Either<T1, Either<T2, T3>>, but I'm not sure if it worths the effort.

Best,
Andy

Juraj Kirchheim

unread,
Feb 3, 2014, 4:16:32 AM2/3/14
to haxe...@googlegroups.com
Congrats, it looks like a good first step!

I'm a little confused by the fact that the externs contain Map, such
as here: https://github.com/jdonaldson/hxtern/blob/master/gen_src/google_loader_api/Google.hx#L7

Most of the time, this is actually something that should be
representable as an anonymous object with optional fields. And when
it's truly a map, it's actually going to be a JS object, so you will
need something like this:
https://gist.github.com/back2dos/5072589#file-jsonmap-hx

As for dealing with JS signatures in general, it might be best to
offer different strategies in the converter. Particularly the Google
APIs are quite influenced by their Java efforts, so overloading might
be the best thing. For other, more js-idiomatic libs, Andy's idea
should get you relatively far, together with Haxe's optional
arguments.

Best,
Juraj

Justin Donaldson

unread,
Feb 3, 2014, 9:56:00 AM2/3/14
to Haxe
On Mon, Feb 3, 2014 at 1:16 AM, Juraj Kirchheim <back...@gmail.com> wrote:
Congrats, it looks like a good first step!

I'm a little confused by the fact that the externs contain Map, such
as here: https://github.com/jdonaldson/hxtern/blob/master/gen_src/google_loader_api/Google.hx#L7

Yeah, map's not the right type there. 

Jsdoc has an "Object.<T1, T2>" type that's used all over the place, but T1 is always a string.  So the translation is probably best as Dynamic<T2>.
 

As for dealing with JS signatures in general, it might be best to
offer different strategies in the converter. Particularly the Google
APIs are quite influenced by their Java efforts, so overloading might
be the best thing. For other, more js-idiomatic libs, Andy's idea
should get you relatively far, together with Haxe's optional
arguments.

I'll keep the general idea in mind, although in the specific situation here I haven't seen any cases where providing a series of overloaded signatures is "better" than union types.  Overloads also have other minor drawbacks in Haxe... they can clutter up completions, and they can add a lot of bloat to method signatures.


Best,
-Justin
 

Saar Korren

unread,
Feb 3, 2014, 1:06:30 PM2/3/14
to haxe...@googlegroups.com
I find that in most cases, HaXe's optional and default arguments do the trick. The real issue arises from variadic functions with unlimited parameter counts. HaXe doesn't have some metadata magic for supporting that in externs. The only thing possible is to have a bunch of overloads going up to as many parameters as you think people would reasonably need.

Justin Donaldson

unread,
Feb 3, 2014, 1:44:52 PM2/3/14
to Haxe
On Mon, Feb 3, 2014 at 10:06 AM, Saar Korren <slugf...@gmail.com> wrote:
I find that in most cases, HaXe's optional and default arguments do the trick. The real issue arises from variadic functions with unlimited parameter counts. HaXe doesn't have some metadata magic for supporting that in externs. The only thing possible is to have a bunch of overloads going up to as many parameters as you think people would reasonably need.


Yes, that was another problem I was running into as well. 


Justin Donaldson

unread,
Feb 8, 2014, 4:20:17 PM2/8/14
to Haxe
I just pushed an update to the jsdoc plugin.  It generates 6 optional arguments when it comes across a variadic function.
I'm adding all of these hacks to a list on the readme:

I know that providing a high quality extern repo has been a standing request for some time.  It might be worth trying to add some more "extern markup" for methods so that the Haxe compiler can better understand common js patterns.

According to my current list on the jsdoc3-hxtern readme, I think the compiler could handle union types and variadic arguments for extern methods.  It would make the extern much easier to understand and use.

Best,
-Justin


Best,
-Justin
Reply all
Reply to author
Forward
0 new messages