Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

昨日第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 1.6.1未満を使っている場合のエラー

:warning: 手元で試したところ、stackのバージョンが1.5.1などのバージョンの場合、下記のAesonExceptionが起こるようです。

$ stack new hoge
Downloading template "new-template" to create project "hoge" in hoge\ ...
Initialized empty Git repository in C:/Users/igrep/Downloads/tmp/hoge/.git/
Looking for .cabal or package.yaml files to use to init the project.
Using cabal packages:
- hoge\hoge.cabal

Selecting the best among 12 snapshots...

Downloaded lts-10.0 build plan.
AesonException "Error in $.packages.cassava.constraints.flags['bytestring--lt-0_10_4']: Invalid flag name: \"bytestring--lt-0_10_4\""

問題のIssueでSnoyman氏が述べているようにstack upgradeしたり、(Chocolateyでstackを入れた場合は)choco upgrade haskell-stackするなどして、stackのバージョンを上げてください。
あるいは、ちょっと試せてないのですが、事情があってstackのアップデートができない場合、stack --resolver=lts-9.20のように古いLTS Haskellを指定するという方法でもよいかも知れません(古いバージョンのGHCがインストールされてしまうので、推奨はしませんが)。

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

こちらのテンプレート、何もしなくてもとりあえずすでにビルドできる状態になっています。
文字通り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が入ります。

:pencil: 2017/12/09 追記: まだ手元で試せてはいないのですが、Stack 1.6.1から、デフォルトで、stack buildしただけでGHCのインストールも行うようになったそうです。なのでstack setupは不要になった模様です。

$ 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ファイルと呼びます)... ではなく、package.yamlというファイルを開いてください。

:warning: 2017/12/23 追記: どうやら、stack newした場合のデフォルトのテンプレートpackage.yamlが含まれるようになったようです。これによって、cabalファイルを編集しても、stack buildした時点で書き換えられてしまいます。詳しくはこの記事へのコメントとその回答をご覧ください。

こんな↓感じのファイルになっています。

package.yaml
name:                foobar-project
version:             0.1.0.0

... 省略 ...

dependencies:
- base >= 4.7 && < 5

library:
  source-dirs: src

... 省略 ...

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

package.yaml
name:                foobar-project
version:             0.1.0.0

... 省略 ...

dependencies:
- base >= 4.7 && < 5
- containers # <= ココ!

... 省略 ...

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

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

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

これでカンペキ! :laughing:

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

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

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

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

package.yaml
  ... 省略 ...

executables:
  foobar-project-exe:
    main:                Main.hs
    source-dirs:         app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - foobar-project

... 省略 ...

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

package.yaml
  ... 省略 ...

executables:
  your-cool-command: # <- ココ!
    main:                Main.hs
    source-dirs:         app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - foobar-project

... 省略 ...

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

まだまだ激しく開発中のツールなので今後仕様が変わるかもですが、私の環境ではこんな感じの場所にインストールされました。
だいたいみなさんのホームディレクトリーや%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をメンテされているみなさんがいらっしゃるからなのです。感謝しましょう。それから、Hackageにアップロードする予定のプロジェクトでは、せめてlower boundくらいは書いた方が親切かと思います(私自身もちゃんとできてませんが...) 

igrep
Haskellとプリキュアが好き。よく眠り、よくコードを書きたい。 今はHaskell関係の記事は https://haskell.jp/blog に書いております。
http://the.igreque.info/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away