我正在为一个新项目寻找一种语言。它是基于 Web 的项目,我想采用 REST 架构。
我还想要一种函数式编程语言。我可以在 Haskell(因为它很酷)和 Scala(因为 Play Framework)之间做出选择。
经过几次研究以找出这与语言之间的主要区别后,我发现了 Frege,一种在 JVM 上运行的类似 Haskell 的语言。
所以我的问题是,因为 Frege 在 JVM 上运行,是否可以将 Play 框架与 Frege 一起使用?
我正在为一个新项目寻找一种语言。它是基于 Web 的项目,我想采用 REST 架构。
我还想要一种函数式编程语言。我可以在 Haskell(因为它很酷)和 Scala(因为 Play Framework)之间做出选择。
经过几次研究以找出这与语言之间的主要区别后,我发现了 Frege,一种在 JVM 上运行的类似 Haskell 的语言。
所以我的问题是,因为 Frege 在 JVM 上运行,是否可以将 Play 框架与 Frege 一起使用?
这是一个简单的应用程序来演示我们如何在 Play 中使用 Frege。由于 Play 支持 Java,因此使用 Frege 的 Java API 实际上非常容易,即使我们还没有对 Frege 的原生 Play 支持。该应用程序基本上是 JSON 输入和 JSON 输出。Frege 程序从 JSON 请求中读取参数,POST
并以向用户致意的 JSON 响应进行响应。
播放conf/routes
:
POST /greet helloplay.FregeApplication.greet()
现在弗雷格中的实际“业务逻辑”:
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()))
);
}
}
在这里,我们定义了 2 种类型,分别对应于请求和响应以及 JSON 转换,用于具有FromJSON
和ToJSON
类型类实例的那些。该webMain
函数接受 play并从 JSON 请求Request
中读取数据,并返回包装在 play 中的 JSON 响应。该函数是为 Play 控制器提供实现的函数。播放控制器是一个扩展 Play 的类。我们可以通过声明一个内部的 Frege 源文件来使 Frege 模块扩展一个 Java 类。该函数也是一个 IO 操作,因此我们必须在某个时候评估发生的事情,这就是底部 Controller 中的 Java 方法通过调用 Frege 所做的name
Result.Status
webMain
play.mvc.Controller
native module
webMain
ST.performUnsafe
然后从可能的 thunk 中强制结果,否则应用程序只会预热 CPU :)
这里的类型和函数都来自 Play 框架Request
,所以我们必须为 Frege 添加原生绑定,提及它们的纯度或可能的 null 值等。这是一件好事,因为 Frege 是一种纯语言并且没有的概念,我们必须明确提及副作用或编译器可能的 null 值,这与 Scala 不同,您可以调用任何 Java 方法。ResultStatus
PlayController
ok
badRequest
null
Maybe
Play 的 Frege 原生绑定:
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
这就对了!我们可以运行它:
activator run
接着
$ curl --header "Content-type: application/json" --request POST --data '{"name": "PlayFrege"}' http://localhost:9000/greet
{"message" : "Hello, PlayFrege"}
Frege 有一个SBT 插件,可用于在 Play 项目中编译 Frege 源代码。如果有人想尝试一下,我已经在Github中推送了这个示例应用程序。