Search code examples
frege

How to evaluate/run frege IO () monad from java?


First of all sorry for my English, and for the fact that I'm trying to learn Haskell

I would run Frege code (Haskell) calling from java, for almost all aspects I managed to make it all work as per instructions found on various sites ... but I still have a question about the following code, and sorry for the wordiness of the request ...

javaHelloTest.java

package local.java;

import java.io.PrintWriter;
import java.util.Arrays;

import frege.runtime.Runtime;
import frege.runtime.Runtime.*;
import frege.java.Util.TList;
import frege.prelude.PreludeArrays;
import frege.prelude.PreludeBase;
import frege.control.monad.State;
import frege.run7.*;
import local.frege.FregeHelloTest;

public class JavaHelloTest {
        public static void main(String[] args) {
                System.out.println("Hello World from Java code ... ");
                System.out.println("========================");
                System.out.println("callingMain0 ... ");
                System.out.println("------------------------");
                FregeHelloTest.callingMain0(Thunk.<PreludeBase.TList<String>>lazy(PreludeArrays.IListSource_JArray.<String>toList(args)));
                System.out.println("========================");
                System.out.println("callingMain1 ... ");
                System.out.println("------------------------");
                FregeHelloTest.callingMain1(Thunk.   <PreludeBase.TList<String>>lazy(PreludeArrays.IListSource_JArray.<String>toList(args)));
                System.out.println("========================");
                System.out.println("callingMain2 ... ");
                System.out.println("------------------------");
                FregeHelloTest.callingMain2(Thunk.   <PreludeBase.TList<String>>lazy(PreludeArrays.IListSource_JArray.<String>toList(args)));
                System.out.println("========================");
        }
}

fregeHelloTest.fr

module local.frege.FregeHelloTest where

import Prelude.PreludeBase as PreludeBase

main :: [String] -> IO ()
main args = println $ "Hello World from Frege code ..."

callingMain0 :: [String] -> ()
callingMain0 ss = PreludeBase.ST.performUnsafe(main ss) 

callingMain1 :: [String] -> IO ()
callingMain1 ss = return ( PreludeBase.ST.performUnsafe(main ss) )

callingMain2 :: [String] -> ()
callingMain2 ss = PreludeBase.ST.run( return ( PreludeBase.ST.performUnsafe(main ss) ) )

fregeHelloTest.java (GENERATED from fregec)

{ ... omissis ... }

final public class FregeHelloTest  {

final public static Func.U<RealWorld, Short> $main(final Lazy<PreludeBase.TList<String/*<Character>*/>> arg$1) {
return PreludeBase.<Func.U<RealWorld, Short>, String/*<Character>*/>$(
               new Func.U.D<String/*<Character>*/, Func.U<RealWorld, Short>>() {
                 public Lazy<Func.U<RealWorld, Short>> apply(final Lazy<String/*<Character>*/> η$7611) {
                   return Thunk.<Func.U<RealWorld, Short>>shared(
                             new Lazy.D<Func.U<RealWorld, Short>>() {
                               public Func.U<RealWorld, Short> call() {
                                 return Prelude.<String/*<Character>*/>println(PreludeText.IShow_String.it, η$7611.call());
                               }
                             }
                           );
                 }
               },
               Thunk.<String/*<Character>*/>lazy("Hello World from Frege code ...")
             ).call();
   }

final public static short callingMain2(final Lazy<PreludeBase.TList<String/*<Character>*/>> arg$1) {
  return (short)PreludeBase.TST.<Short>run(
            PreludeMonad.IMonad_ST.<Object, Short>pure(
                  Thunk.<Short>nested(
                        new Lazy.D<Lazy<Short>>() {
                          public Lazy<Short> call() {
                            return PreludeBase.TST.<Short>performUnsafe(FregeHelloTest.$main(arg$1));
                          }
                        }
                      )
                )
          ).call();
}

final public static Func.U<RealWorld, Short> callingMain1(final Lazy<PreludeBase.TList<String/*<Character>*/>> arg$1) {
  return PreludeMonad.IMonad_ST.<RealWorld, Short>pure(
            Thunk.<Short>nested(
                  new Lazy.D<Lazy<Short>>() {
                    public Lazy<Short> call() {
                      return PreludeBase.TST.<Short>performUnsafe(FregeHelloTest.$main(arg$1));
                    }
                  }
                )
          );
}

final public static short callingMain0(final Lazy<PreludeBase.TList<String/*<Character>*/>> arg$1) {
  return (short)PreludeBase.TST.<Short>performUnsafe(FregeHelloTest.$main(arg$1)).call();
}

public static void main(final java.lang.String[] argv) { ... omissis ... }

}

Program output ... with entry point: local.java.JavaHelloTtest.main

------------------
Hello World from Java code ... 
========================
callingMain0 ... 
------------------------
Hello World from Frege code ...
========================
callingMain1 ... 
------------------------
========================
callingMain2 ... 
------------------------
Hello World from Frege code ...
========================

and after long (for me) survey I realized it is right that "CallingMain1" does not perform anything ... in fact, as you can see generated by the "callingMain2" needs a "run" ... but if I try to execute, with a "run", what comes back "callingMain1" IDE (Eclipse and then the compiler) tells me that the signature is incorrect, PreludeBase.TST.<Short>run is on "Object" and not on "RealWorld", in fact, the compiler in the case of "callingMain2" sets a "Object" and not a "RealWorld" to run callingMain2.

obviously (I think) the signature (Haskell) of "callingMain1" is correct ... and I think no one can touch ...

and now the question ... at this point I think, perhaps, it should be a function ... TST.runOnRealWorld allowing assessment IO () returned from yet "callingMain1"; however, as in the generation of "callingMain2" I clearly see that the operation is changed on-the-fly on "Object" I have to assume that this function does not exist ...

this is wanted or just need to add a "run" method that allows java to evaluate the output of "callingMain1" ?

or, more likely, I understood very little ... Thanks a lot in advance ...


Solution

  • First, I'd like to say there is no need to be sorry for trying to learn Haskell. To the contrary. Consider yourself as belonging to the elite, insead!

    The java code generated for callingMain0 is the right one to run an I/O action from Java. I recommend to use that directly (or through a Java utility method) and not have a seemingly pure helper function like callingMain0 for hygienic reasons.

    By the way, when you pass a value that has a Frege algebraic data type (except for enumerations) you don't need to wrap it in an extra Thunk.<...>lazy() since all those types already implement the Lazy interface. So you can write:

    FregeHelloTest.callingMain0(PreludeArrays.IListSource_JArray.<String>toList(args));
    

    This works regardless whether the function actually expects a lazy list or a strict one.

    Next, callingMain1 Of course, it does nothing, just like

    FregeHelloTest.$main(...) 
    

    would do nothing. Why? Because the type is IO () This type tells us that the function returns an action that will yield a () when that action is executed. And the only way to execute an IO action in Frege is through PreludeBase.TST.<T>performUnsafe. But you don't pass the action (that is, the result of calling callingMain1(...) ) to performUnsafe. Hence, the action is never performed.

    Remark: When you examined the code generated for your Frege module, you have maybe noticed the presence of a main method. If not, look it up. You'll see that the main method that is entered by the JVM just calls $main (which corresponds to your Frege main function) by passing its result to performUnsafe. There simply is no other way.

    Yet another remark: There is a widely shared misconception, namely that Haskell (or Frege) functions with IO type are impure. You see here that this is outright wrong. You can call IO functions as often as you want, and nothing will ever happen, except that an IO action is constructed. This is absolutly pure. For the same arguments, you'll get the "same" (in terms of behaviour, as we can't compare them) IO action back, and no side effect will happen until such actions are actually performed.

    But, you'll ask, why did the performUnsafe inside the callingMain1 function do nothing? This is because return is lazy. There's simply no reason to evaluate its argument. This also shows that performUnsafe is indeed unsafe in Frege code, and all bets are off regarding when and in what order it will be evaluated. For another example, try:

    tail [IO.performUnsafe $ print "Ha"]
    

    Finally, calingMain2 This is the most confusing one, and I'm not sure what you were thinking here. ST.run will run only genuine ST actions that are polymorphic in the phantom type. Now, sure enough, you created such a ST action by saying

    return IO.performUnsafe(main args)
    

    and ST.run did run this action, which resulted in evaluating the performUnsafe.

    But you can't apply ST.run to IO actions. Consider

    type IO = ST RealWorld
    ST.run :: (forall s. ST s a) -> a
    

    When you say:

    ST.run(print "foo")
    

    this won't work, because RealWorld is not as polymorphic as s. And luckily, you can't cheat with it in Java either, since Func<RealWorld,Short> is not a subtype of Func<Object,Short>

    Finally, I want to re-iterate: To run an IO action from Java, there is no other way than to pass it to performUnsafe

    Hope this helps, feel free to ask about things that are not clear yet.