javafrege

Passing Values From Frege to Java and Back


Suppose I have a dumb Frege function that constructs a pair of Nums.

newPair :: (Num α, Num β) => α -> β -> (α, β)
newPair = (,)
-- alternatively -- newPair x y = (x, y)

Attempting to call this function from Java, however, a PreludeBase.CNum<α> and a PreludeBase.CNum<β> are demanded in addition to the expected Lazy<α> and Lazy<β>. Likewise with Show types, where

showSomething :: (Show α) => α -> String
showSomething = show
-- alternatively -- showSomething x = show x

would require a PreludeBase.CShow<α> in addition to the expected parameter.

What is the proper way to pass constrained Frege objects to and from Java?


Solution

  • Good question, since this is not explained in the wiki yet.

    As in all cases like this, I recommend to use the

    :java
    

    command in the REPL. For example:

    frege> newPair 1 2.3
    frege> :java
    

    You will then get a window that contains among all active definitions one that corresponds to this call. A simple text search can help find the place where newPair is called. This should help to resolve such issues most of the time.

    In your case, the relevant part would look like:

    Console.<Integer, Double>numPair(
       PreludeBase.INum_Int.it, 
       PreludeBase.IReal_Double.it, 
       Thunk.<Integer>lazy(1), 
       Thunk.<Double>lazy(2.3))
    

    Here is a short overwiew about how type classes and instances are named and how you can get at them.

    module x.y.Z where
        class Xable where ...
    

    This results in a Java-interface with the fully qualified name

    x.y.Z.CXable
    

    And this:

    module a.b.C where
        import x.y.Z
        data MyType ... = ....
        instance Xable MyType where ...
    

    results in some class

    a.b.C.IXable_MyType  /* implements CXable<TMyType> */
    

    If your instance definition does not have constraints themselves, there will be a singleton instance that you can use.

    a.b.C.IXable_MyType.it
    

    Otherwise, you need to construct a new instance by passing all constraints as argument to the constructor. For example, the Show instance for

    Maybe Int
    

    would look something like this:

    new IShow_Maybe(IShow_Int.it)
    

    since the instance head lists a constraint for the Maybe element type:

    instance Show a => Show (Maybe a)
    

    Note that you need to know the actual type fully, you can't make a generic type class instance. This is never a problem in Frege itself, as all needed instances are passed to a polymorphic function from the caller. However, as it stands, we don't have constraints in native functions.

    Should you need something like this, you can achieve the functionality in most cases by just passing the function you wanted to call as argument.

    For example, this doesn't work:

    pure native myMethod :: Show a => a -> ...
    

    but this should:

    pure native myMethod :: (a -> String) -> a -> ....
    myMethod show (Just 47)
    

    The example java code above also reveals that it is not always as easy as described. For example, it so happens that the Double type doesn't have a separate Num instance, but just one for Real which is a subclass of Num. Unfortunately, only the compiler has the knowledge of what instances are actually present for some type, and which ones are implicit, that is, provided by an instance for a sub-class. Again, the REPL is the best way to find this out.