Haskell
懒惰求值与严格求值
在捕获 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)
-- ...
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。
链接
静态链接
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
profiling
使用 GHC 编译时使用-O -rtsopts开启此功能[8],运行时添加参数+RTS -s -RTS,程序即可在退出时输出 profiling 信息。-RTS是用于结束 RTS 参数的,-s用于打印内存使用统计信息。[9]
cabal
编译静态链接的可执行文件:
cabal install shellcheck --ghc-options='-optl-static -optl-pthread'
参见
外部链接
- Haskell Amuse Bouche, slides by Mark Lentczner
教程
- Learn You a Haskell for Great Good!,中文翻译:Haskell趣学指南,Haskell趣學指南 (Learn You a Haskell for Great Good! 繁體中文版)
- Real World Haskell
- Parallel and Concurrent Programming in Haskell
- functional programming - How to learn Haskell - Stack Overflow
- enumerator: simple, efficient incremental IO for Haskell
- Enumerator Package
- 学习Haskell - lele's Blog,相关学习资源收集
- APIO讲稿——函数式编程 « Beyond the Void
- YBlog - Haskell web programming,yesod 入门
- Yesod Web Framework Book
文档
博客文章
- 惰性求值和尾递归
- 对单子的求索 - # emerge -e world
- Haskell与范畴论
- Hello Haskell, Goodbye Lisp | Lost in Technopolis
- Making Haskell programs faster and smaller
- Mort | 【译文】Haskell可以和C一样快(个案研究)已失效,存档
- Why I prefer Scheme to Haskell
- Repeat after me: “Cabal is not a Package Manager” | «Insert Name Here»
- osa1 - Loading dynamic Haskell libs in Lua
- Higher-kinded fun in Haskell - Sparx Blog
- Covariance and Contravariance
工具
- GHC 和 cabal 的 Zsh 命令补全:[1]
- lpaste—Haskell Pastebin,能够对代码进行建议
软件包
参考资料
- ↑ Making Haskell programs faster and smaller
- ↑ linker - Making small haskell executables? - Stack Overflow
- ↑ How to compile Haskell to a static library? - Stack Overflow
- ↑ New-to-Haskell, i.e. elementary, Haskell questions; extended discussions
- ↑ 参见 Haskell 代码片断#信号处理。
- ↑ Haskell.cz blog » Blog Archive » Building a shared library in Haskell
- ↑ PythonVsHaskell - PythonInfo Wiki
- ↑ Chapter 5. Profiling
- ↑ Performance