Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert Python function to Java Function #17

Open
ctrueden opened this issue Jun 18, 2020 · 2 comments
Open

Convert Python function to Java Function #17

ctrueden opened this issue Jun 18, 2020 · 2 comments
Milestone

Comments

@ctrueden
Copy link
Member

ctrueden commented Jun 18, 2020

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]

😎

@ctrueden ctrueden added this to the 1.0.0 milestone Jun 18, 2020
@imagejan
Copy link
Member

I know this is a bit of a different terrain, but this issue reminds me of scijava/scijava-common#295.

@ctrueden
Copy link
Member Author

ctrueden commented Nov 2, 2022

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...

@ctrueden ctrueden modified the milestones: 1.0.0, unscheduled Nov 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants