Performance 是否有适合脚本编写的快速入门Haskell解释器?

Performance 是否有适合脚本编写的快速入门Haskell解释器?,performance,scripting,haskell,interpreter,Performance,Scripting,Haskell,Interpreter,有谁知道一个快速启动的Haskell解释器适合用于编写shell脚本吗?使用Hugs运行“hello world”在我的旧笔记本电脑上需要400毫秒,在我当前的Thinkpad X300上需要300毫秒。这对于即时响应来说太慢了。与GHCi的时间相似 函数式语言不必太慢:Objective Caml和Moscow ML都能在1ms或更短的时间内运行hello world 澄清:我是GHC的忠实用户,我知道如何使用GHCi。我知道所有关于快速编译的事情。解析成本应该完全无关:如果ML和OCaml的

有谁知道一个快速启动的Haskell解释器适合用于编写shell脚本吗?使用Hugs运行“hello world”在我的旧笔记本电脑上需要400毫秒,在我当前的Thinkpad X300上需要300毫秒。这对于即时响应来说太慢了。与GHCi的时间相似

函数式语言不必太慢:Objective Caml和Moscow ML都能在1ms或更短的时间内运行hello world

澄清:我是GHC的忠实用户,我知道如何使用GHCi。我知道所有关于快速编译的事情。解析成本应该完全无关:如果ML和OCaml的启动速度比GHCi快300倍,那么还有改进的余地

我在找

  • 脚本编写的便利性:一个源文件,没有二进制代码,所有平台上都运行相同的代码
  • 性能与其他解释器相当,包括快速启动和执行简单的程序,如

    module Main where
    main = print 33
    

我不是在为更严肃的程序寻找编译性能。关键是看Haskell是否对脚本编写有用。

如果您真的关心速度,那么每次启动都会重新解析代码,这将阻碍您的工作。Haskell不需要从解释器运行,使用编译它,您应该会获得优异的性能。

这个问题有两个部分:

  • 你在乎表现吗
  • 你想要脚本吗
如果您关心性能,唯一重要的选择是GHC,它非常快:

如果您希望Unix脚本编写简单,我会使用GHCi。它比Hugs快约30倍,但也支持hackage上的所有库


因此,现在就安装GHC(并免费获得GHCi)。

使用
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编译器的启动开销变得更好。这种语言的声明性质意味着,即使在尝试打字检查任何内容之前,也必须先阅读整个程序,而不必执行任何内容,然后你要么要承受严格分析的代价,要么要承受不必要的懒惰和重击

流行的“脚本”语言(shell、Perl、Python等)和基于ML的语言只需要一次传递。。。好吧,ML需要一个静态类型检查过程,Perl有一个有趣的5过程方案(其中两个运行相反);无论哪种方式,程序化意味着编译器/解释器可以更轻松地将程序的各个部分组装在一起


简言之,我认为不可能比这更好。我还没有测试过Hugs或GHCi是否有更快的启动速度,但与非Haskell语言相比,faaar还有任何不同。

为什么不创建一个脚本前端来编译脚本呢?如果以前没有,或者编译的版本已经过时了

这是基本的想法,这段代码可以改进很多——搜索路径而不是假设所有内容都在同一个目录中,更好地处理其他文件扩展名,等等。此外,我对haskell编码(ghc编译脚本.hs)非常熟悉:


让一个ghci守护进程和一个feeder脚本获取脚本路径和位置,与已经运行的ghci进程通信,以便在适当的目录中加载和执行脚本,并将输出返回给stdout的feeder脚本,怎么样

不幸的是,我不知道如何写这样的东西,但从ghci中的:l的速度来看,它似乎真的很快。runhaskell的大部分成本似乎是启动ghci,而不是解析和运行脚本

编辑:经过一些尝试,我发现(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
import Language.Haskell.解释器
进口管制
运行=运行解释器。测试
测试::字符串->解释器()
测试mname=
做
loadModules[mname++“.hs”]
setTopLevelModules[mname]
res守护进程->命名管道

这里的feeder是一个小的已编译的线束程序,只需将std重定向到守护进程中,然后再从守护进程中返回,并将脚本的名称和位置提供给守护进程。

我不太关心总体性能,而是关心启动成本。“非常非常非常快”?别激动,唐---我在GHC的后端工作过,我知道那些特殊的尸体埋在哪里:-)嘿,诺曼。我真的认为除了编译脚本(如果它们经常运行)或使用#之外,没有太多的选择/usr/bin/runhaskell语法,如果它们很少运行。也就是说,hugs启动得更快。在我看来,这不是很有用。我使用三种不同的计算平台:x86、amd64和sparc。为这三种语言编译代码的差异是很重要的。我用Lua和ksh编写了很多脚本,我更喜欢用Haskell。但如果我必须编译所有的东西,就不会了。你的计时准确地显示了我的麻烦,除了我的“回声”比你的快得多。。。奇怪的是,这最初是在Cygwin下进行的,在Cygwin中(因为Windows是死板的)进程创建非常昂贵。是的,我没有看到一个简单的解决方案(轻量级+二进制可移植)Haskell“shell”脚本。。。对不起,解析要花很多钱
#! ghc-compiled-script

import Data.List
import System
import System.Environment

main = do
  args <- getArgs
  putStrLn $ foldl (++) "" $ intersperse " " args
$ time hs-echo.hs "Hello, world\!"     
[1 of 1] Compiling Main             ( hs-echo.hs, hs-echo.o )
Linking hs-echo ...
Hello, world!
hs-echo.hs "Hello, world!"  0.83s user 0.21s system 97% cpu 1.062 total

$ time hs-echo.hs "Hello, world, again\!"
Hello, world, again!
hs-echo.hs "Hello, world, again!"  0.01s user 0.00s system 60% cpu 0.022 total
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
grep ... | feeder my_script.hs | xargs ...
            |   ^---------------- <
            V                      |
         named pipe -> daemon -> named pipe