Other uses for the Multiplication Operator (*)

124 views
Skip to first unread message

extenditus

unread,
Jan 25, 2012, 10:21:52 AM1/25/12
to gosu-lang
Are there any other special uses for the multiplication operator in
gosu?

I see code like this

for( building in Locations*.Buildings ){
// do something with building
}


Where Location is an array(Collection) and Buildings is an
array(Collection). And a Location has many Buildings.

Carson Gross

unread,
Jan 25, 2012, 10:48:38 AM1/25/12
to gosu...@googlegroups.com
The '*.' operator is a map/flatmap in gosu: the result is always a single dimension array or list.  This makes it convenient to iterate over multiply nested arrays or collections without nesting loops.

That make sense?

You may also see '?.', which is a null safe invocation, so 

  var x : String = null
  print( x?.length() )

prints null, rather than causing an NPE.

Cheers,
Carson


--
You received this message because you are subscribed to the Google Groups "gosu-lang" group.
To post to this group, send email to gosu...@googlegroups.com.
To unsubscribe from this group, send email to gosu-lang+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/gosu-lang?hl=en.


Alan Keefer

unread,
Jan 25, 2012, 11:05:26 AM1/25/12
to gosu...@googlegroups.com
It's technical name is the "array expansion operator". The official
language docs for that feature are at:

http://gosu-lang.org/doc/wwhelp/wwhimpl/js/html/wwhelp.htm#context=gosu&src=collections&topic=List_and_Array_Expansion_(*.)

-Alan

extenditus

unread,
Jan 25, 2012, 11:13:07 AM1/25/12
to gosu-lang
Okay! Thanks for the explanation. It does make sense.

If we are on the same page, from my example above, instead of

for( location in Locations) {

for( building in location.Buildings){
// do something with buildings
}
}

we do this

for( building in Locations*.Buildings ){
// do something with building

}

If this is correct, what is really happening under the scene? I
understand that gosu is a statically typed language that supports
type inference.
Is the array or collection converted to a generic type of Object? e.g
SomeCollectionType<? extends Object>().

I hope my bonus question isn't too much Carson. I am a little bit
curious.

Thanks for your quick response and help.

extenditus

unread,
Jan 25, 2012, 11:34:16 AM1/25/12
to gosu-lang
Thanks Alan. I will take a look at the docs.

On Jan 25, 10:05 am, Alan Keefer <akee...@gmail.com> wrote:
> It's technical name is the "array expansion operator".  The official
> language docs for that feature are at:
>
> http://gosu-lang.org/doc/wwhelp/wwhimpl/js/html/wwhelp.htm#context=go....)
>
> -Alan
>
>
>
>
>
>
>
> On Wed, Jan 25, 2012 at 7:48 AM, Carson Gross <carsongr...@gmail.com> wrote:
> > The '*.' operator is a map/flatmap in gosu: the result is always a single
> > dimension array or list.  This makes it convenient to iterate over multiply
> > nested arrays or collections without nesting loops.
>
> > That make sense?
>
> > You may also see '?.', which is a null safe invocation, so
>
> >   var x : String = null
> >   print( x?.length() )
>
> > prints null, rather than causing an NPE.
>
> > Cheers,
> > Carson
>

Alan Keefer

unread,
Jan 25, 2012, 11:44:05 AM1/25/12
to gosu...@googlegroups.com
There's some slightly-crazy stuff happening under the covers in the
compiler, depending on if the right-hand side is a scalar or an array.
If the right-hand side is a scalar, i.e. locations*.Name, then it'll
act as a standard map() operation. The compiled code will look
more-or-less like if you called locations.map(\l -> l.Name). If you
call locations*.Buildings, it's like calling locations.flatMap(\l ->
l.Buildings). You can also technically use a method call on the
right-hand size instead of a property, and if that method has a void
return type, say locations*.doSomething(), it's like calling
locations.each(\l -> l.doSomething()).

The implementation in bytecode is a wee bit complicated, due to the
number of different possibilities and some differences with regards to
short-circuiting depending on if the right-hand side is a primitive
value or not. We also take a different path depending on if we can
allocate the result array up front, which we can do if the left-hand
size is an array or Collection and the right-hand side is a scalar (in
which case we can cheaply know up front how big the result array
should be) or if we have to compile the results into a temporary List
which then gets turned into an array, which we do if the left-hand
side is some other Iterable or if the right-hand side is an array or
Collection, such that we don't know up-front how large to make the
result array. So there are a dozen or so different paths the compiler
can ultimately take depending on all those factors. But the end
result is effectively like calling .map() or .flatMap() or .each(),
depending on the right-hand-side of the expression.

Personally, though, I'm not a huge fan of the operator, and prefer the
explicitness of .map() and .flatMap() instead. The *. operator
predates the inclusion of blocks in Gosu, and I'm not sure we'd
necessarily add it in these days if we didn't already have it, since
we already have blocks for doing those sorts of mappings.

-Alan

extenditus

unread,
Jan 25, 2012, 12:45:35 PM1/25/12
to gosu-lang
Hello Alan - That was a really good explanation. It was really
helpful. As far as performance goes I would guess from your
explanation that it is best to actually use the closure functions
instead of the list and array expansion feature since the compiler
would have to go through all those permutation trying to figure out
what the right hand side is.

Thanks for the really good explanation.

Alan Keefer

unread,
Jan 25, 2012, 12:50:41 PM1/25/12
to gosu...@googlegroups.com
Well, for runtime performance, the *. operator is likely faster, since
it doesn't have to create the anonymous inner class that represents
the closure, and the bytecode to do the map/flatmap operation is
directly inline in the method instead of hiding behind a method call.
The compiler step is such a trivial amount of time that it's pretty
insignificant, and compiling a closure is probably slower anyway since
it ends up as a separate top-level class. But it's really such a
trivial difference either way that I wouldn't worry about it: I'd
recommend going with whatever approach you feel makes your code the
most readable.

-Alan

Reply all
Reply to author
Forward
0 new messages