Stackでやる最速Haskell Hello world! (GHCのインストール付き!)

  • 149
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

昨日第23回Haskellもくもく会 @ 朝日ネットで初めてstackを触ったのですが、
あまりにも簡単・高速にパッケージ作りの準備ができたので、やったことを共有したいと思います。
GHC(Haskellの最も有名なコンパイラ)のインストールまでやってくれるので、これからHaskell始めます!みたいな人にもオススメです。

stack自体が何かは @tanakh さんのHaskellのビルドツール"stack"の紹介をご覧ください。
一言で言うと「GHCのインストールからパッケージのインストールにビルドまで、ワンストップでやってくれる神ツール」ですかね。

インストール

各OSごとに微妙に違うみたいなので、公式ページの解説をご覧ください。
とはいえ、基本的には実行ファイルをPATHのどこかに置くだけなので、この手の作業に慣れている人には朝飯前でしょう。
1ファイルだけでインストールできちゃうのも魅力ですね!

:warning: すでにcabalをお使いの方へ

くれぐれもcabal install stackしないでください。
あれはstack自身の開発者向けのパッケージです。
cabalの難しいところをカバーするためにstackがあるのですから、cabalのことは一旦忘れましょう。

プロジェクトディレクトリーの作成

stack newコマンドで適当なプロジェクト名を指定すれば、適当なプロジェクトテンプレートが入ったディレクトリーを作ってくれます。
第2引数にテンプレートの名前を指定すればもっとよいものを提供してくれるみたいですが、今回はとりあえず簡単に。

$ stack new foobar-project
Downloading template "new-template" to create project "foobar-project" in foobar-project/ ...
Using the following authorship configuration:
author-email: example@example.com
author-name: Example Author Name
Copy these to /home/yu/.stack/stack.yaml and edit to use different values.
Writing default config file to: /home/yu/tmp/foobar-project/stack.yaml
Basing on cabal files:
- /home/yu/tmp/foobar-project/foobar-project.cabal

Checking against build plan lts-3.2
Selected resolver: lts-3.2
Wrote project config to: /home/yu/tmp/foobar-project/stack.yaml

$ cd foobar-project/
$ ls
LICENSE  Setup.hs  app  foobar-project.cabal  src  stack.yaml  test

とにかくビルドしましょう

こちらのテンプレート、何もしなくてもとりあえずすでにビルドできる状態になっています。
文字通りstack buildを実行してください。

$ stack build
No GHC found, expected version 7.10.2 (x86_64) (based on resolver setting in /home/michael/helloworld/stack.yaml).
Try running stack setup

おっと、「何もしなくても」はさすがに大袈裟でした。 :sweat:
上記のように、ここで必要なバージョンのGHCがインストールされていない場合、エラーになります。
@tanakhさんの記事の時点ではstack buildだけで自動的にインストールまでしてくれる仕様だったようですが、あまりにもいきなりすぎると判断したんでしょう。
とはいえ、ちゃんとエラーメッセージにstack setupしてくれ、と親切にも書いてくださっているので、素直に実行しましょう。
自動的に最新安定版のGHC 7.10.2が入ります。

$ stack setup
Downloaded ghc-7.10.2.
... 省略 ...
Installed GHC.
stack will use a locally installed GHC
For more information on paths, see 'stack path' and 'stack exec env'
To use this GHC and packages outside of a project, consider using:
stack ghc, stack ghci, stack runghc, or stack exec

ネットワークの状況にも依存するとは思いますが、GHCのインストールはやっぱり結構時間がかかりました。
待ちましょう。 :sleeping:

インストールが終わったら、気を取り直してstack build

foobar-project-0.1.0.0: configure
Configuring foobar-project-0.1.0.0...
foobar-project-0.1.0.0: build
Preprocessing library foobar-project-0.1.0.0...
[1 of 1] Compiling Lib              ( src/Lib.hs, .stack-work/dist/x86_64-linux/Cabal-1.22.4.0/build/Lib.o )
In-place registering foobar-project-0.1.0.0...
Preprocessing executable 'foobar-project-exe' for foobar-project-0.1.0.0...
[1 of 1] Compiling Main             ( app/Main.hs, .stack-work/dist/x86_64-linux/Cabal-1.22.4.0/build/foobar-project-exe/foobar-project-exe-tmp/Main.o )
Linking .stack-work/dist/x86_64-linux/Cabal-1.22.4.0/build/foobar-project-exe/foobar-project-exe ...
foobar-project-0.1.0.0: install
Installing library in
/home/yu/tmp/foobar-project/.stack-work/install/x86_64-linux/lts-3.2/7.10.2/lib/x86_64-linux-ghc-7.10.2/foobar-project-0.1.0.0-E05fmeQhYiF0vVxMlsqLP6
Installing executable(s) in
/home/yu/tmp/foobar-project/.stack-work/install/x86_64-linux/lts-3.2/7.10.2/bin
Registering foobar-project-0.1.0.0...

さぁ実行だ!

stack execを使えば、プロジェクトの設定(stack.yaml)毎に空気を読んでPATHを適切に設定してくれます。
デフォルトのテンプレートでビルドした実行ファイルは<プロジェクト名>-exeという名前になってますので、
今回の場合はstack exec foobar-project-exeしましょう。

$ stack exec foobar-project-exe
someFunc

...お、惜しい。Hello, world!じゃない... :cry:
せっかくなんでプロジェクトの構造を確認しつつ、Hello, world!を出すよう編集しましょう。
お好きなエディタでapp/Main.hsを開いてください。

app/Main.hs
module Main where

import Lib

main :: IO ()
main = someFunc

ここではHaskell自体の詳細は割愛しますが、このapp/Main.hsmainという関数が、stack exec foobar-project-exeした時に最初に実行される関数です。
この場合、Libというパッケージから(暗黙に)importされた、someFuncという関数を呼んでいます。

それではLibsomeFuncを覗いてみましょう。src/Lib.hsにその正体があります。

src/Lib.hs
module Lib
    ( someFunc
    ) where

someFunc :: IO ()
someFunc = putStrLn "someFunc"

さっきstack exec foobar-exeした時に出力されたsomeFuncという文字列はここにありました。
どうやらputStrLnという関数で指定した文字列を出力していたようですね。
ここを我らが(?)Hello, world!に書き換えましょう。それだけです。

src/Lib.hs
module Lib
    ( someFunc
    ) where

someFunc :: IO ()
someFunc = putStrLn "Hello, world!"

後はもう一度stack buildして実行するだけ!

$ stack build
... 省略 ...
$ stack exec foobar-exe
Hello, world!

できました!これであなたもHaskellerです! :white_check_mark:

この先はどうする?

あまりにもstackが簡単だったので勢いで導入を書いてしまいました。
誰か入門を考えてください...。 :anguished:
すごいH本は最初ghciから入るので、この記事の入門では不向きなのです...。

残りは気になったら読んでください。

他のパッケージを入れたい時は?

例えばおなじみ「すごいH本」を読み進めたら出てくる、Data.Mapimportしたくなったとしましょう。

src/Lib.hs
module Lib
    ( someFunc
    ) where

import qualified Data.Map as M

someMap :: M.Map String String
someMap = M.fromList [("stack exec", "Hello, world!")]

someFunc :: IO ()
someFunc = print $ M.lookup "stack exec" someMap

すごいH本には「最初から入っているから大丈夫だよ〜」と書いてありますが、ここで紹介したテンプレートで使用しようとすると、残念ながらそのままではエラーになってしまいます。

$ stack build
foobar-project-0.1.0.0-42e96c77117d3dac75f022f842817f66: unregistering (local file changes)
foobar-project-0.1.0.0: build
Preprocessing library foobar-project-0.1.0.0...

/home/yu/tmp/foobar-project/src/Lib.hs:5:18:
    Could not find module ‘Data.Map’
    It is a member of the hidden package ‘containers-0.5.6.2@conta_LKCPrTJwOTOLk4OU37YmeN’.
    Perhaps you need to add ‘containers’ to the build-depends in your .cabal file.
    Use -v to see a list of the files searched for.

ここでstack...ではなく、stackがラップしているパッケージマネージャー、cabal(これまでHaskellを使ってきた方にはおなじみですね)に、
Data.Mapが入ってるcontainersっていうパッケージを使いたいんだけど!!」と教えてあげましょう。
foobar-project.cabalというファイル(通称cabalファイルと呼びます)を開いてください。
こんな↓感じのファイルになっています。

foobar-project.cabal
name:                foobar-project
version:             0.1.0.0

... 省略 ...

library
  hs-source-dirs:      src
  exposed-modules:     Lib
  build-depends:       base >= 4.7 && < 5
  default-language:    Haskell2010

... 省略 ...

上記のbuild-dependsと書かれた行の下に、次のように追記してください。

foobar-project.cabal
name:                foobar-project
version:             0.1.0.0

... 省略 ...

library
  hs-source-dirs:      src
  exposed-modules:     Lib
  build-depends:       base >= 4.7 && < 5
                     , containers -- <= ココ!
  default-language:    Haskell2010

... 省略 ...

上のbase >= 4.7 && < 5と書かれた行のように、特にバージョン番号を指定する必要はないようです。
stack自身が用意したlts-3.2というバージョンのStackage(安定してビルドできるバージョンを記録したパッケージ集)から適切なバージョンを選んでくれるからでしょう。
自前でバージョン指定を考えて首をひねるのは、もう過去の出来事です 1

さて、foobar-project.cabalを編集したら、もう一度stack buildするだけで、新しいパッケージをとってきてくれます。

$ stack build
... 省略 ...
$ stack exec foobar-project-
Just "Hello, world!"

これでカンペキ! :laughing:

もっと他のパッケージを入れたいときは?

冒頭でも触れた、@tanakhさんの記事をご覧ください。

foobar-project-exe とか実行ファイルの名前ダサすぎなんですけど? :rage2:

こちらもstackがラップしている、cabalの設定によります。foobar-project.cabalを開いて...

foobar-project.cabal
name:                foobar
version:             0.1.0.0
... 省略 ...

library
  ... 省略 ...

executable foobar-project-exe
  hs-source-dirs:      app
  main-is:             Main.hs
... 省略 ...

下記のように、libraryの下の方にあるexecutable foobar-project-exeと書かれた行を、executable <好きな実行ファイル名>に変え、stack buildし直してください。
Windows向けに拡張子.exeは特に言わなくても付けてくれたはず。

foobar-project.cabal
name:                foobar
version:             0.1.0.0
... 省略 ...

library
  ... 省略 ...

executable your-cool-command -- <= ココ!
  hs-source-dirs:      app
  main-is:             Main.hs
... 省略 ...

インストールしたものはどこに行くの?

まだまだ激しく開発中のツールなので今後仕様が変わるかもですが、私の環境ではこんな感じの場所にインストールされました。
だいたいみなさんのホームディレクトリーや%USERPROFILE%\AppData\Local以下あたりのそれっぽいディレクトリーに読み替えていただければ見つかるでしょう。

  • stack installした、プロジェクト固有の実行ファイル: ~/.local/bin/
  • stack setupでインストールしたGHC 7.10.2の実行ファイル: ~/.stack/programs/x86_64-linux/ghc-7.10.2/bin/

必要に応じてこれらもPATHに追加しちゃいましょう。

気が向いたら調べて記事にする

  • プロジェクトディレクトリーを作らないでとりあえずGHC一式だけ欲しい時の方法。
    • 何にもないディレクトリーでstack setupするだけでとりあえずインストールされるっぽい。PATHの設定はお好みで。
  • デフォルトのテンプレートに入っているユーザー名やメールアドレスを変える方法。

  1. もちろん、そうやってできるのはStackageをメンテされているみなさんがいらっしゃるからなのです。感謝しましょう。