Haskell

来自百合仙子's Wiki
跳转到导航 跳转到搜索

懒惰求值与严格求值

在捕获 I/O 异常时,由于懒惰求值,异常可能无法捕获,如:

import Control.Exception (catch, SomeException)

main = readFile "/proc/1/smaps" `catch` handler >>= putStrLn
  where handler :: SomeException -> IO String
        handler e = print e >> return []

而像这样则可以:

import Control.Exception (catch, SomeException)

main = (readFile "/proc/1/smaps" >>= putStrLn) `catch` handler
  where handler :: SomeException -> IO ()
        handler e = print e >> return ()

可能的解决办法是使用 strict 包:

import Prelude hiding (readFile)
import System.IO.Strict (readFile)

-- ...

[1]

GHC

GHC(Glasgow Haskell Compiler)是一个 Haskell 编译器。

参数

-dynamic
使用动态链接,能有效减小生成的可执行文件大小,但是依赖 GHC 的库。[2]
-hide-package=xxx
屏蔽某个库。在多个库冲突时有用。
-C
生成 C 源文件 (.hc)

ghc 也接受一些 gcc 接受的参数。

文件

除非给出-ignore-dot-ghci,会尝试读取并执行以下文件:./.ghci%appdata%/ghc/ghci.conf$HOME/.ghc/ghci.conf(Unix 系统)、$HOME/.ghci

链接

静态链接

[3]

ghc -static -optl-pthread -optl-static test.hs

要部分静态链接,使用-v参数编译,然后重新执行最后一条命令,但是在相应的-l部分之前添加-Wl,-static,之后添加-Wl,-dy[4]

有可能还需要使用-keep-tmp-files -tmpdir .来保留临时文件。

动态链接

要生成动态链接库,使用-shared -fPIC -dynamic选项。

要将多个.dyn_o合并成一个.dyn_a,可使用命令:

ar vr libHS<NAME>-<VERSION>.dyn_a `find . -name '*.dyn_o' ! -name '*__*'`

要将多个.dyn_a合并成一个.so,可使用命令:

/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/collect2 --whole-archive --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=both -shared -o libName.so -h libSNAME.so /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/../../../../lib/crti.o /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/crtbeginS.o -L/usr/lib/ghc-7.0.3/base-4.3.1.0 -L/usr/lib/ghc-7.0.3/integer-gmp-0.2.0.3 -L/usr/lib/ghc-7.0.3/ghc-prim-0.2.0.0 -L/usr/lib/ghc-7.0.3 -L/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2 -L/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/../../.. -Bsymbolic libHShoogle-4.2.8.dyn_a /home/lilydjwg/src/haskell/haskell-haskell-xmpp/src/haskell-xmpp-1.0/dist/build/libHShaskell-xmpp-1.0.dyn_a --no-whole-archive -rpath /usr/lib/ghc-7.0.3/base-4.3.1.0 -rpath /usr/lib/ghc-7.0.3/integer-gmp-0.2.0.3 -rpath /usr/lib/ghc-7.0.3/ghc-prim-0.2.0.0 -rpath /usr/lib/ghc-7.0.3 -lHSbase-4.3.1.0-ghc7.0.3 -lHSinteger-gmp-0.2.0.3-ghc7.0.3 -lgmp -lHSghc-prim-0.2.0.0-ghc7.0.3 -lHSffi-ghc7.0.3 <MORE_SO_HERE> -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/crtendS.o /usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/../../../../lib/crtn.o

如果要将此共享库链接到更多的共享库上,对于每个 Haskell 库需要同时提供-rpath -L -l三种选项。

线程

默认在 Linux 下,GHC 会使用自己实现的线程。在此情况下,System.Posix.Unistd.sleep会阻塞整个程序中的所有线程(包括信号处理,因为它会在新的线程中执行;使用Control.Concurrent.threadDelay更好)。[5]使用-threaded参数来使用操作系统的线程。

编写动态链接库

Haskell 代码Test.hs

{-# LANGUAGE ForeignFunctionInterface #-}
module Test where
 
import Foreign.C.Types
 
hsfun :: CInt -> IO CInt
hsfun x = do
    putStrLn "Hello World"
    return (42 + x)
 
foreign export ccall
    hsfun :: CInt -> IO CInt

辅助程序module_init.c

#define CAT(a,b) XCAT(a,b)
#define XCAT(a,b) a ## b
#define STR(a) XSTR(a)
#define XSTR(a) #a
 
#include <HsFFI.h>
 
extern void CAT(__stginit_, MODULE)(void);
 
static void library_init(void) __attribute__((constructor));
static void library_init(void) {
    /* This seems to be a no-op, but it makes the GHCRTS envvar work. */
    static char *argv[] = { STR(MODULE) ".so", 0 }, **argv_ = argv;
    static int argc = 1;
 
    hs_init(&argc, &argv_);
    hs_add_root(CAT(__stginit_, MODULE));
}
 
static void library_exit(void) __attribute__((destructor));
static void library_exit(void) {
    hs_exit();
}

编译之:

ghc -O2 --make \
      -no-hs-main -optl '-shared' -optc '-DMODULE=Test' \
      -o Test.so Test.hs module_init.c

测试一下:

#include <stdio.h>
#include <dlfcn.h>
void test(void) {
    void *dl = dlopen("./Test.so", RTLD_LAZY);
    printf("%d\n", ((int(*)(int)) dlsym(dl, "hsfun"))(5));
    dlclose(dl);
}
int main(int argc, char *argv[]) {
    test(); test(); test();
    return 0;
}

Makefile 规则:

%.so: %.hs
        $(HC) $(HCFLAGS) --make \
                -no-hs-main -optl '-shared' -optc '-DMODULE='$(*F) \
                -o $@ $< module_init.c

[6] [7]

profiling

使用 GHC 编译时使用-O -rtsopts开启此功能[8],运行时添加参数+RTS -s -RTS,程序即可在退出时输出 profiling 信息。-RTS是用于结束 RTS 参数的,-s用于打印内存使用统计信息。[9]

cabal

编译静态链接的可执行文件:

cabal install shellcheck --ghc-options='-optl-static -optl-pthread'

参见

外部链接

教程

文档

博客文章

工具

软件包

参考资料