我正在尝试学习 Haskell 中的语句是如何工作的。我试图制作一个非常简单的程序,您可以在其中调用 REST 端点并执行系统命令(非常简单,例如“ls”) 问题在于在单个 do 语句中组合不同的操作类型。
import Web.Scotty
import System.Cmd
main = do
putStrLn "Starting Server"
scotty 3000 $ do
get "/url" $ do
system "ls"
text "Success"
但我收到下一个编译器错误:
Main.hs:12:7:
Couldn't match expected type ‘Web.Scotty.Internal.Types.ActionT
Data.Text.Internal.Lazy.Text IO a0’
with actual type ‘IO GHC.IO.Exception.ExitCode’
In a stmt of a 'do' block: system "ls"
In the second argument of ‘($)’, namely
‘do { system "ls";
text "Success" }’
我很难学习 Haskell!
最佳答案
在 Haskell 中, do
-notation用于链接类似语句的东西。语句是某种类型的构造函数,如 IO
应用于特定的结果类型。例如,语句 system "ls"
有类型 IO ExitCode
.
除 IO
之外的其他类型构造函数可以作为语句使用。所有这些 do
-notation 要求类型构造函数实现 Monad
interface这解释了如何明智地链接语句。
然而,内单do
-block,只允许一种类型的语句!他们必须都是 IO
声明,或所有 ActionT Text IO
声明。在您的示例中,您将两者混合在一起,这会导致错误。 Scotty's get
函数需要一个 ActionT Text IO
陈述:
get :: RoutePattern -> ActionM () -> ScottyM ()
-- ActionM is actually a synonym for ActionT Text IO
好消息是有一种方法可以转换(通常的 Haskell 术语是“提升”)
IO
声明成ActionT Text IO
声明。后者实际上是 IO
之上的一种“装饰器”(通常的 Haskell 术语是 "monad transformer" )操作,启用与 Scotty 相关的额外功能。您可以“举”IO
使用 liftIO
进入装饰器的操作函数,像这样:get "/url" $ do
liftIO (system "ls")
text "Success"
一般什么时候可以用
liftIO
举起平原 IO
语句变成“装饰”语句? “装饰器”类型构造函数必须有 MonadIO
除了通常的实例 Monad
实例。 MonadIO
是什么提供了 liftIO
功能。在我们的例子中,查看
ActionT
的可用实例:(MonadIO m, ScottyError e) => MonadIO (ActionT e m)
这意味着类似“如果
m
有一个 MonadIO
实例——就像 IO
一样——而错误类型 e
有一个 ScottyError
实例——就像 Text
那样——然后我们可以提升 |1046 | 声明到 IO
声明”。以及
ActionT e m
的专用类型是:liftIO :: IO a -> ActionT Text IO a
关于haskell - 如何在 Scotty Action 中使用 System.command,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58670103/