FregeFX IntelliJ setup

After an unsuccessfull weekend of trying to get Haskell using an HTTP package, I decided to finally give Frege a try. And while being at it, why not try FregeFX as well. Another weekend of moments of confusion, frustration and euphoria got me to the initial setup of creating projects with FregeFX in IntelliJ. I put this setup in a template for whomever might need it. Here's the link to the template: FregeFX IntelliJ project template. The following article describes my odyssey into the world of Frege(FX) ...

Introduction

I'm a fan of functional programming and Haskell in praticular, since the moment I understood the idea at university. I've used it quite a bit on codingame for implementing algorithms. Then I wanted to move forward and use it for a real project, with accessing the network and the filesystem. I failed miserably, but, while searching the internet, I found that at least I wasn't the only one. I'm also a fan of the JVM and I've known of the existance of Frege for a while too. I wanted to give it a try for a while and I guess this was the moment, that I've been looking for.

First of all, there is lots of helpful content about Frege and FregeFX on the internet, that is worth mentioning. While setting up the template, I worked through most of the following sources:

Big thanks to the authors of all those projects!

What are the project properties/requirements?

The technical basics of the template project are as follows:

  • It's gradle project.
  • It supports a GUI based on FregeFX (JavaFX for Frege).
  • The window design should be defined in FXML and CSS.
  • User actions are handled by a controller, which is referenced in the FXML file.
  • All logic should be implemented in Frege.

Setting up the gradle project

A basic gradle file can be found in the build gradle of the fregeTutorial. It covers the following features:

  • Addition of Frege to Java source set
  • Gradle tasks for Frege and FregeFX
    • Compile tasks
    • Test tasks
    • Frege REPL task

Frege code will first be compiled to Java (.java) and then into Java class files (.class). The files of both types will be generated into the build folder. This setup allows using Java from Frege, since Frege can access manually created Java files from the source directory. This includes IDE support.

!!! Hint: The IDE support in Eclipse using the Frege Eclipse plugin seems to be more advanced then the support in IntelliJ at the time of the creation of this article. This is a guess though, since I only worked with Frege in IntelliJ and the knowledge for working with Frege in Eclipse comes from reading a few articles and comments.

It is also possible to use Frege code from Java with this setup, but there is no IDE support. At least IntelliJ is not searching the build folder for Java sources (why should it) and setting up the build folder as src directory will break the compile process. Without the word completion, it is necessary to check the generated code. When implementing the calls to the generated code, the IDE will mark the calls as faulty, because it can't find the code. It will compile though, since the compilation will use the compiled Frege code in the build directory.

This turns out to be a problem. The fourth project property requires a Controller, which is written in Java, because an object instance with non-static methods (handler methods) and fields (element references) is required. Frege code doesn't compile into "simple POJOs". Annotations can't be set either in Frege, but the interface fields and methods towards the FXML need to be annotated with the @FXML tag. So, the controller needs to be a POJO with annotations. If the controller is written in Java and the logic is written in Frege, then Java to Frege calls are necessary, in which the controller acts as interface from the GUI to the actual controller logic. Of course, IDE support for this would be great.

So, how can the tooling support of the IDE be achieved, with FXML and controllers, which forces Java to Frege calls? The solution is a gradle feature called source sets. By defining a seperate feature set for Frege, it is also possible to change the build path for compilation. The Frege compile path can direct to a new Java source directory, which also needs to be added to the Java source set. The compile dependencies need to be adjusted as well - Frege needs to be compiled before compiling Java. When Java is compiled to class files, it will also use the compiled Frege code. And the IDE will use the new Java source directory for tooling support.

This is the additional/changed code in the build.gradle:

sourceSets {
    main {
        java {
            // src/frege/java will contain the java files generated from Frege
            // by adding it to the srcDirs, we get IDE support for accessing
            // the Frege content from Java.
            srcDirs = ['src/main/java', 'src/frege/java']
        }
    }
    // Add a special source set for Frege and set the output directory for the
    // Frege compiler to src/frege/java. It's using the Java output dir, so we
    // need to change that one for this source set *rolling eyes*
    frege {
        java {
            outputDir = file("src/frege/java")
        }
    }
}

dependencies {
    fregeCompile 'org.frege-lang:frege:3+'
    fregeCompile 'org.frege-lang:fregefx:0.8'

    compile 'org.frege-lang:frege:3+'
    compile 'org.frege-lang:fregefx:0.8'
    // we want to call Frege from Java, so it needs to be compiled to Java first
    compileOnly sourceSets.frege.output

    testCompile 'junit:junit:4+'
    testCompile 'org.mockito:mockito-all:1+'
    // Just in case we have an implicit reference from Java to Frege during the tests
    testCompile sourceSets.frege.output
}

compileFrege {
    target = javaTarget
    compileGeneratedJava = false // i'd like to NOT have class files generated by Frege, but it doesn't work :-\
}
compileTestFrege {
    target = javaTarget
}

fregeDoc {
    verbose = true
    module = 'src/frege/java'
}

fregeQuickCheck {
    moduleDir = "$project.buildDir/classes/java/test"
    classpathDirectories = ["$project.buildDir/classes/java/main", "$project.buildDir/classes/java/test"]
}

The Frege code goes into src/frege/frege. The compiled Frege code goes into src/frege/java. Non-generated Java code goes as usual into src/main/java and resources into src/main/resources. Tests go into src/test/frege and src/test/java with test resources being in src/test/resources.

This will compile Java twice: once before Frege (this is part of the Frege tool chain) and once after Frege, which is the result of the above shown gradle setup. The achievement of this is the IDE tooling supporting two way communication between Frege and Java.

There are at least two alternatives worth mentioning:

  • Don't use FXML. All elements and handlers can be created in Frege during the GUI setup. This is similar to the way everything can be created as Java objects. This is the way of all examples, that I've found so far.
  • Use FXML, but don't use a controller. FregeFX, just like Java, allows creating/accessing Scene objects. They can be used to gain access to elements defined in FXML using the elements IDs. Then handlers can be attached to the elements. This can also be done during the GUI setup.

What is the reason to go through all the problems with the FXML and the controller? There are good arguments to use the alternatives. First and foremost, it's less complicated then the setup of this article. Well, I prefer FXML to take the UI design out of the code to "clean it up" and I simply like to link the UI to the logic using references to controllers.

Setting up the UI

This step is quite short in code, because the GUI is defined in FXML. Here is the code:

module de.atennert.fregefx.Hello where

import fregefx.JavaFxType
import fregefx.JavaFxAll
import fregefx.JavaFxUtils

{-- Main function - starting point of the program, initialize UI --}
main _ = do
    FregeFX.launch $ withStage buildUI

{-- Build the UI using FXML --}
buildUI :: Family a => a -> Stage -> JFX a
buildUI root stage = do
    stage.setTitle "Hello world"
    view  <- FregeFX.fxml "de.atennert.fregefx.Hello" "/main_screen.fxml"
    scene <- Scene.new view
    stage.setScene scene
    -- you can get UI elements with scene.lookup "#id" -> JFX (Maybe Node) and register handling
    return root

This code includes the main function, which will be called at the start of the program. That function launches FregeFX, which builds the UI using the buildUI function. This function gets the root element and the stage. It sets the window title and, by utilising the Scene, the FXML UI description and it returns root element. As mentioned earlier, it's possible to use the Scene for returning UI elements and registering handlers on them.

Communication from the UI to Frege

How can user actions be evaluated? At this point, the concept of JFX and IO actions becomes relevant. In short, UI actions should run in a different thread, then the rest of the program, to not block while data is processed in the background. This is partially enforced by the framework, in which the UI actions need to be in the JFX monad, while IO actions need to be in the IO monad. It is possible to switch between the to worlds by a view functions, provided by FregeFX. I'll name three of them here:

  • inIO - allows to execute IO actions coming from the UI
  • withUI - allows to execute UI (JFX) actions coming from IO
  • withStage - allows to execute UI (JFX) actions with the JavaFX stage (will be provided as parameter)

!!! Note: In Frege, the interaction with mutable Java objects is done with the ST monad. As far as I have seen it can be used with functions, which make use of the IO monad, like inIO.

So, when IO actions, like writing to stdout, need to be executed on a button press of a user, inIO can be used to achieve this.

There is another thing. With the given setup, the user actions will first be executed in the Java conttroller. Then the controller calls Frege code. Calling Frege from Java means, that the actions are run inside the ST monad. It is kind of equal to the IO monad so it is not necessary to use a conversion method like inIO.

However, the call from Java to Frege needs to be done in a certian way.

Given a function doIO in the Hello module in Frege, it can be called like this:

...
doIO :: IO ()
doIO = println "hello again"
...
public class HelloController {
    @FXML
    public void doIO( ActionEvent e ) {
        PreludeBase.TST.performUnsafe( Hello.doIO.call() ).call();
    }

    ...
}

After the conversion to Java, doIO will be wrapped in a so called Func (a special kind of function). Executing call on it, the doIO function (also a Func object) is returned. This Frege function needs to be executed from Java inside the performUnsafe method, which will return a thrunk. That is a lazy execution wrapper container. Executing call on this container will then execute the function.

The Frege functions, that can be called from Java need to use IO or ST monads. In case of UI (JFX) actions, it's necessary to use a conversion function like withUI to wrap the UI call. Here is an example from a project (not the template).

...
setLabeledText :: Labeled -> String -> IO ()
setLabeledText labeled text = (withUI $ labeled.setText text) `inIO` (\_ -> return ())
...
public class UIController {
    @FXML
    private Button myButton;

    @FXML
    public void () {
        PreludeBase.TST.performUnsafe( Hello.setLabeledText(myButton, "new text").call() ).call();
    }
}

It's similar to the IO scenario, with the addition of providing the button and the text to the function. Here withUI and inIO are used to execute the UI action (setting the text of the button) in the JFX context and then returning back to the IO context, which is required as the return type, because the function is called in the Java code.

To round up this section, here is the FXML UI definition from the template:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import java.lang.String?>

<VBox styleClass="container" xmlns="http://javafx.com/javafx"
      xmlns:fx="http://javafx.com/fxml"
      fx:controller="de.atennert.fregefx.HelloController">
  <stylesheets>
    <String fx:value="hello.css"/>
  </stylesheets>

  <Label fx:id="label1" text="Hello" />
  <HBox styleClass="container">
    <Button fx:id="button1" text="Do IO" onAction="#doIO"/>
    <Button fx:id="button2" text="Do UI" onAction="#doUI"/>
  </HBox>
</VBox>

How about file and network IO?

This is not part of the template, but since it is mentioned in the introduction, let's have a peek.

File output

It is suggested to use a BufferedWriter for efficient writing to the storage. A FileWriter is required for writing files and both are wrapped in a PrintWriter for convenience.

Frege already contains definitions for different readers and writers, which can be looked up in the API and the Frege sources. Unfortunately, the PrintWriter can't take a BufferedWriter as parameter at the time of writing this article. It can take a Writer and BufferedWriter extends Writer in Java, but Frege has a strict type system, which doesn't know about this. In Frege BufferedWriter and Writer are completely different things. The PrintWriter is already part of Frege, so it only needs to be extended. The FileWriter as well as the BufferedWriter are not yet part of the Frege definitions. So, I decided to write my own definitions for accessing the Java classes:

private data BufferedWriter = mutable native java.io.BufferedWriter where
    native new :: FileWriter -> IO BufferedWriter

private data FileWriter = mutable native java.io.FileWriter where
    native new :: String -> Bool -> IO FileWriter throws IOException

-- extending the PrintWriter
native newPW new   :: BufferedWriter -> IO PrintWriter
native close close :: PrintWriter -> IO ()
native flush flush :: PrintWriter -> IO ()

Those definitions contain the minimal set, that was needed for my project, but it can be extended as necessary.

Now the functions for writing to a file can be created:

open :: String -> IO PrintWriter
open path = newPw =<< BufferedWriter.new =<< FileWriter.new path

write :: String -> PrintWriter -> IO ()
write text writer = writer.print text

close :: PrintWriter -> IO ()
close writer = writer.close

=<< (or >>= respectively) is explained well in Frege Goodness, chapter "List and Path".

HTTP requests

The goal of the HTTP request is, to download a website, which is defined by it's URL, and return the content in form of a String.

The corresponding Java function may look like this (source taken from stackoverflow):

public static String getHTML(String urlToRead) throws Exception {
    StringBuilder result = new StringBuilder();
    URL url = new URL(urlToRead);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    BufferedReader rd = new BufferedReader( new InputStreamReader( conn.getInputStream()));
    String line;
    while ((line = rd.readLine()) != null) {
        result.append(line);
    }
    rd.close();
    return result.toString();
}

This code contains a cast to HttpURLConnection. Again, extends / implements doesn't exist in Freges strict type system, neither do casts. Similarly to the BufferedWriter, the BufferedReader requires a Reader as argument and not an InputStreamReader. Fortunately, it's possible to define explicit casts from one class to another. For the HTTP request handling, I choose to define the casts to profit from the existing URL type and the BufferedReader features from Frege:

import frege.java.Net

private data ClassCastException = native java.lang.ClassCastException

private data ProtocolException = native java.net.ProtocolException
derive Exceptional ProtocolException

private class URLCastTarget a where
    private downcastUrl :: URLConnection -> IO (ClassCastException | a)

instance URLCastTarget (MutableIO HttpURLConnection) where
    native downcastUrl "(java.net.HttpURLConnection)" :: URLConnection -> IO (ClassCastException | MutableIO HttpURLConnection)

private class ReaderCastTarget a where
    private downcastReader :: a -> IO (ClassCastException | Reader)

instance ReaderCastTarget InputStreamReader where
    native downcastReader "(java.io.Reader)" :: InputStreamReader -> IO (ClassCastException | Reader)

private data HttpURLConnection = native java.net.HttpURLConnection where
    native setRequestMethod :: Mutable RealWorld HttpURLConnection -> String -> IO () throws ProtocolException
    native getInputStream :: Mutable RealWorld HttpURLConnection -> IO InputStream throws IOException


private castConnection :: URLConnection -> IO (MutableIO HttpURLConnection)
private castConnection urlcon = downcastUrl urlcon >>= either
    (\cce -> error $ "cannot cast connection. Reason: " ++ show cce.getMessage)
    return

private castReader :: ReaderCastTarget a => a -> IO Reader
private castReader reader = downcastReader reader >>= either
    (\cce -> error $ "cannot cast reader. Reason: " ++ show cce.getMessage)
    return

This code contains the definitions of ClassCastException and ProtocolException. Below are the necessary class and instance definitions for the casts. Then follows the definition for HttpURLConnection, because that is not part of the Frege definitions. Finally, there are two functions for handling the casts and throwing errors, in case an Exception should be thrown. Frege, just like Haskell, doesn't have the concept of Exceptions. There are good ways to handle corresponding use cases, for instance with the Maybe monad. Throwing an error is a little bit of a dirty short cut, as it avoids the explicit error handling at the cost of ending the application in case of an error.

What is left, is the actual implementation of the download function, which only needs a few lines to write and can be read quite well, thanks to the preparatory work:

downloadPage :: String -> IO String
downloadPage urlString = do
    httpConnection <- castConnection =<< URL.openConnection =<< URL.new urlString
    httpConnection.setRequestMethod "GET"
    reader <- BufferedReader.new =<< castReader =<< flip InputStreamReader.new "UTF-8" =<< httpConnection.getInputStream
    webpage <- reader.getLines
    return $ unlines webpage

Final thoughts

Here and there it's still a little cumbersome to use Frege, compared to using Java. I say this, because of the missing definitions, that need to be defined for certain use cases. Taking a look into the Frege and FregeFX sources, there are a lot prepared definitions, which are still put in comments and therefor not yet available. I imagine this to be the result of a lot of special cases, due to the strict type system.

On the other hand, the strict type system helps preventing type errors. Writing stateless functional code also has it's charm, because it removes the necessity to keep track of the state of variables. Data in Frege is immutable, so one part of the code cannot manipulate data, which is processed in another part of the code.

Comparing Frege to Haskell, the most important thing for me is: I don't have the issues with Frege, that I had with Haskell in terms of using packages/libraries. Haskell has a lot of packages though and I guess it's nice, when it works. Frege builds on the JVM and Java libraries can be used in Frege. The downside is, it may be necessary to write the definitions yourself, because at the time of writing this article, Frege(FX) covers definitions of many parts of Java/JavaFX, but not all of it. To my current knowledge, there is also no definition source for, let's say, libraries from the Maven or Bintray repositories.

Overall, I suggest to give it a try. For me it is a challenging, great experience, that gives food for thought about software development.

Edit

I updated the code for the IO writing, above. In my project, I also refactored and extended the HTTP handling heavily. The current version is shown by the following code. It includes some basic handling for HTTP errors and exceptions during opening of the connection.

Careful: The HTTP status handling treats all codes >= 300 the same way ... with a simple retry after 5 seconds. That worked for my tests, but is very wrong! I display it here, to show the general approach on how to handle exceptions and different return codes.

import frege.java.Net

private data ClassCastException = pure native java.lang.ClassCastException

private data ProtocolException = pure native java.net.ProtocolException

private class URLCastTarget a where
    private downcastUrl :: URLConnection -> IO (ClassCastException | a)

instance URLCastTarget HttpURLConnection where
    native downcastUrl "(java.net.HttpURLConnection)" :: URLConnection -> IO (ClassCastException | HttpURLConnection)

private class ReaderCastTarget a where
    private downcastReader :: a -> IO (ClassCastException | Reader)

instance ReaderCastTarget InputStreamReader where
    native downcastReader "(java.io.Reader)" :: InputStreamReader -> IO (ClassCastException | Reader)

private data HttpURLConnection = mutable native java.net.HttpURLConnection where
    native setRequestMethod :: HttpURLConnection -> String -> IO () throws ProtocolException
    native getInputStream :: HttpURLConnection -> IO InputStream throws IOException
    native getResponseCode :: HttpURLConnection -> IO Int throws IOException


private castConnection :: URLConnection -> IO HttpURLConnection
private castConnection urlcon = downcastUrl urlcon >>= either
    (\cce -> error $ "cannot cast connection. Reason: " ++ show cce.getMessage)
    return

private castReader :: ReaderCastTarget a => a -> IO Reader
private castReader reader = downcastReader reader >>= either
    (\cce -> error $ "cannot cast reader. Reason: " ++ show cce.getMessage)
    return

getUrlString :: String -> String
getUrlString word = baseUrl ++ word

downloadPage :: String -> IO String
downloadPage urlString = do
    httpConnection <- castConnection =<< getConnection
    httpConnection.setRequestMethod "GET"
    responseCode <- httpConnection.getResponseCode
    if responseCode >= 200 && responseCode < 300
      then do
        reader <- BufferedReader.new =<< castReader =<< flip InputStreamReader.new "UTF-8" =<< httpConnection.getInputStream
        webpage <- reader.getLines
        return $ unlines webpage
      else do
        Thread.sleep 5000
        stderr.println $ "Error connection to " ++ urlString ++ " ... retrying"
        return =<< downloadPage urlString
  where getConnection :: IO URLConnection
        getConnection = (URL.new urlString >>= URL.openConnection)
            `catch` (\(ioe::IOException) -> (stderr.println $ ioe.getMessage) >> Thread.sleep 2000 >> getConnection)