有没有人知道一个快速启动的Haskell解释器,它适合用于编写shell脚本?使用Hugs运行'hello world'在我的旧笔记本电脑上运行了400ms,在我目前的Thinkpad X300上运行了300ms。这对于瞬时响应来说太慢了。与GHCi的时代相似。
功能语言不一定要慢:Objective Caml和Moscow ML在1ms或更短时间内运行hello world。
澄清:我是GHC的重要用户,我知道如何使用GHCi。我知道所有关于编译以使事情变得快速的事情。解析成本应该完全不相关:如果ML和OCaml的启动速度比GHCi快300倍,那么还有改进的余地。
我正在寻找
与其他解释器相媲美的性能,包括
等简单程序的快速启动和执行module Main where
main = print 33
我不正在为更严肃的程序寻找编译性能。重点是看看Haskell是否对脚本有用。
答案 0 :(得分:31)
答案 1 :(得分:8)
使用ghc -e
几乎等同于调用ghci
。我相信GHC的runhaskell
在运行之前将代码编译为临时可执行文件,而不是像ghc -e
/ ghci
那样解释它,但我不是100%肯定。
$ time echo 'Hello, world!'
Hello, world!
real 0m0.021s
user 0m0.000s
sys 0m0.000s
$ time ghc -e 'putStrLn "Hello, world!"'
Hello, world!
real 0m0.401s
user 0m0.031s
sys 0m0.015s
$ echo 'main = putStrLn "Hello, world!"' > hw.hs
$ time runhaskell hw.hs
Hello, world!
real 0m0.335s
user 0m0.015s
sys 0m0.015s
$ time ghc --make hw
[1 of 1] Compiling Main ( hw.hs, hw.o )
Linking hw ...
real 0m0.855s
user 0m0.015s
sys 0m0.015s
$ time ./hw
Hello, world!
real 0m0.037s
user 0m0.015s
sys 0m0.000s
在运行之前简单地编译所有“脚本”有多难?
啊,为多种架构提供二进制文件确实很痛苦。我之前走过那条路,这并不是很有趣......
可悲的是,我认为不可能更好地使任何Haskell编译器的启动开销。语言的声明性意味着,在尝试进行类型检查之前,有必要首先阅读整个程序,从不执行任何操作,然后您要么承受严格性分析或不必要的懒惰和thunking的成本。
流行的'脚本'语言(shell,Perl,Python等)和基于ML的语言只需要一次通过......好吧,ML需要静态类型检查传递,而Perl有这个有趣的5通道方案(其中两个反向运行);无论哪种方式,程序化都意味着编译器/解释器可以更容易地将程序的各个部分组合在一起。
简而言之,我认为不可能比这更好。我没有测试过Hugs或GHCi是否有更快的启动,但是任何差异仍然远离非Haskell语言。
答案 2 :(得分:5)
如果您真的关心速度,那么每次启动时重新解析代码都会受到阻碍。 Haskell不需要从解释器运行,用GHC编译它就可以获得出色的性能。
答案 3 :(得分:4)
这个问题分为两部分:
如果你关心表现,唯一严肃的选择是GHC,这是非常快的:http://shootout.alioth.debian.org/u64q/benchmark.php?test=all&lang=all
如果你想为Unix脚本编写一些简单的东西,我会使用GHCi。它比Hugs快约30倍,但也支持所有hackage库。
现在安装GHC(并免费获得GHCi)。
答案 4 :(得分:3)
如果有一个ghci守护进程和一个接受脚本路径和位置的馈送脚本,与已经运行的ghci进程通信以在正确的目录中加载和执行脚本并将输出传递回stdout的feeder脚本?
不幸的是,我不知道如何写这样的东西,但似乎它可以通过以下的速度来判断:l在ghci中。因为看起来runhaskell的大部分成本都是在启动ghci,而不是解析和运行脚本。
编辑:经过一番游戏,我发现Hint package(GHC API的包装)在这里非常有用。以下代码将加载传入的模块名称(此处假定位于同一目录中)并将执行main函数。现在'所有'剩下的就是让它成为一个守护进程,让它接受管道或套接字上的脚本。
import Language.Haskell.Interpreter
import Control.Monad
run = runInterpreter . test
test :: String -> Interpreter ()
test mname =
do
loadModules [mname ++ ".hs"]
setTopLevelModules [mname]
res <- interpret "main" (as :: IO())
liftIO res
Edit2:就stdout / err / in go而言,使用this specific GHC trick看起来可以将客户端程序的标准重定向到进纸器程序,然后进入一些命名管道(可能),守护程序始终连接到该管道,然后将守护程序的stdout返回到进程程序正在侦听的另一个命名管道。伪示例:
grep ... | feeder my_script.hs | xargs ...
| ^---------------- <
V |
named pipe -> daemon -> named pipe
这里的馈线是一个小编译的线束程序,只需将std的重定向到守护进程中,然后从守护进程中退出,并将该脚本的名称和位置提供给守护进程。