typelead/eta

Trouble getting FFI working with a static Java method...

GordonBGood opened this issue · 4 comments

Description

I wanted to time some functions as a bench mark, and ran into some snags getting it going. The problem arose because of errors in a couple of places:

  1. the Importing example under Modules in the Eta Cheatsheet refers to the use of the Haskell package "Data.Time" which doesn't (yet) exist for Eta.
  2. Trying to import the Java static method "currentTimeMillis" from java.lang.System didn't seem to work as described below...

Expected Behavior

I can't see any problems and expected to see a Long printed showing the current time value.

Actual Behavior

The program compiled, but the following error halted it at run time:

Exception in thread "main" java.lang.NoSuchMethodError: java.lang.System.currentTimeMillis(Leta/runtime/stg/Closure;)J
        at main.Main.$wa(etabench.hs:29)
        at main.Main.main1(etabench.hs)
        at main.Main$main1.applyV(etabench.hs)
        at eta.runtime.exception.Exception.catch_(Exception.java:132)
        at main.Main.main5(etabench.hs)
        at main.Main.DZCmain(etabench.hs:21)
        at main.Main$DZCmain.applyV(etabench.hs:21)
        at eta.runtime.stg.Closures$EvalLazyIO.enter(Closures.java:145)
        at eta.runtime.stg.Capability.schedule(Capability.java:254)
        at eta.runtime.stg.Capability.scheduleClosure(Capability.java:210)
        at eta.runtime.Runtime.evalLazyIO(Runtime.java:372)
        at eta.runtime.Runtime.main(Runtime.java:365)
        at eta.main.main(Unknown Source)

Possible Fix

Eventually, I discovered "getCPUTime" from the "System.CPUTime" module, which did what I needed in this case.

However, I would like to know why the static method failed to be imported correctly, as there may be a need for other static methods. If I did something wrong, then this should be better documented with better examples on how to import static methods...

Steps to Reproduce

The following code exhibits the behaviour:

import Java

foreign import java unsafe "@static java.lang.System.currentTimeMillis"
  currentTimeMillis :: () -> IO Int64
  
main :: IO ()
main = do
  now <- currentTimeMillis()
  print now

Context

I was trying to time a benchmark function...

Your Environment

Windows 10 64-bit updated to everything except the October 2018 update, with Eta installed and run through "etlas".

@GordonBGood The error was specifying () -> IO Int64 instead of IO Int64 - perhaps we need to put an example of binding to a Java method that doesn't take arguments. Happy to accept a PR for this as well (adding this particular example to the docs).

Btw, Data.Time does exist if you specify a dependency for the time package in your .cabal file. Perhaps the cheatsheet should make that more clear.

@rahulmutt, thanks for the assist in the proper type signature. I see that you use a getter type of syntax, I suppose in support of calling Java getter properties. I wondered if it might be something like this as I saw declarations for methods with no arguments (other than the object itself in the case of instance methods) that looked like what works, but didn't understand the implications and spent quite some time looking for an example showing it clearly, so it does need a better example for cases with a method with no arguments (both static and instance methods).

Also thanks for the advice on how to use Data.Time. I'll try adding the time package as well, but don't know where the repository for the cheatsheet is to fill an issue on it.

The cheatsheets are not synced (yet) with this repo. I'll look into it.

As per @rahulmutt's advice above, I added the corrected working example from this issue as PR #936.