Open clojurians-org opened 5 years ago
@clojurians-org Have you tried using Maybe [object]
as your return type? Nothing
should get marshalled to null
.
thanks, i'm trying do it. but another issue occur, i don't know to build the object for export.
data HTest = HTest @my.HTest deriving Class
foreign export java mkHTest :: Int -> Java a (Maybe HTest)
mkHTest 0 = return Nothing
mkHTest _ = return $ Just HTest
Expected type: Int -> Java a (Maybe HTest)
Actual type: Int
-> Java
a (Maybe (ghc-prim-0.4.0.0:GHC.Prim.Object# HTest -> HTest))
Hi! I think you have to create an instance of HTest instead, somethink like:
foreign import java unsafe "@new" newHTest :: Java a HTest
mkHTest _ = return $ Just newHTest
it seems it didn't generate the corresponding class file:
the code is very simple:
data HTest = HTest @my.HTest deriving Class
foreign import java unsafe "@new" newHTest :: Java a HTest
foreign export java mkHTest :: Int -> Java a (Maybe HTest)
mkHTest 0 = return Nothing
mkHTest _ = Just <$> newHTest
unzip jar, and find it.
[op@my-200 tmp]$ jar -tf callJava.jar | grep HTest.class
main/main/datacons/HTest.class
main/main/tycons/HTest.class
main/Main$$fe_mkHTest.class
main/Main$DHTest.class
main/Main$mkHTest.class
main/Main$newHTest.class
[op@my-200 tmp]$ javap ./main/main/datacons/HTest.class
Compiled from "Main.hs"
public final class main.main.datacons.HTest extends main.main.tycons.HTest {
public my.HTest x1;
public main.main.datacons.HTest(my.HTest);
public int getTag();
}
[op@my-200 tmp]$ javap ./main/main/tycons/HTest.class
Compiled from "Main.hs"
public abstract class main.main.tycons.HTest extends eta.runtime.stg.DataCon {
public main.main.tycons.HTest();
}
Oh, yeah, you can't use a free type variable in exports if they are not static so i think that:
-- To make eta create the class you have to replace `a` with `HTest`
foreign export java mkHTest :: Int -> Java HTest (Maybe HTest)
... should work.
However in the java world it is not frequent to use a instance method to generate an instance of the same class but a static factory method so this one maybe would be more idiomatic:
data HTest = HTest @my.HTest deriving Class
-- As all classes have an empty constructor by default you can import it.
foreign import java unsafe "@new" newHTest :: Java a HTest
-- This export actually creates the class my.HTest
foreign export java "@static my.HTest.mkHTest" mkHTest :: Int -> Java a (Maybe HTest)
mkHTest 0 = return Nothing
mkHTest _ = Just <$> newHTest
Note that you can use Java a (Maybe HTest)
in this case cause the export has an @static annotation.
It is a little bit tricky but i hope the info in the user guide could help you.
There is plans to make exports automatic to avoid all this: #690
it seems the maybe type didn't handle null value well. this is the information from my side
larrys-MBP:javaCall larluo$ cat src/Main.hs
module Main where
import GHC.Base(Class)
import Java (Java)
main :: IO ()
main = putStrLn "Hello, Eta!"
data HTest = HTest @my.HTest deriving Class
foreign import java unsafe "@new" newHTest :: Java a HTest
foreign export java "@static my.HTest.mkHTest" mkHTest :: Int -> Java a (Maybe HTest)
mkHTest 0 = return Nothing
mkHTest _ = Just <$> newHTest
foreign export java "@static my.HTest.mkHTest2" mkHTest2 :: Int -> Java a HTest
mkHTest2 _ = newHTest
larrys-MBP:tmp larluo$ javap my/HTest.class
public class my.HTest {
public my.HTest();
public static my.HTest mkHTest2(int);
public static eta.runtime.stg.Closure<my.HTest> mkHTest(int);
}
wget https://download.clojure.org/install/clojure-tools-1.10.0.442.tar.gz
larrys-MBP:javaCall larluo$ java -cp clojure-tools-1.10.0.442.jar:./dist/build/eta-0.8.6.5/javaCall-0.1.0.0/x/javaCall/build/javaCall/javaCall.jar clojure.main
Clojure 1.10.0
user=> (import '[my HTest])
my.HTest
user=> (HTest/mkHTest2 1)
#object[my.HTest 0x45f24169 "my.HTest@45f24169"]
user=> (HTest/mkHTest 0)
Execution error (NoSuchFieldError) at my.HTest/mkHTest (REPL:-1).
x1
user=> (HTest/mkHTest 1)
Execution error (ClassCastException) at my.HTest/mkHTest (REPL:-1).
base.ghc.maybe.datacons.Just cannot be cast to base.ghc.maybe.datacons.Nothing
@jneira I remember we allowed arbitrary closures in the foreign export mechanism a while back. I wonder if we need to fix that to handle Maybe
's properly.
I am just analyzing this case. For a very simple file:
data HTest = HTest @my.HTest deriving Class
foreign import java unsafe "@new" newHTest :: Java a HTest
foreign export java "@static my.HTest.mkHTest4" mkHTest4 :: Int -> Java a (Maybe HTest)
mkHTest4 _ = return Nothing
I am wondering why the final cast in method bytecode is to Nothing
(not to Maybe
).
So far I got that
typeDataConClass is DsForeign.hs
for Type argument Maybe HTest
returns a class name:
base/ghc/maybe/datacons/Nothing
Which seems odd, and is (maybe :-) ) one of the problems.
I am going further.
Hi! I've created somehow naive but working solution. I catch explicitly Maybe result types in export FFI and make check for Notihng.INSTANCE - in case of eq there is null returned, otherwise code as before. After some code cleanup I could potentially make PR. I am however, awaiting for hints / issues. (I've used the example to relearn again eta/ghc compiler - very likely some fragments are reinventing the wheel. Also I am not even sure if the whole approach is sensible).
Fix works with such code:
data HTest = HTest @my.HTest deriving Class
foreign import java unsafe "@new" newHTest :: Java a HTest
foreign export java "@static my.HTest.mkHTest" mkHTest :: Int -> Java a (Maybe HTest)
mkHTest 0 = return $ Nothing
mkHTest _ = Just <$> newHTest
Closure obj = HTest.mkHTest(1);
System.out.println(obj.getClass());
System.out.println(obj);
Closure obj2 = HTest.mkHTest(0);
System.out.println(obj2);
@jarekratajski Just took a look at your fork and it looks good to me. Feel free to send a PR.
Make sure to add a couple tests. The testing framework was upgraded and it now supports full etlas projects so you can make a project that uses both Eta/Java to make sure this is working properly.
I have realised that this result as Closure<X>
in exported Java is probably not something anyone would want - I am trying to reduce it to be exactly X (return type in case of Java a (Maybe X)
. It will take some time - but I am getting there (slowly).
i export an eta object to java for implementing interface. but the interface result object can be null on some situation. i didn't find any eta code for doing this, so what i should do for this case?