Haskell 编程入门

简介: 在过去的几个月里,学习Haskell让我觉得非常快乐,但是入门的过程并没有我原先想象的那么简单。我非常幸运地在一个正确的地方工作,并且因此能够在Facebook参加Bryan O'Sullivan的Haskell课程。在Try Haskell上玩了一段时间后,最终你就会想要在自己的电脑上安装GHC了。

在过去的几个月里,学习Haskell让我觉得非常快乐,但是入门的过程并没有我原先想象的那么简单。我非常幸运地在一个正确的地方工作,并且因此能够在Facebook参加Bryan O'Sullivan的Haskell课程。在Try Haskell上玩了一段时间后,最终你就会想要在自己的电脑上安装GHC了。

image.png

设置Cabal

Cabal是Haskell用于构建应用程序和库的公共架构。和其配套使用的工具包是Hackage,作用则类似于Perl的CPAN,Python的pip或者Ruby的gem。你可能对其表示失望,但是它也不至于糟糕。

当最终安装完cabal包时,默认会把它安装到 ~/.cabal/目录,脚本则安装到 ~/.cabal/bin目录下。紧接着,还要添加环境路径。以下设置即可满足,但也可以按照你自己喜好来设置shell的profile:

echo'export PATH=$HOME/.cabal/bin:$PATH'>> ~/.bashrc

在使用cabal工作之前,你还需要更新可用包列表。这只是偶尔是使用到而已,特别在安装或者更新新的包时。

cabal update

这时,~/.cabal/config 文件的library-profiling 选项还没开启。如果你现在不开启,以后再开启则可能需要重新编译所有的文件。开启此选项,需要编辑~/.cabal/config文件,并且设置--library-profiling: Falsetolibrary-profiling: True。

$forfin ~/.cabal/config;

do

 cp $f$f.old && sed-E's/(-- )?(library-profiling: )False/\2True/'< $f.old > $f;

done

在安装其他东西前,还需要安装 Calbal installer:

cabal install cabal-install


安装ghc-mod(更好的Emacs/Vim的支持)

ghc-mod是在Emacs和Vim中集成GHC的环境.你也许能够从SublimeHaskell中使用Sublime

Text2和ghc-mod.我至今只试过在Emacs中使用过它.Vim用户可以尝试hdevtools,它编译更加迅速并且同样准确(参考kamatsu's comment).

$ cabal install ghc-mod

显然你必须为你的Emacs配置它,并且我会将我现在的 ~/.emacs.d留给你做参考.


安装 Cabal-dev(构建沙盒的工具)

Cabal-dev是一个能帮助你安装Haskell软件的工具。它类似于Python 的 virtualenv或者Ruby的rvm,但是使用上有些不同。Cabal-dev 能让你免于“Calbal Hell”(即你现在安装的包和你之前安装的包存在依赖冲突)的烦恼。

尽量使用calbal-dev 代替仅仅使用cabal进行编译工作。其主要代价是花费多一点时间编译那些已经安装在某处的包(和浪费一些磁盘空间),不过这也是相当划算的了。

使用源码编译和安装Cabal-dev:

如果你想尝试一些工具,但是并不想污染你的haskell环境,你可以使用cabal-dev。cabal-dev默认的沙盒在 ./cabal-dev,但是你可以把它放到任何地方。下面的例子中, 我将安装 darcs 2.8.2 (一个haskell写的版本控制系统)到/usr/local/Cellar/darcs/2.8.2中,并使用Homebrew创建符号链接。在其他平台你可能希望使用自己指定的路径和PATH环境变量。

$ cabal-dev install -s /usr/local/Cellar/darcs/2.8.2 darcs-2.8.2

$ brew link --overwrite darcs

OK!现在darcs已经在你的PATH中了,而且你并不需要担心版本冲突。好吧,你没办法完全摆脱它们,但是没有以前那么多了。特别注意,cabal-dev会将所有包安装在沙盒顶层目录。这意味着如果两个包有着相同的依赖关系(完全相同),它们会互相破坏它们内部指向依赖的包内的文件的符号链接,例如发布协议文件和文档。这样使用--overwrite可能没有什么损失,但是你最好先用--overwrite --dry-run选项检查一番。有些麻烦,但是不会毁掉你一整天的成果。

如果你想看看你所安装的darcs的版本,使用cabal info darcs。

其他有意思的haskell写的工具(不分先后):

  • pandoc - 一个像瑞士军刀一样的标记语言转换器(例如,markdown, reStructuredText, org-mode, LaTeX)
  • gitit - 使用git, darcs或者mercurical作仓库的wiki
  • pronk - 一个HTTP负载测试工具,像ab或者httperf,只是相比更现代和易用


配置GHCi

GHCi是GHC交互式解释器的缩写。更详细的文档请参考GHC用户指南第二章:使用GHCi)。你将在编码过程中花上很长一段时间,你大概想要设置一个更短的提示符。它刚开始的时候看起来像这样:

Prelude>

一旦你开始引入模块,提示符将不断变长。真的,你一生都不需要这么长的提示符。

Prelude>:m+Data.List

Prelude Data.List>:m+Data.Maybe

Prelude Data.List Data.Maybe>

配置文件是.ghci。我用一个非常简单的ASC II提示符,而另一些人喜欢把提示符设置成诸如"λ>"这样。

echo':set prompt "h> "'>> ~/.ghci

Hello World:

$ ghci

h>putStrLn"Hello World!"

Hello World!

h>

官方对Hackage支持还很不足,但其也有非官方镜像.

令人遗憾的是,目前Hackage还不是很稳定。我不知道什么原因造成的,但希望他们能尽早做点什么。Hackage有其工作区,你可以使用hdiff.luite.com或者hackage.csc.stanford.edu的仓库。

修改 ~/.cabal/config 文件行为:

remote-repo: hackage.haskell.org: http://hackage.haskell.org/packages/archive

变成这样:

--TODOWhen hackage is back up, set back to hackage.haskell.org!

-- remote-repo: hackage.haskell.org: http://hackage.haskell.org/packages/archive

remote-repo: hdiff.luite.com: http://hdiff.luite.com/packages/archive

-- remote-repo: hackage.csc.stanford.edu: http://hackage.scs.stanford.edu/packages/archive

当你完成修改远程仓库的设置后,你将需要更新包列表:

cabal update

不要忘记后续把它还原回来!


开始一个项目(使用cabal-dev)

你最终会明白如何去做,但是一个快速的方法是直接使用cabal-dev。下面告诉你开始一个简单的程序。

对于你自己的项目,你可能想要去掉-n标志,让cabal-dev询问你你想要的选项。-n会使用所有默认的选项而没有提示。

$ mkdir -p ~/src/hs-hello-world

$ cd ~/src/hs-hello-world

$ touch LICENSE

$ cabal init -n --is-executable

这回生成一个Setup.hs文件和hs-hello-world.cabal文件。下面需要修改main-is这行来说明你要从哪个文件编译你的可执行文件。最终的文件可能像这样。

hs-hello-world.cabal

-- Initial hs-hello-world.cabal generated by cabal init.  For further

-- documentation, see http://haskell.org/cabal/users-guide/

name:               hs-hello-world

version:            0.1.0.0

-- synopsis:          

-- description:        

license:            AllRightsReserved

license-file:       LICENSE

-- author:            

-- maintainer:        

-- copyright:          

-- category:          

build-type:         Simple

cabal-version:      >=1.8

executable hs-hello-world

 main-is:            HelloWorld.hs

 -- other-modules:      

 build-depends:      base ==4.5.*

然后创建一个HelloWorld.hs,可能像这样:


HelloWorld.hs

main :: IO()

main = putStrLn "Hello, world!"

你可以编译并安装它到一个本地的沙盒:

$ cabal-dev install

Resolving dependencies...

Configuring hs-hello-world-0.1.0.0...

Building hs-hello-world-0.1.0.0...

Preprocessing executable'hs-hello-world'forhs-hello-world-0.1.0.0...

Installing executable(s)in/Users/bob/src/hs-hello-world/cabal-dev//bin

Installed hs-hello-world-0.1.0.0

$ ./cabal-dev/bin/hs-hello-world

Hello, world!

生成的文件比较大,因为他是静态连接的。你可以复制它到任何处理器架构和操作系统相同的机器上,它可以直接运行。

你可以跳过安装步骤,这样可能节省一点时间:

$ cabal-dev configure

Resolving dependencies...

Configuring hs-hello-world-0.1.0.0...

$ cabal-dev build

Building hs-hello-world-0.1.0.0...

Preprocessing executable'hs-hello-world'forhs-hello-world-0.1.0.0...

[1 of 1] Compiling Main             ( HelloWorld.hs, dist/build/hs-hello-world/hs-hello-world-tmp/Main.o )

Linking dist/build/hs-hello-world/hs-hello-world...

$ ./dist/build/hs-hello-world/hs-hello-world

Hello, world!

既然这个项目没有什么依赖,你可跳过一些步骤。

可以解释执行它,不需要编译:

$ runghc HelloWorld.hs

Hello, world!

$ ghci

GHCi, version7.4.2: http://www.haskell.org/ghc/ :? for help

Loading package ghc-prim...linking...done.

Loading package integer-gmp...linking...done.

Loading package base...linking...done.

Prelude>:load HelloWorld

[1of1] Compiling Main             ( HelloWorld.hs, interpreted )

Ok, modules loaded: Main.

*Main>main

Hello, world!

你甚至不需要用cabal-dev(cabal)编译它:

$ runghc Setup.hs configure

Configuring hs-hello-world-0.1.0.0...

$ runghc Setup.hs build

Building hs-hello-world-0.1.0.0...

Preprocessing executable'hs-hello-world'forhs-hello-world-0.1.0.0...

[1 of 1] Compiling Main             ( HelloWorld.hs, dist/build/hs-hello-world/hs-hello-world-tmp/Main.o )

Linking dist/build/hs-hello-world/hs-hello-world...

但是对一些复杂的项目,你可以使用cabal-dev ghci(在cabal-dev configure &&

cabal-dev build之后)。注意它会自动装载你的可执行文件到解释器里:

$ cabal-dev ghci

on the commandline:

   Warning:-O conflicts with--interactive;-O ignored.

GHCi, version7.4.2: http://www.haskell.org/ghc/ :? for help

Loading package ghc-prim...linking...done.

Loading package integer-gmp...linking...done.

Loading package base...linking...done.

Ok, modules loaded: Main.

h> main

Hello, world!

GHCi基本使用

更多你想知道的GHCi用法可以在Chapter 2. Using GHCi找到。


****:t 显示一个表达式的类型签名

λ >> :t main

main :: IO ()

λ >> :t map

map :: (a -> b) -> [a] -> [b]

λ >> :t map (+1)

map (+1) :: Num b => [b] -> [b]

:i 显示一个符号的信息(函数,typeclass,类型等等)

λ >> :i Num

class Num a where

 (+) :: a -> a -> a

 (*) :: a -> a -> a

 (-) :: a -> a -> a

 negate :: a -> a

 abs :: a -> a

 signum :: a -> a

 fromInteger :: Integer -> a

   -- Defined in `GHC.Num'

instance Num Integer -- Defined in `GHC.Num'

instance Num Int -- Defined in `GHC.Num'

instance Num Float -- Defined in `GHC.Float'

instance Num Double -- Defined in `GHC.Float'

λ >> :info map

map :: (a -> b) -> [a] -> [b]  -- Defined in `GHC.Base'

λ >> :info Int

data Int = GHC.Types.I# GHC.Prim.Int#   -- Defined in `GHC.Types'

instance Bounded Int -- Defined in `GHC.Enum'

instance Enum Int -- Defined in `GHC.Enum'

instance Eq Int -- Defined in `GHC.Classes'

instance Integral Int -- Defined in `GHC.Real'

instance Num Int -- Defined in `GHC.Num'

instance Ord Int -- Defined in `GHC.Classes'

instance Read Int -- Defined in `GHC.Read'

instance Real Int -- Defined in `GHC.Real'

instance Show Int -- Defined in `GHC.Show'

:m 增加一个模块

λ >> :m +Data.List

λ >> sort [10, 9..1]

[1,2,3,4,5,6,7,8,9,10]


:l 导入一个模块,:r 重新导入模块

λ >> :! echo 'hello = print "hello"' > Hello.hs

λ >> :l Hello

[1 of 1] Compiling Main             ( Hello.hs, interpreted )

Ok, modules loaded: Main.

λ >> hello

"hello"

λ >> :! echo 'hello = print "HELLO"' > Hello.hs

λ >> :r

[1 of 1] Compiling Main             ( Hello.hs, interpreted )

Ok, modules loaded: Main.

λ >> hello

"HELLO"

相关文章
|
移动开发 JavaScript 前端开发
如何处理 h5 使用 iframe 嵌套页面,内外 viewport 不一致导致的缩放问题?
如何处理 h5 使用 iframe 嵌套页面,内外 viewport 不一致导致的缩放问题?
1971 0
|
安全 Java 数据库
SpringSecurity 入门
Spring Security是Spring采用 `AOP`思想,基于 `servlet过滤器`实现的安全框架。它提供了完善的**认证机制**和**方法级的授权功能**。是一款非常优秀的权限管理框架。
185 0
|
供应链 小程序 物联网
B2B2C、C2F、S2B2b2C、O2O、S2B2C和各种的模式缩写解释说明
B2B2C、C2F、S2B2b2C、O2O、S2B2C和各种的模式缩写解释说明
3115 0
B2B2C、C2F、S2B2b2C、O2O、S2B2C和各种的模式缩写解释说明
|
8月前
|
人工智能 物联网 Python
VMix:即插即用!字节联合中科大推出增强模型生成美学质量的开源适配器,支持多源输入、高质量视频处理
VMix 是一款创新的即插即用美学适配器,通过解耦文本提示和交叉注意力混合控制,显著提升图像生成的美学质量,支持多源输入和高质量视频处理。
299 11
VMix:即插即用!字节联合中科大推出增强模型生成美学质量的开源适配器,支持多源输入、高质量视频处理
|
9月前
「Mac畅玩鸿蒙与硬件43」UI互动应用篇20 - 闪烁按钮效果
本篇将带你实现一个带有闪烁动画的按钮交互效果。通过动态改变按钮颜色,用户可以在视觉上感受到按钮的闪烁效果,提升界面互动体验。
211 19
「Mac畅玩鸿蒙与硬件43」UI互动应用篇20 - 闪烁按钮效果
|
SQL JSON 监控
使用 SPL 高效实现 Flink SLS Connector 下推
SLS 推出了 SPL 语言,可以高效的对日志数据的清洗,加工。对 SPL 及 SPL 在阿里云 Flink SLS Connector 中应用进行介绍及举例。
56338 239
|
网络协议 Linux Windows
有了这个iPerf小工具,测试UDP方便多了。
有了这个iPerf小工具,测试UDP方便多了。
583 1
|
JavaScript Java 关系型数据库
springboot+vue餐厅点餐系统(源码+文档)
这是一篇关于基于SpringBoot的餐厅点餐系统的介绍。该系统由Java开发者风歌分享,提供完整的源码。项目支持多种Java框架,包括SSM和SpringBoot,适用于Java毕设项目。开发环境包括Java 1.8、MySQL 5.7+、Node.js、Vue等。系统架构包括管理员、用户和食堂三个角色的功能模块,涵盖用户注册登录、食堂管理、菜单管理、订单处理、消息留言、留言板和系统管理等全面功能。文章还展示了部分界面截图。如有需要,可联系作者获取更多详情和源码。
|
存储 关系型数据库 MySQL
表设计的10条军规
本文主要介绍了数据库建表的18个小技巧,包括:名字的命名规范、字段类型的选取、字段长度的控制、外键的使用、索引的创建、主键的选择、字段个数的限制、存储引擎的选择、时间字段的处理、金额字段的保存、冗余字段的使用以及注释的添加。作者强调了命名的重要性,如使用小写字母、避免全大写、使用下划线分隔等,并提倡使用NOT NULL和默认值,合理选择字段类型如datetime、decimal等,以及避免使用过多的字段和索引。此外,还提到了字符集和排序规则的选择,以及大字段和冗余字段的处理。
334 1
|
Web App开发 前端开发 测试技术
【如何学习Python自动化测试】—— 页面元素定位
【如何学习Python自动化测试】—— 页面元素定位
441 1