Re: Constructing a java lambda expression?

Alessio Stalla <[email protected]> Thu, 25 Aug 2022 12:35:17 +0200
Newsgroups gmane.editors.j.devel
Message-ID <CAL2Kp+gFL3hG-MEO8aOjR_EdiTACOCTsoi1=xc-TLU3qvg0DGA@mail.gmail.com>
--0000000000001ebf5005e70e5caa
Content-Type: text/plain; charset="UTF-8"

Because *andThen *is a *default method *in the interface, i.e. it's not
abstract:
https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html
So, effectively, the interface only has one abstract method, and that's the
target of lambda conversion.
Now I have no idea if and how ABCL deals with default methods. If it's
still stuck on Java 5/6 compatibility then I suspect that it can't, at
least not in its Java code.

On Thu, 25 Aug 2022 at 06:37, Alan Ruttenberg <[email protected]>
wrote:

> So in order to implement this I need to know the interface to use for the
> lambda? Presumably I can determine this with reflection.
>
> An example of one of the functions that takes the lambda is defined:
>
> public final NitfSegmentsFlow forEachImageSegment(final
> Consumer<ImageSegment> consumer)
>
> But consumer has two methods: accept and andThen
>
> Eventually it looks like accept is called on the consumer. So in this case
> it looks like I need to use jinterface-implementation and define the accept
> method, no lambda necessary?
>
> What confuses me now is if a lambda is passed as the test code I'm reading
> does:
>
> forEachImageSegment(imageSegment -> {do something})
>
> How does java know that the lambda is the implementation of accept rather
> than andThen. Or more practically, how do I figure out which method to
> implement without reading the source code?
>
> Thanks,
> Alan
>
>
> On Wed, Aug 24, 2022 at 4:14 AM Alessio Stalla <[email protected]>
> wrote:
>
>> Function is a convenient interface for cases when a more specific
>> interface does not exist. But any Java interface with a single abstract
>> method can be the target of a lambda expression:
>>
>> new Thread(() -> { System.out.println("foo"); }).run();
>>
>> The Thread constructor takes a Runnable argument, not a Function.
>>
>> Lambda is just syntax sugar for interfaces with a single method, so you
>> can reproduce them on ABCL with the jinterface thing + some macrology.
>>
>> On Wed, 24 Aug 2022 at 05:10, Vibhu Mohindra <[email protected]>
>> wrote:
>>
>>> On 23/08/2022 05:27, Alan Ruttenberg wrote:
>>> > There's a library I want to use that takes a lambda as an argument.
>>> > Anyone know how to construct one in ABCL?
>>>
>>> I assume it's a Java library taking a Java Lambda that you want to call
>>> from ABCL.
>>>
>>> A Java Lambda is really an instance of one of the classes in
>>> java.util.function. Which one depends on how many parameters it has and
>>> whether it returns a value. Let's assume your library wants a Java
>>> Lambda that has one parameter and returns a value. That's a
>>> java.util.function.Function. Say it looks like this:
>>>
>>> //Lib.java
>>> public class Lib {
>>>    public static void f(java.util.function.Function f) {
>>>      System.out.println("You answered: " + f.apply(5));
>>>    }
>>> }
>>>
>>> such that it can be used from Java with a Java Lambda like this:
>>>
>>> Lib.f((Object x) -> (Integer)x * (Integer)x);
>>> => You answered: 25
>>>
>>> You'd like to give it a Lisp Lambda from ABCL as follows:
>>>
>>> (jstatic "f" "Lib" #'(lambda (x) (* x x)))
>>>
>>> but that's not allowed because although Java can implicitly convert from
>>> a Java Lambda to a java.util.function.Function, it can't convert from a
>>> Lisp Lambda to a java.util.function.Function.
>>>
>>> A Lisp Lambda is really an org.armedbear.lisp.Function. So one solution
>>> is to adapt that.
>>>
>>> //Adaptor.java
>>> import org.armedbear.lisp.*;
>>> public class Adaptor implements java.util.function.Function {
>>>    private org.armedbear.lisp.Function lispFn;
>>>    public Adaptor(org.armedbear.lisp.Function lispFunction) {
>>>      this.lispFn = lispFunction;
>>>    }
>>>    public Object apply(Object input) {
>>>      return lispFn.execute(
>>>        JavaObject.getInstance(input, true)).javaInstance();
>>>    }
>>> }
>>>
>>> and use it from ABCL like this:
>>>
>>> (jstatic "f" "Lib" (jnew "Adaptor" #'(lambda (x) (* x x))))
>>> => You answered: 25
>>> => NIL
>>>
>>> ----
>>> Notes:
>>>
>>> I'm on Java 10, ABCL-1.4.0 (which is old).
>>> I did this to build and run, starting with the jar and two java files in
>>> the current directory:
>>> javac -classpath abcl-1.4.0.jar:. Adaptor.java Lib.java
>>> java -classpath abcl-1.4.0.jar:. org.armedbear.lisp.Main
>>>  > (jstatic "f" "Lib" (jnew "Adaptor" #'(lambda (x) (* x x))))
>>>
>>> You can probably create the Adaptor class from within ABCL if you don't
>>> like that it's written in Java, but I don't remember how to. ABCL's
>>> documentation might describe such bytecode generation somewhere.
>>>
>>> Pointers:
>>> Java: java.util.function.*
>>> ABCL: In org.armedbear.lisp, LispObject, JavaObject, Function
>>>
>>>
>>> --
>>> Vibhu
>>>
>>>

--0000000000001ebf5005e70e5caa
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div>Because <i>andThen </i>is a <b>default method </b>in =
the interface, i.e. it&#39;s not abstract: <a href=3D"https://docs.oracle.c=
om/javase/8/docs/api/java/util/function/Consumer.html">https://docs.oracle.=
com/javase/8/docs/api/java/util/function/Consumer.html</a></div><div>So, ef=
fectively, the interface only has one abstract method, and that&#39;s the t=
arget of lambda conversion.</div><div>Now I have no idea if and how ABCL de=
als with default methods. If it&#39;s still stuck on Java 5/6 compatibility=
 then I suspect that it can&#39;t, at least not in its Java code.<br></div>=
</div><br><div class=3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">=
On Thu, 25 Aug 2022 at 06:37, Alan Ruttenberg &lt;<a href=3D"mailto:alanrut=
[email protected]">[email protected]</a>&gt; wrote:<br></div><blockq=
uote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1p=
x solid rgb(204,204,204);padding-left:1ex"><div dir=3D"ltr"><div>So in orde=
r to implement this I need to know the interface to use for the lambda? Pre=
sumably I can determine this with reflection.<br></div><div><br></div><div>=
An example of one of the functions that takes the lambda is defined:</div><=
div><br></div><div>public final NitfSegmentsFlow forEachImageSegment(final =
Consumer&lt;ImageSegment&gt; consumer) <br></div><div><br></div><div>But co=
nsumer has two methods: accept and andThen <br></div><div><br></div><div>Ev=
entually it looks like accept is called on the consumer. So in this case it=
 looks like I need to use jinterface-implementation and define the accept m=
ethod, no lambda necessary?</div><div><br></div><div>What confuses me now i=
s if a lambda is passed as the test code I&#39;m reading does:</div><div><b=
r></div><div>forEachImageSegment(imageSegment -&gt; {do something})</div><d=
iv><br></div><div>How does java know that the lambda is the implementation =
of accept rather than andThen. Or more practically, how do I figure out whi=
ch method to implement without reading the source code?<br></div><div><br><=
/div><div>Thanks,<br></div><div>Alan</div><div><br></div></div><br><div cla=
ss=3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">On Wed, Aug 24, 20=
22 at 4:14 AM Alessio Stalla &lt;<a href=3D"mailto:[email protected]"=
 target=3D"_blank">[email protected]</a>&gt; wrote:<br></div><blockqu=
ote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px=
 solid rgb(204,204,204);padding-left:1ex"><div dir=3D"ltr"><div>Function is=
 a convenient interface for cases when a more specific interface does not e=
xist. But any Java interface with a single abstract method can be the targe=
t of a lambda expression:</div><div><pre style=3D"background-color:rgb(255,=
255,255);color:rgb(0,0,0);font-family:&quot;JetBrains Mono&quot;,monospace"=
><span style=3D"color:rgb(0,0,128);font-weight:bold">new </span>Thread(() -=
&gt; { System.<span style=3D"color:rgb(102,14,122);font-weight:bold;font-st=
yle:italic">out</span>.println(<span style=3D"color:rgb(0,128,0);font-weigh=
t:bold">&quot;foo&quot;</span>); }).run();</pre></div><div>The Thread const=
ructor takes a Runnable argument, not a Function.</div><div><br></div><div>=
Lambda is just syntax sugar for interfaces with a single method, so you can=
 reproduce them on ABCL with the jinterface thing + some macrology.<br></di=
v></div><br><div class=3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr=
">On Wed, 24 Aug 2022 at 05:10, Vibhu Mohindra &lt;<a href=3D"mailto:vibhu.=
[email protected]" target=3D"_blank">[email protected]</a>&gt; wrot=
e:<br></div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0=
.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On 23/08/2022=
 05:27, Alan Ruttenberg wrote:<br>
&gt; There&#39;s a library I want to use that takes a lambda as an argument=
. <br>
&gt; Anyone know how to construct one in ABCL?<br>
<br>
I assume it&#39;s a Java library taking a Java Lambda that you want to call=
 <br>
from ABCL.<br>
<br>
A Java Lambda is really an instance of one of the classes in <br>
java.util.function. Which one depends on how many parameters it has and <br=
>
whether it returns a value. Let&#39;s assume your library wants a Java <br>
Lambda that has one parameter and returns a value. That&#39;s a <br>
java.util.function.Function. Say it looks like this:<br>
<br>
//Lib.java<br>
public class Lib {<br>
=C2=A0 =C2=A0public static void f(java.util.function.Function f) {<br>
=C2=A0 =C2=A0 =C2=A0System.out.println(&quot;You answered: &quot; + f.apply=
(5));<br>
=C2=A0 =C2=A0}<br>
}<br>
<br>
such that it can be used from Java with a Java Lambda like this:<br>
<br>
Lib.f((Object x) -&gt; (Integer)x * (Integer)x);<br>
=3D&gt; You answered: 25<br>
<br>
You&#39;d like to give it a Lisp Lambda from ABCL as follows:<br>
<br>
(jstatic &quot;f&quot; &quot;Lib&quot; #&#39;(lambda (x) (* x x)))<br>
<br>
but that&#39;s not allowed because although Java can implicitly convert fro=
m <br>
a Java Lambda to a java.util.function.Function, it can&#39;t convert from a=
 <br>
Lisp Lambda to a java.util.function.Function.<br>
<br>
A Lisp Lambda is really an org.armedbear.lisp.Function. So one solution <br=
>
is to adapt that.<br>
<br>
//Adaptor.java<br>
import org.armedbear.lisp.*;<br>
public class Adaptor implements java.util.function.Function {<br>
=C2=A0 =C2=A0private org.armedbear.lisp.Function lispFn;<br>
=C2=A0 =C2=A0public Adaptor(org.armedbear.lisp.Function lispFunction) {<br>
=C2=A0 =C2=A0 =C2=A0this.lispFn =3D lispFunction;<br>
=C2=A0 =C2=A0}<br>
=C2=A0 =C2=A0public Object apply(Object input) {<br>
=C2=A0 =C2=A0 =C2=A0return lispFn.execute(<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0JavaObject.getInstance(input, true)).javaInstanc=
e();<br>
=C2=A0 =C2=A0}<br>
}<br>
<br>
and use it from ABCL like this:<br>
<br>
(jstatic &quot;f&quot; &quot;Lib&quot; (jnew &quot;Adaptor&quot; #&#39;(lam=
bda (x) (* x x))))<br>
=3D&gt; You answered: 25<br>
=3D&gt; NIL<br>
<br>
----<br>
Notes:<br>
<br>
I&#39;m on Java 10, ABCL-1.4.0 (which is old).<br>
I did this to build and run, starting with the jar and two java files in <b=
r>
the current directory:<br>
javac -classpath abcl-1.4.0.jar:. Adaptor.java Lib.java<br>
java -classpath abcl-1.4.0.jar:. org.armedbear.lisp.Main<br>
=C2=A0&gt; (jstatic &quot;f&quot; &quot;Lib&quot; (jnew &quot;Adaptor&quot;=
 #&#39;(lambda (x) (* x x))))<br>
<br>
You can probably create the Adaptor class from within ABCL if you don&#39;t=
 <br>
like that it&#39;s written in Java, but I don&#39;t remember how to. ABCL&#=
39;s <br>
documentation might describe such bytecode generation somewhere.<br>
<br>
Pointers:<br>
Java: java.util.function.*<br>
ABCL: In org.armedbear.lisp, LispObject, JavaObject, Function<br>
<br>
<br>
-- <br>
Vibhu<br>
<br>
</blockquote></div>
</blockquote></div>
</blockquote></div>

--0000000000001ebf5005e70e5caa--