I'm searching a language for a new project. It's web based project and I want to adopt a REST architecture.
I also want a function programming language. I have the choice between Haskell (because it's cool) and Scala (because of the Play Framework).
After few researches to find out the main differences between this to languages, I have found Frege, an Haskell like language running on the JVM.
So my question is, because Frege is running on the JVM, is it possible to use Play framework with Frege ?
Here is a simple application to demonstrate how we can use Frege with Play. Since Play supports Java, it is actually very easy to use the Java API from Frege even though we don't have native Play support for Frege yet. The application is basically JSON-in and JSON-out. A Frege program reads a parameter from a JSON POST
request and responds with a JSON response that greets the user.
Play conf/routes
:
POST /greet helloplay.FregeApplication.greet()
Now the actual "business logic" in Frege:
module helloplay.FregeApplication where
import Data.JSON
import helloplay.Play
data GreetingRequest = GreetingRequest { name :: String }
data GreetingResponse = GreetingResponse { message :: String }
instance FromJSON GreetingRequest where
fromJSON (Struct fs) = do
name <- field "name" fs
pure $ GreetingRequest name
fromJSON invalid = fail ("Invalid JSON for Greeting Request: " ++ show invalid)
instance ToJSON GreetingResponse where
toJSON (GreetingResponse message) = Struct [ assoc "message" message ]
greet :: GreetingRequest -> GreetingResponse
greet request = GreetingResponse $ "Hello, " ++ request.name
webMain :: Request -> IO ResultStatus
webMain request = do
let jsonRequest = parseJSON request.body.asJson.toString
return $ either badRequest (ok . show . toJSON . greet) jsonRequest
{-
- This makes the Frege module extend Play Controller class so that it can be configured to handle a route.
-}
native module type PlayController where {
public static play.mvc.Result greet() {
return frege.runtime.Delayed.forced(
frege.prelude.PreludeBase.TST.performUnsafe(webMain(request()))
);
}
}
Here we have defined 2 types corresponding to request and response and JSON conversions for those with FromJSON
and ToJSON
type class instances. The webMain
function takes the play Request
and reads name
from JSON request and returns a JSON response wrapped in play's Result.Status
.
The webMain
function is the one which provides an implementation for a Play controller. A play controller is a class which extends Play's play.mvc.Controller
. We can make a Frege module extend a Java class by declaring a native module
inside Frege source file. The webMain
function is also an IO action so we have to evaluate at some point for something to happen which is what the Java method in the Controller at the bottom does by invoking Frege's ST.performUnsafe
and then forcing the result from a possible thunk otherwise the application would just warm up the CPU :)
Here the types like Request
, ResultStatus
, PlayController
and functions like ok
, badRequest
are all from Play framework so we have to add native bindings for Frege mentioning their purity or possible null values etc. This is a good thing as Frege is a pure language and doesn't have a notion of null
, we have to explicitly mention side effects or Maybe
for possible null values for the compiler unlike Scala where you can just call any Java methods.
The Frege native bindings for Play:
module helloplay.Play where
data PlayController = native play.mvc.Controller
data Result = pure native play.mvc.Result
data ResultStatus = pure native play.mvc.Results.Status
pure native badRequest play.mvc.Results.badRequest :: String -> ResultStatus
data Request = pure native play.mvc.Http.Request where
pure native body :: Request -> RequestBody
data RequestBody = pure native play.mvc.Http.RequestBody where
pure native asText :: RequestBody -> String
pure native asJson :: RequestBody -> JsonNode
data JsonNode = pure native com.fasterxml.jackson.databind.JsonNode where
pure native asText :: JsonNode -> String
pure native toString :: JsonNode -> String
pure native ok play.mvc.Results.ok :: String -> ResultStatus
That is it! We can run it with:
activator run
and then
$ curl --header "Content-type: application/json" --request POST --data '{"name": "PlayFrege"}' http://localhost:9000/greet
{"message" : "Hello, PlayFrege"}
Frege has an SBT plugin that can be used to compile Frege sources in a Play project. I have pushed this sample application in Github if anyone wants to try it out.