using lambda to implement one method interface

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

using lambda to implement one method interface

Sonny To
follwing docs at https://www.gnu.org/software/kawa/Anonymous-classes.html

(let ((handler (android.os.Handler (android.os.Looper:getMainLooper)))
      (runnable (lambda ()
                        (android.util.Log:i "scm" "run me"))))
  (handler:post runnable)
  )

that doesn't seem to work. Do I need to give type hints to be able to do this?

I'm getting this error:

#|.....44|# #|.....45|# #|.....46|# #|.....47|# /dev/stdin:46:17:
warning - type function is incompatible with required type
java.lang.Runnable
java.lang.ClassCastException: don't know how to coerce
gnu.expr.LambdaExp$Closure to java.lang.Runnable
        at gnu.bytecode.ObjectType.coerceFromObject(ObjectType.java:180)
        at gnu.kawa.functions.Convert.apply2(Convert.java:38)
        at gnu.mapping.Procedure2.applyToObject(Procedure2.java:62)
        at java.lang.reflect.Method.invoke(Native Method)
        at gnu.mapping.CallContext$ReflectMethodHandle.invokeExact(CallContext.java:726)
        at gnu.mapping.Procedure.applyToConsumerDefault(Procedure.java:75)
        at java.lang.reflect.Method.invoke(Native Method)
        at gnu.mapping.CallContext$ReflectMethodHandle.invokeExact(CallContext.java:726)
        at gnu.mapping.CallContext.runUntilDone(CallContext.java:586)
        at gnu.mapping.CallContext.getFromContext(CallContext.java:616)
        at gnu.expr.Expression.eval(Expression.java:52)
        at gnu.expr.ApplyExp.apply(ApplyExp.java:161)
        at gnu.expr.LetExp.apply(LetExp.java:72)
        at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:281)
        at gnu.expr.ModuleExp.evalModule(ModuleExp.java:211)
        at kawa.Shell.run(Shell.java:283)
        at kawa.Shell.run(Shell.java:196)
        at kawa.Shell.run(Shell.java:183)
        at kawa.TelnetRepl.apply0(TelnetRepl.java:25)
        at gnu.mapping.RunnableClosure.run(RunnableClosure.java:75)
        at java.lang.Thread.run(Thread.java:764)
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Sonny To
this works

(let ((handler (android.os.Handler (android.os.Looper:getMainLooper)))
      )
  (handler:post (lambda ()
                        (android.util.Log:i "scm" "run me")))
  )

this is non-intuitive. why doesn't it work if i bind the lambda to a
symbol? seems like a bug to me


On Sun, Sep 17, 2017 at 8:32 AM, Sonny To <[hidden email]> wrote:

> follwing docs at https://www.gnu.org/software/kawa/Anonymous-classes.html
>
> (let ((handler (android.os.Handler (android.os.Looper:getMainLooper)))
>       (runnable (lambda ()
>                         (android.util.Log:i "scm" "run me"))))
>   (handler:post runnable)
>   )
>
> that doesn't seem to work. Do I need to give type hints to be able to do this?
>
> I'm getting this error:
>
> #|.....44|# #|.....45|# #|.....46|# #|.....47|# /dev/stdin:46:17:
> warning - type function is incompatible with required type
> java.lang.Runnable
> java.lang.ClassCastException: don't know how to coerce
> gnu.expr.LambdaExp$Closure to java.lang.Runnable
>         at gnu.bytecode.ObjectType.coerceFromObject(ObjectType.java:180)
>         at gnu.kawa.functions.Convert.apply2(Convert.java:38)
>         at gnu.mapping.Procedure2.applyToObject(Procedure2.java:62)
>         at java.lang.reflect.Method.invoke(Native Method)
>         at gnu.mapping.CallContext$ReflectMethodHandle.invokeExact(CallContext.java:726)
>         at gnu.mapping.Procedure.applyToConsumerDefault(Procedure.java:75)
>         at java.lang.reflect.Method.invoke(Native Method)
>         at gnu.mapping.CallContext$ReflectMethodHandle.invokeExact(CallContext.java:726)
>         at gnu.mapping.CallContext.runUntilDone(CallContext.java:586)
>         at gnu.mapping.CallContext.getFromContext(CallContext.java:616)
>         at gnu.expr.Expression.eval(Expression.java:52)
>         at gnu.expr.ApplyExp.apply(ApplyExp.java:161)
>         at gnu.expr.LetExp.apply(LetExp.java:72)
>         at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:281)
>         at gnu.expr.ModuleExp.evalModule(ModuleExp.java:211)
>         at kawa.Shell.run(Shell.java:283)
>         at kawa.Shell.run(Shell.java:196)
>         at kawa.Shell.run(Shell.java:183)
>         at kawa.TelnetRepl.apply0(TelnetRepl.java:25)
>         at gnu.mapping.RunnableClosure.run(RunnableClosure.java:75)
>         at java.lang.Thread.run(Thread.java:764)
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Per Bothner
On 09/17/2017 08:38 AM, Sonny To wrote:

> this works
>
> (let ((handler (android.os.Handler (android.os.Looper:getMainLooper)))
>        )
>    (handler:post (lambda ()
>                          (android.util.Log:i "scm" "run me")))
>    )
>
> this is non-intuitive. why doesn't it work if i bind the lambda to a
> symbol? seems like a bug to me

Perhaps a documentation bug: It may not be clear that when the manual
say "required type" in:

     This is possible when the required type is an interface or
     abstract class with a Single (exactly one) Abstract Methods.

we mean the type required by the *compile-time context* of the expression.

This is a deliberate design limitation, not a bug.  This way the anonymous
class can be created a compile-time, not run-time.  Doing it at run-time
would be more difficult and more expensive. Kawa has always put a high
priority on performance.

Perhaps a future Kawa extension will allow "SAM-conversion" at run-time,
but it is not currently in the plans.
--
        --Per Bothner
[hidden email]   http://per.bothner.com/
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Sonny To
Thanks for the explanation. However, I'm still not clear about
compile-time. I'm evaluating this in the TelnetRepl. its interpreted
no?

On Sun, Sep 17, 2017 at 8:59 AM, Per Bothner <[hidden email]> wrote:

> On 09/17/2017 08:38 AM, Sonny To wrote:
>>
>> this works
>>
>> (let ((handler (android.os.Handler (android.os.Looper:getMainLooper)))
>>        )
>>    (handler:post (lambda ()
>>                          (android.util.Log:i "scm" "run me")))
>>    )
>>
>> this is non-intuitive. why doesn't it work if i bind the lambda to a
>> symbol? seems like a bug to me
>
>
> Perhaps a documentation bug: It may not be clear that when the manual
> say "required type" in:
>
>     This is possible when the required type is an interface or
>     abstract class with a Single (exactly one) Abstract Methods.
>
> we mean the type required by the *compile-time context* of the expression.
>
> This is a deliberate design limitation, not a bug.  This way the anonymous
> class can be created a compile-time, not run-time.  Doing it at run-time
> would be more difficult and more expensive. Kawa has always put a high
> priority on performance.
>
> Perhaps a future Kawa extension will allow "SAM-conversion" at run-time,
> but it is not currently in the plans.
> --
>         --Per Bothner
> [hidden email]   http://per.bothner.com/
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Per Bothner
On 09/17/2017 09:03 AM, Sonny To wrote:
> Thanks for the explanation. However, I'm still not clear about
> compile-time. I'm evaluating this in the TelnetRepl. its interpreted
> no?

Yes and no. "Compile-time" is when the Scheme expression is parsed,
analyzed, and (normally) translated to JVM bytecode.  On most platforms,
even when you type an expression into a REPL, it gets translated to bytecode
and then loaded on-the-fly using java.lang.ClassLoader.defineClass.

Because Android doesn't have ClassLoader.defineClass (because it uses dex),
we don't translate REPL input to bytecode. Instead, there is an
interpreter that evaluates to tree (Expression) form of the expression.

Determining when "SAM-conversion" (converting lambda to anonymous class)
is allowed is done at compile-time.  On most platforms, creating the
anonymous class is also done at time-time.  On Android, the last step is
deferred to run-time, using a ProcedurallProxy class, but that only supports
converting a lambda to an interface, not a class.

It probably wouldn't be difficult to use ProcedurallProxy at run-time to convert
a procedure value to a class instance. However, it would require changes to
error handling: Instead of compiling a simple cast, we have to compile to a more
complex (and slower) procedure.  There is also a question of what warning messages,
if any to emit.  Perhaps worth revisiting if/when we implement
"Optional strict typing along with an explicit dynamic type"
(https://www.gnu.org/software/kawa/Ideas-and-tasks.html#Optional-strict-typing-along-with-an-explicit-dynamic-type).

(The 'dynamic' type is already partially supported.)
--
        --Per Bothner
[hidden email]   http://per.bothner.com/
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Sonny To
this is a big problem for my use case...

i wouldn't be able to do something like this on android at runtime

(object (java.lang.Runnable)
((run (a ::void))::void
         (+ 1 1)
         ))



On Sun, Sep 17, 2017 at 9:50 AM, Per Bothner <[hidden email]> wrote:

> On 09/17/2017 09:03 AM, Sonny To wrote:
>>
>> Thanks for the explanation. However, I'm still not clear about
>> compile-time. I'm evaluating this in the TelnetRepl. its interpreted
>> no?
>
>
> Yes and no. "Compile-time" is when the Scheme expression is parsed,
> analyzed, and (normally) translated to JVM bytecode.  On most platforms,
> even when you type an expression into a REPL, it gets translated to bytecode
> and then loaded on-the-fly using java.lang.ClassLoader.defineClass.
>
> Because Android doesn't have ClassLoader.defineClass (because it uses dex),
> we don't translate REPL input to bytecode. Instead, there is an
> interpreter that evaluates to tree (Expression) form of the expression.
>
> Determining when "SAM-conversion" (converting lambda to anonymous class)
> is allowed is done at compile-time.  On most platforms, creating the
> anonymous class is also done at time-time.  On Android, the last step is
> deferred to run-time, using a ProcedurallProxy class, but that only supports
> converting a lambda to an interface, not a class.
>
> It probably wouldn't be difficult to use ProcedurallProxy at run-time to
> convert
> a procedure value to a class instance. However, it would require changes to
> error handling: Instead of compiling a simple cast, we have to compile to a
> more
> complex (and slower) procedure.  There is also a question of what warning
> messages,
> if any to emit.  Perhaps worth revisiting if/when we implement
> "Optional strict typing along with an explicit dynamic type"
> (https://www.gnu.org/software/kawa/Ideas-and-tasks.html#Optional-strict-typing-along-with-an-explicit-dynamic-type).
>
> (The 'dynamic' type is already partially supported.)
>
> --
>         --Per Bothner
> [hidden email]   http://per.bothner.com/
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Per Bothner
On 09/17/2017 11:54 AM, Sonny To wrote:
> this is a big problem for my use case...
>
> i wouldn't be able to do something like this on android at runtime
>
> (object (java.lang.Runnable)
> ((run (a ::void))::void
>           (+ 1 1)
>           ))

You can probably work around it with:

(->java.lang.Runnable
   (lambda ()::void (set! i (+ i 1))))

BTW - I assume (a ::void) is just a typo.  Kawa should probably complain about it.
--
        --Per Bothner
[hidden email]   http://per.bothner.com/
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Sonny To
yes that was an error. copy and paste of testing code.  how would I
implement an interface with multiple methods?


On Sun, Sep 17, 2017 at 11:02 PM Per Bothner <[hidden email]> wrote:

> On 09/17/2017 11:54 AM, Sonny To wrote:
> > this is a big problem for my use case...
> >
> > i wouldn't be able to do something like this on android at runtime
> >
> > (object (java.lang.Runnable)
> > ((run (a ::void))::void
> >           (+ 1 1)
> >           ))
>
> You can probably work around it with:
>
> (->java.lang.Runnable
>    (lambda ()::void (set! i (+ i 1))))
>
> BTW - I assume (a ::void) is just a typo.  Kawa should probably complain
> about it.
> --
>         --Per Bothner
> [hidden email]   http://per.bothner.com/
>
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Sonny To
It seems implementing single method interface using lambda only works
with methods and not in constructors?

for example, this fails

#|kawa:137|# (java.lang.Thread (lambda ()
                    (+ 1 1)))
#|.....138|# /dev/stdin:137:1: warning - no possibly applicable method
'<init>/valueOf' in java.lang.Thread
gnu.mapping.WrongArguments
        at gnu.mapping.MethodProc.matchFailAsException(MethodProc.java:136)
        at gnu.kawa.reflect.Invoke.applyToObject(Invoke.java:264)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at gnu.mapping.CallContext$ReflectMethodHandle.invokeExact(CallContext.java:726)
        at gnu.mapping.Procedure.applyToConsumerDefault(Procedure.java:75)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at gnu.mapping.CallContext$ReflectMethodHandle.invokeExact(CallContext.java:726)
        at gnu.mapping.CallContext.runUntilDone(CallContext.java:586)
        at gnu.expr.ModuleExp.evalModule2(ModuleExp.java:342)
        at gnu.expr.ModuleExp.evalModule(ModuleExp.java:211)
        at kawa.Shell.run(Shell.java:283)
        at kawa.Shell.run(Shell.java:196)
        at kawa.Shell.run(Shell.java:183)
        at kawa.repl.processArgs(repl.java:714)
        at kawa.repl.main(repl.java:820)

On Mon, Sep 18, 2017 at 9:35 AM, Sonny To <[hidden email]> wrote:

> yes that was an error. copy and paste of testing code.  how would I
> implement an interface with multiple methods?
>
>
> On Sun, Sep 17, 2017 at 11:02 PM Per Bothner <[hidden email]> wrote:
>>
>> On 09/17/2017 11:54 AM, Sonny To wrote:
>> > this is a big problem for my use case...
>> >
>> > i wouldn't be able to do something like this on android at runtime
>> >
>> > (object (java.lang.Runnable)
>> > ((run (a ::void))::void
>> >           (+ 1 1)
>> >           ))
>>
>> You can probably work around it with:
>>
>> (->java.lang.Runnable
>>    (lambda ()::void (set! i (+ i 1))))
>>
>> BTW - I assume (a ::void) is just a typo.  Kawa should probably complain
>> about it.
>> --
>>         --Per Bothner
>> [hidden email]   http://per.bothner.com/
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Sudarshan S Chawathe
In reply to this post by Sonny To
> how would I implement an interface with multiple methods?

I have found that simply defining the methods required by an interface
(with the proper names and type signatures) works, in the sense that the
resulting objects can be used anywhere that requires objects
implementing the interface.

Regards,

-chaw
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Per Bothner
In reply to this post by Sonny To
On 09/18/2017 01:16 AM, Sonny To wrote:

> It seems implementing single method interface using lambda only works
> with methods and not in constructors?
>
> for example, this fails
>
> #|kawa:137|# (java.lang.Thread (lambda ()
>                      (+ 1 1)))
> #|.....138|# /dev/stdin:137:1: warning - no possibly applicable method
> '<init>/valueOf' in java.lang.Thread
> gnu.mapping.WrongArguments

Yes and no.  Method selection uses different code than the code that checks
for type compatibility.  It's mostly the same tests, but the former doesn't
handle lambda-to-interface conversion.  It would be non-trivial to change that.

However, this works:

(java.lang.Thread (->java.lang.Runnable (lambda () (newline))))

--
        --Per Bothner
[hidden email]   http://per.bothner.com/
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Per Bothner
In reply to this post by Sudarshan S Chawathe
On 09/18/2017 04:22 AM, Sudarshan S Chawathe wrote:
>> how would I implement an interface with multiple methods?
>
> I have found that simply defining the methods required by an interface
> (with the proper names and type signatures) works, in the sense that the
> resulting objects can be used anywhere that requires objects
> implementing the interface.

Not sure I understand what you mean by that.

The problem is defining an instance of an interface:
(1) with multiple methods;
(2) on-the-fly, in a REPL;
(3) on Android, which doesn't (didn't?) have ClassLoader.defineClass.

It should be possible to generalize gnu.kawa.reflect.ProceduralProxy
to handle multiple methods and corresponding implementing procedures.
The tricky is specifying which method is implemented by which procedure.

Perhaps problem (3) above is no longer a problem, at least on Android 8.
https://developer.android.com/reference/java/lang/ClassLoader.html
says that the byte array to defineClass "should have the format of a valid
class file as defined by The Java™ Virtual Machine Specification."

So maybe we need to update the compilerAvailable test in ModuleExp.java.
If we now can generate classes on-the-fly then that removes a major
limitation of Kawa on Android.  (At least on newer Android versions.)
--
        --Per Bothner
[hidden email]   http://per.bothner.com/
Reply | Threaded
Open this post in threaded view
|

Re: using lambda to implement one method interface

Sudarshan S Chawathe

Mea culpa.  I temporarily lost sight of the fact that the discussion was
in the context of a REPL on Android.

-chaw

> Subject: Re: using lambda to implement one method interface
> Cc: Kawa mailing list <[hidden email]>
> From: Per Bothner <[hidden email]>
> Date: Mon, 18 Sep 2017 07:22:56 -0700
>
> On 09/18/2017 04:22 AM, Sudarshan S Chawathe wrote:
> >> how would I implement an interface with multiple methods?
> >
> > I have found that simply defining the methods required by an interface
> > (with the proper names and type signatures) works, in the sense that the
> > resulting objects can be used anywhere that requires objects
> > implementing the interface.
>
> Not sure I understand what you mean by that.
>
> The problem is defining an instance of an interface:
> (1) with multiple methods;
> (2) on-the-fly, in a REPL;
> (3) on Android, which doesn't (didn't?) have ClassLoader.defineClass.
>
> It should be possible to generalize gnu.kawa.reflect.ProceduralProxy
> to handle multiple methods and corresponding implementing procedures.
> The tricky is specifying which method is implemented by which procedure.
>
> Perhaps problem (3) above is no longer a problem, at least on Android 8.
> https://developer.android.com/reference/java/lang/ClassLoader.html
> says that the byte array to defineClass "should have the format of a valid
> class file as defined by The Java™ Virtual Machine Specification."
>
> So maybe we need to update the compilerAvailable test in ModuleExp.java.
> If we now can generate classes on-the-fly then that removes a major
> limitation of Kawa on Android.  (At least on newer Android versions.)
> --
> --Per Bothner
> [hidden email]   http://per.bothner.com/
>