有没有人知道一个快速启动的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是否对脚本有用.
为什么不创建一个脚本前端来编译脚本(如果它以前没有,或者编译后的版本已经过时).
这是基本的想法,这个代码可以进行很多改进 - 搜索路径而不是假设所有内容都在同一个目录中,更好地处理其他文件扩展名等.另外,我在haskell编码时非常环保(ghc-compiled-script. HS):
import Control.Monad import System import System.Directory import System.IO import System.Posix.Files import System.Posix.Process import System.Process getMTime f = getFileStatus f >>= return . modificationTime main = do scr : args <- getArgs let cscr = takeWhile (/= '.') scr scrExists <- doesFileExist scr cscrExists <- doesFileExist cscr compile <- if scrExists && cscrExists then do scrMTime <- getMTime scr cscrMTime <- getMTime cscr return $ cscrMTime <= scrMTime else return True when compile $ do r <- system $ "ghc --make " ++ scr case r of ExitFailure i -> do hPutStrLn stderr $ "'ghc --make " ++ scr ++ "' failed: " ++ show i exitFailure ExitSuccess -> return () executeFile cscr False args Nothing
现在我们可以创建这样的脚本(hs-echo.hs):
#! 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
使用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语言.
如果您真的关心速度,那么每次启动时重新解析代码都会受到阻碍.Haskell不需要从解释器运行,用GHC编译它就可以获得出色的性能.