Dynamic { def applyIfNotDefined(...) }

94 views
Skip to first unread message

Paul Phillips

unread,
Feb 3, 2011, 2:41:17 AM2/3/11
to Odersky Martin, scala-i...@googlegroups.com
Some notes on Dynamic.

1) I agree with everyone else, please don't call it invokeDynamic. I've
been hearing about invokeDynamic daily for over a year or two now and
this is not what I've been hearing about. I think I would prefer "def
paulpIsStupid" to def invokeDynamic. (Actually I know I would, that
name is awesome.)

2) Taking only Any args buries a vital distinction. There is no way to
tell whether primitives or boxed types were given as arguments. In real
life there are a couple ways to work around this, but in its present
form Dynamic takes all those ways completely out of the game. It is a
serious issue because they are different types to the jvm and reflection
does not offer any wiggle room. It really complicates matters quite a
lot if every time you're given an argument you have to splatter 2^n
pieces of "I wonder if this was boxed" logic around if you don't like
failure.

...which leads to...

3) I we can do far better than some Any-goo. First let's agree that
dynamic languages are slow and nobody will notice a couple light
abstractions. ("It is so agreed.") We can solve 2) and throw in
language-agnostic named arguments and other frostings if, rather than
taking Any*s, we define the method around some simple new Arg container
class. Populating it via implicits lets us hang onto the knowledge of
what is primitive.

abstract class Arg {
type ArgType
def isPrimitive: Boolean
val value: ArgType
def name: String = ""
}
abstract class Primitive extends Arg {
def isPrimitive = true
}
abstract class Reference extends Arg {
def isPrimitive = false
}

// Any isn't so bad as a return type because of covariant override
def invokeSymbolic(name: String)(args: Arg[_]*): Any

implicit def anyval2arg[T <: AnyVal](x: T) = new Primitive { val value = x }
implicit def anyref2arg[T <: AnyRef](x: T) = new Reference { val value = x }

--

This is a sample implementation only. However I know this approach to
reflection works, since I've implemented it at least a couple times. Oh
yeah, I should have started with that. Here, this:

https://github.com/paulp/scala-improving/raw/master/src/main/scala/reflect/AnyExt.scala

Oh, I have a whole prefab comment on this subject. I really need to
stop doing the same work repeatedly.

/** In order to encapsulate anything to do with reflection, we must
* overcome an issue with the boxing of primitives. If we declare a
* method which takes arguments of type Any, by the time the
* method parameters can be examined, the primitives have already
been boxed.
* The reflective call will then fail because classOf[java.lang.Integer]
* is not the same thing as classOf[scala.Int].
*
* Any useful workaround will require examining the arguments before
* the method is called. The approach here is to define different
implicits
* for AnyVal and AnyRef which preserves their original identity as it
* transforms them. We are left only with "Any" as a problem.
*/

4) I don't quite understand the thinking behind the typed method.

/** Returns the underlying value typed as an instance of type T
* @param T The target type
*/
def typed[T]: T

What implementation is this going to have besides the obvious one?
There's not even a manifest to get tricky with. Is this only to offer
more aesthetic casts? If we're coming around to offering more aesthetic
casts then I will be bummed if they are exclusively for this zone.
Those of us with both feet firmly in static type land still have to do
enormous amounts of casting. Between Any and AnyRef for starters. But
not for enders.

But beyond that, the method seems to assume a lot about what I'm doing
with the Dynamic trait. Of the first half dozen things I thought about
doing with it, none really involved an "underlying value" which I want
to offer up in this way. Especially, none involved a value I wanted to
return with a blind cast. I feel like I must be missing something major
on this one because I'm not seeing it.


Here is some code vaguely relevant to earlier points in case anyone has
reached this far distant shore.


class ReflectoWrapper[T <: AnyRef](target: T) extends Dynamic {
def invokeDynamic(name: String)(args: Any*): Any = {
val anyrefs = args map (_.asInstanceOf[AnyRef])
val clazzes = anyrefs map (_.getClass)
val method = target.getClass.getMethod(name, clazzes: _*)

method.invoke(target, anyrefs: _*)
}
def typed[U] : U = target.asInstanceOf[U]
}

class Tricky {
def f(x: Int): String = "primitive"
def f(x: java.lang.Integer): String = "boxed"
}

object Test {
implicit def anyoneToReflective[T <: AnyRef](target: T):
ReflectoWrapper[T] =
new ReflectoWrapper[T](target)

def main(args: Array[String]): Unit = {
val t: AnyRef = new Tricky

// Pretty relieved this doesn't work.
// println(t.f(5))

// These are ok.
println((t: Dynamic).f(5))
println(t.typed[Tricky].f(5))
}
}
// Output:
//
// boxed
// primitive

Kevin Wright

unread,
Feb 3, 2011, 4:40:11 AM2/3/11
to Paul Phillips, scala-i...@googlegroups.com
Oh, that's quite gorgeous!

Mind if I steal it for some of my own (scalasig-based) reflection work? (https://github.com/scalaj/scalaj-reflect)
Hey, it even compiles now, I can use it to look up parameter names...

If you're especially insistent, I might also find a way to work in `paulpIsStupid` :)
--
Kevin Wright

gtalk / msn : kev.lee...@gmail.com
mail: kevin....@scalatechnology.com
vibe / skype: kev.lee.wright
quora: http://www.quora.com/Kevin-Wright
twitter: @thecoda

READ CAREFULLY. By reading this email, you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED  agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies ("BOGUS AGREEMENTS") that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.

Paul Phillips

unread,
Feb 3, 2011, 4:56:22 AM2/3/11
to scala-i...@googlegroups.com
On 2/3/11 1:40 AM, Kevin Wright wrote:
> If you're especially insistent, I might also find a way to work in
> `paulpIsStupid` :)

I'm sure I won't have to insist; sometimes there's only one right name
for a function.

Kevin Wright

unread,
Feb 3, 2011, 4:58:21 AM2/3/11
to scala-i...@googlegroups.com
I *think* that was a yes... :)

Paul Phillips

unread,
Feb 3, 2011, 5:44:27 PM2/3/11
to Odersky Martin, scala-i...@googlegroups.com
On 2/2/11 11:41 PM, Paul Phillips wrote:
> Some notes on Dynamic.

A lucky message reminded me of these forgotten classes, which now that I
have unforgotten I think should not be ignored in this matter.

http://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/compiler/scala/tools/reflect/Mock.scala
http://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/compiler/scala/tools/reflect/Invoked.scala

That second one, Invoked, is a wrapper defining a method call. So the
overlap here is obvious. Mock and Invoked are accomplishing something
you can't do any other way (short of bytecode generation) with or
without Dynamic: implement an interface which is not known at compile
time. But in tandem with Dynamic the possibilities multiply.

Paul Phillips

unread,
Feb 4, 2011, 12:44:37 AM2/4/11
to Odersky Martin, scala-i...@googlegroups.com
On 2/3/11 2:44 PM, Paul Phillips wrote:
> On 2/2/11 11:41 PM, Paul Phillips wrote:
> A lucky message reminded me of these forgotten classes, which now that I
> have unforgotten I think should not be ignored in this matter.

Not having heard anything on my other Dynamic ideas I don't know if I'm
just digging my way to china on a boat that will never get there (and
I'm really not trying to complicate anything: to save us from
near-future complications, maybe, and practice my metaphor swirling) but
I had one more idea which I must air.

Here's java.util.Map.

trait Map[K, V] {
def size(): Int
def isEmpty(): Boolean
def containsKey(key: Object): Boolean
def containsValue(value: Object): Boolean
def get(key: Object): V
def put(key: K, value: V): V
def remove(key: Object): V
def putAll(m: Map[_ <: K, _ <: V]): Unit
def clear(): Unit
def keySet(): Set[K]
def values(): Collection[V]
def entrySet(): Set[Map.Entry[K, V]]
}

I write this:

class Mappy extends java.util.Map[String, Int] with Dynamic {
def applyDynamic(name: String)(args: Any*) = /* not the point here */
def typed[T] = sys.error("begone")
def size(): Int = 52
def get(key: Object): V = 5
/* too tired to continue */
}

Compiler says: non-abstract class, extends Dynamic, missing methods. I
hear and obey: supply all the missing implementations.

def isEmpty(): Boolean =
applyDynamic("isEmpty")().asInstanceOf[Boolean]
def containsKey(key: Object): Boolean =
applyDynamic("containsKey")(key).asInstanceOf[Boolean]
// etc

Fun times would ABOUND.

Note: this need not be Dynamic, it could be separated into
DynamicInterface or something.

Adriaan Moors

unread,
Feb 4, 2011, 3:17:28 AM2/4/11
to scala-i...@googlegroups.com, Odersky Martin
On Fri, Feb 4, 2011 at 6:44 AM, Paul Phillips <pa...@improving.org> wrote:
On 2/3/11 2:44 PM, Paul Phillips wrote:
On 2/2/11 11:41 PM, Paul Phillips wrote:
A lucky message reminded me of these forgotten classes, which now that I
have unforgotten I think should not be ignored in this matter.

Not having heard anything on my other Dynamic ideas I don't know if I'm just digging my way to china on a boat
<conveniently cutting in here> I like these ideas, and I'm sure I'm not alone -- it's just that we're all busy digging our way somewhere (me, it's Down Under on a canoe)

The other problem, for me, is that I don't have a good mental model to reason about the intricacies of real world dynamism. As you pointed out in your previous post, boxing and static overloading don't play nice with Dynamic without some extra work. Also, how do we reason about programmer surprise? The academic writings about this topic have mostly focussed on defending static type systems (blame the dynamic guys!), but not so much on how to make them vibe harmoniously, man.
 
that will never get there (and I'm really not trying to complicate anything: to save us from near-future complications, maybe, and practice my metaphor swirling) but I had one more idea which I must air.

Here's java.util.Map.

trait Map[K, V] {
 def size(): Int
 def isEmpty(): Boolean
 def containsKey(key: Object): Boolean
...
 
I write this:

class Mappy extends java.util.Map[String, Int] with Dynamic {
 def applyDynamic(name: String)(args: Any*) = /* not the point here */
 def typed[T] = sys.error("begone")
 def size(): Int = 52
 def get(key: Object): V = 5
 /* too tired to continue */
}

Compiler says: non-abstract class, extends Dynamic, missing methods.  I hear and obey: supply all the missing implementations.

 def isEmpty(): Boolean =
   applyDynamic("isEmpty")().asInstanceOf[Boolean]
 def containsKey(key: Object): Boolean =
   applyDynamic("containsKey")(key).asInstanceOf[Boolean]
 // etc

Fun times would ABOUND.

Note: this need not be Dynamic, it could be separated into DynamicInterface or something.
yes: delegation, meet dynamic, dynamic, meet delegation

i think the primary player is delegation, though -- delegating to a target that is Dynamic

cheers
adriaan

martin odersky

unread,
Feb 21, 2011, 1:05:27 PM2/21/11
to adriaa...@epfl.ch, scala-i...@googlegroups.com
I think I have a solution to our deliberations what to put into the
Dynamic trait. Let's remove everything that is controversial and we
are left with - NOTHING AT ALL!

So the idea is that, if `obj` not have a method `name`, but `obj`s
type inherits from Dynamic, then
obj.name(args) is interpreted as obj.applyDynamic("name", args).

We do not need an applyDynamic to be defined in the trait for this.
It's good enough for it to be defined in the actual type of `obj`.
That scheme is completely analogous to the way we handle `apply` and
the mapping of for comprehensions.

The upside is that different implementations of Dynamic can implement
applyDynamic with different signatures, according to their needs. And
the spec becomes even shorter than it is now.

Cheers

-- Martin

--
----------------------------------------------
Martin Odersky
Prof., EPFL and CEO, Scala Solutions
PSED, 1015 Lausanne, Switzerland

√iktor Klang

unread,
Feb 21, 2011, 1:24:01 PM2/21/11
to scala-i...@googlegroups.com, martin odersky, adriaa...@epfl.ch
On Mon, Feb 21, 2011 at 7:05 PM, martin odersky <martin....@epfl.ch> wrote:
I think I have a solution to our deliberations what to put into the
Dynamic trait. Let's remove everything that is controversial and we
are left with - NOTHING AT ALL!

So the idea is that, if `obj` not have a method `name`, but `obj`s
type inherits from Dynamic, then
obj.name(args) is interpreted as obj.applyDynamic("name", args).

We do not need an applyDynamic to be defined in the trait for this.
It's good enough for it to be defined in the actual type of `obj`.
That scheme is completely analogous to the way we handle `apply` and
the mapping of for comprehensions.

The upside is that different implementations of Dynamic can implement
applyDynamic with different signatures, according to their needs. And
the spec becomes even shorter than it is now.


Slick!
 



--
Viktor Klang,
Code Connoisseur
Work:   Scalable Solutions
Code:   github.com/viktorklang
Follow: twitter.com/viktorklang
Read:   klangism.tumblr.com

Tiark Rompf

unread,
Feb 21, 2011, 1:44:11 PM2/21/11
to scala-i...@googlegroups.com, adriaa...@epfl.ch
Would it be possible to get rid of the Dynamic trait as well, replacing it with a type class?

I might want to make Rep[MyDSLType] dynamic, but not Rep[Int].

- Tiark

Paul Phillips

unread,
Feb 21, 2011, 4:02:37 PM2/21/11
to scala-i...@googlegroups.com, Tiark Rompf, adriaa...@epfl.ch
On 2/21/11 10:44 AM, Tiark Rompf wrote:
> Would it be possible to get rid of the Dynamic trait as well,
> replacing it with a type class?

I was also thinking getting rid of the trait would be nice, but for
different reasons. There's no interface you have to implement to be
used in a for comprehension. Requiring an interface to be implemented
brings on grumpy old grandpa JVM and his inflexible rules, but a
fictional one like NotNull would allow us to cast to it. (There could
also be a real interface which implied the fictional one.)

Sort of similarly, there should/could be a "Comprehensible" interface
but we can't properly capture it with only one method signature per
name. Tangent:

https://lampsvn.epfl.ch/trac/scala/ticket/676
"Create trait for things that are comprehendable"

Jason Zaugg

unread,
Feb 21, 2011, 4:24:57 PM2/21/11
to scala-i...@googlegroups.com, Tiark Rompf, adriaa...@epfl.ch
On Mon, Feb 21, 2011 at 7:44 PM, Tiark Rompf <tiark...@epfl.ch> wrote:
> Would it be possible to get rid of the Dynamic trait as well, replacing it with a type class?
>
> I might want to make Rep[MyDSLType] dynamic, but not Rep[Int].

If the signature isn't captured in the Dynamic trait, implicit params
might let you express that constraint.

trait Rep[T] {
def applyDynamic(name: String, args: Any*)(implicit ev: T <:< MyDslType]
}

-jason

martin odersky

unread,
Feb 22, 2011, 4:59:59 AM2/22/11
to scala-i...@googlegroups.com, Paul Phillips, Tiark Rompf, adriaa...@epfl.ch
Yes, but there's a difference. In for comprehensions we have syntax
that tells us what goes on.
If we made applyDynamic available just by injection of the method
(whether by inheritance or implicits), we have opened the door very
wide indeed to dynamic dispatch everywhere. For instance, it would be
good enough to
have an implicit that adds applyDynamic to Any to avoid all static
type checking everywhere. Maybe in the end
the benefits outweigh the risks, but that's a BIG maybe. So I propose
to proceed carefully.

Introduce the marker trait now. Investigate whether Jason's way is
good enough to deal with all application scenarios. Only if we come up
against a roadblock for something that we agree is essential, we could
consider the following further relaxation:

if `obj` not have a method `name`, but `obj`s

type _conforms to_ (was: inherits from) Dynamic, then


obj.name(args) is interpreted as obj.applyDynamic("name", args).

We can do this in an upwards compatible way later if needed.

Cheers

-- Martin

martin odersky

unread,
Feb 22, 2011, 5:01:15 AM2/22/11
to scala-i...@googlegroups.com, Paul Phillips, Tiark Rompf, adriaa...@epfl.ch
> wide indeed to dynamic dispatch everywhere.

I meant dynamic binding, of course. -- M

Paul Phillips

unread,
Feb 22, 2011, 4:59:04 PM2/22/11
to martin odersky, scala-i...@googlegroups.com, Tiark Rompf, adriaa...@epfl.ch
On 2/22/11 1:59 AM, martin odersky wrote:
> Introduce the marker trait now. Investigate whether Jason's way is
> good enough to deal with all application scenarios.

Sold. I'm digging the good vibes from this scene. (Am attempting to
speak in the language of one of adriaan's hypothetical dynamic language
programmers.)

Reply all
Reply to author
Forward
0 new messages