Convert Python function to Java Function
Opened this issue · 2 comments
It would be nice to support something like this:
>>> def fib(n):
... if n == 0 or n == 1:
... return 1;
... return fib(n - 1) + fib(n - 2)
...
>>> import scyjava
>>> jfib = scyjava.convert.to_java(fib)
>>> print(type(jfib))
<class 'scyjava.convert._convert.FunctionFromPython'>
>>>
Where FunctionFromPython
is:
from jnius import PythonJavaClass, java_method
class FunctionFromPython(PythonJavaClass):
__javainterfaces__ = ['java/util/function/Function']
def __init__(self, function):
self.function = function
@java_method('(Ljava/lang/Object;)Ljava/lang/Object;')
def apply(self, t):
return self.function(t)
Then anything operating on a Java Function
can accept a Python function
with appropriate conversion. For example:
jlist = ij.py.to_java([5, 4, 3, 2, 1])
fibList = jlist.stream().map(jfib).toArray()
print(f'{type(fibList)}')
print(fibList)
Prints:
<class 'list'>
[8, 5, 3, 2, 1]
😎
I know this is a bit of a different terrain, but this issue reminds me of scijava/scijava-common#295.
Here is a JPype version of the above example code:
import jpype
jpype.startJVM()
@jpype.JImplements('java.util.function.Function')
class PythonFunction:
def __init__(self, function):
self.function = function
@jpype.JOverride
def apply(self, o):
return self.function(o)
def fib(n):
if n == 0 or n == 1:
return 1;
return fib(n - 1) + fib(n - 2)
jfib = PythonFunction(fib)
ArrayList = jpype.JClass('java.util.ArrayList')
jlist = ArrayList()
jlist.addAll([5, 4, 3, 2, 1])
jvals = jlist.stream().map(jfib).toArray()
print(f"type(jvals) = {type(jvals)}")
print(f"type(jvals[0]) = {type(jvals[0])}")
print(f"jvals = {jvals}")
produces:
type(jvals) = <java class 'java.lang.Object[]'>
type(jvals[0]) = <java class 'java.lang.Long'>
jvals = [8, 5, 3, 2, 1]
A similar JImplements
proxy could be made for all of the various functional interfaces that are part of the Java standard library (Supplier
, BiFunction
, Consumer
, BiConsumer
, etc.).
Then, each Python function (or Callable
more generally perhaps) could be wrapped to the an appropriate interface by examining its signature. But just because we could do that, doesn't mean we should... I don't have a good use case for automagical inference in this way, and it might not end up being doable—e.g. the only way I know of to glean whether a Python function returns anything is via type hints, which might not be present.
I think we should wait to think about this any further for the moment... I foresee returning to this issue as the SciJava Ops project gets further along and we start wanting to write existing Python functions as ops...