Nibblesを0から始める（環境構築編）

## Nibbles とは

コードゴルフ言語の一種とのこと。GitHubは[こちら](https://github.com/darrenks/nibbles?tab=readme-ov-file)。

http://www.golfscript.com/nibbles/index.html

面白そうなのでAtCoderで使用してみようと思って調べてみたところ日本語のインストール手順などがほとんど見つけられなかったため、いい機会だと思い記事執筆に至りました。
せっかくなので自分がハマった点も含めて実行したままの手順をほぼそのまま記載しています。
手元の環境でエラーにならない箇所などは適宜飛ばして読んでいただければと思います。

## 環境

Macです。

Apple M1 チップ
macOS Ventura 13.5

シェルはzsh

## インストール手順

AtCoderで使用することを想定しているので、まずは[AtCoder公式](https://img.atcoder.jp/file/language-update/language-list.html)を見てみる。
AtCoderに記載の環境はUbuntuしかない。Ubuntu使用の場合は公式に記載されている手順実行するだけで良いのでは。

ただ私の環境はMacなので、Nibbles公式の方を確認する。
ただしどのバージョンをインストールすべきかをメモしておく。記事執筆時点では1.01。

このあとはNibbles公式の[Try It](http://www.golfscript.com/nibbles/try_it.html) を確認し、その手順に沿って進めていく。


### Haskellのインストール

> You will need GHC <b>8.0.2 - 8.10.7</b>. I recommend using ghcup to get it.

ghcupを使用するのを推奨すると書かれている。

https://www.haskell.org/ghcup/

書いてあるコマンドをそのままコピペ。

```shell-session
$ curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
```

```shell-session
Press ENTER to proceed or ctrl-c to abort.
Note that this script can be re-run at any given time.
```

最初に上記のような文言が出てくる。とりあえずEnterを押す。
その後いくつかクエスチョンが出てくる。

```shell-session
Detected zsh shell on your system...
Do you want ghcup to automatically add the required PATH variable to "/Users/[ユーザー名]/.zshrc"?

[P] Yes, prepend  [A] Yes, append  [N] No  [?] Help (default is "P").
```

PATHを.zshrcに追記するかどうかを聞いているように見える。
雰囲気で [A]。

```shell-session
Do you want to install haskell-language-server (HLS)?
HLS is a language-server that provides IDE-like functionality
and can integrate with different editors, such as Vim, Emacs, VS Code, Atom, ...
Also see https://haskell-language-server.readthedocs.io/en/stable/

[Y] Yes  [N] No  [?] Help (default is "N").
```

HaskellのLanguage Server をインストールするかどうか。
入れておいて困ることはないだろうと思って[Y]。

```shell-session
Do you want to enable better integration of stack with GHCup?
This means that stack won't install its own GHC versions, but uses GHCup's.
For more information see:
  https://docs.haskellstack.org/en/stable/yaml_configuration/#ghc-installation-customisation-experimental
If you want to keep stacks vanilla behavior, answer 'No'.

[Y] Yes  [N] No  [?] Help (default is "Y").
```
よくわからなかったのでとりあえずデフォルトの[Y]。

```shell-session
System requirements
  Note: On OS X, in the course of running ghcup you will be given a dialog box to install the command line tools. Accept and the requirements will be installed for you. You will then ne
ed to run the command again.
On Darwin M1 you might also need a working llvm installed (e.g. via brew) and have the toolchain exposed in PATH.
Press ENTER to proceed or ctrl-c to abort.
Installation may take a while.
```

これが出たらEnter。

```shell-session
===============================================================================

OK! /Users/[ユーザー名]/.zshrc has been modified. Restart your terminal for the changes to take effect,
or type ". /Users/[ユーザー名]/.ghcup/env" to apply them in your current terminal session.

===============================================================================

All done!

To start a simple repl, run:
  ghci

To start a new haskell project in the current directory, run:
  cabal init --interactive

To install other GHC versions and tools, run:
  ghcup tui

If you are new to Haskell, check out https://www.haskell.org/ghcup/steps/
```

これが出たら完了。
あとは最後の方に書いてあるコマンドをターミナルにコピペして実行する。

```shell-session
$ . /Users/[ユーザー名]/.ghcup/env
```

### Nibbles のインストール


ここで先ほどメモしておいたバージョンを[ダウンロード](http://www.golfscript.com/nibbles/downloads.html)してtgzファイルを解凍する。

ダウンロードフォルダのまま作業を進めるのがなんとなく嫌だったので、解凍済みのフォルダをホームディレクトリへ移動した。

```shell-session
$ mv ~/Downloads/nibbles ~/.
```

#### インストール失敗(1回目)

公式の手順に沿ってコマンド実行。

```shell-session
$ ghc -O -package ghc *.hs
[ 1 of 23] Compiling Data.DList.Internal ( Data/DList/Internal.hs, Data/DList/Internal.o, Data/DList/Internal.dyn_o )
[ 2 of 23] Compiling Data.DList       ( Data/DList.hs, Data/DList.o, Data/DList.dyn_o )
[ 3 of 23] Compiling Data.Digest.Murmur64 ( Data/Digest/Murmur64.hs, Data/Digest/Murmur64.o, Data/Digest/Murmur64.dyn_o )
[ 4 of 23] Compiling Data.Function.Memoize.Class ( Data/Function/Memoize/Class.hs, Data/Function/Memoize/Class.o, Data/Function/Memoize/Class.dyn_o )
[ 5 of 23] Compiling Data.Function.Memoize.TH ( Data/Function/Memoize/TH.hs, Data/Function/Memoize/TH.o, Data/Function/Memoize/TH.dyn_o )
[ 6 of 23] Compiling Data.Function.Memoize ( Data/Function/Memoize.hs, Data/Function/Memoize.o, Data/Function/Memoize.dyn_o )

Data/Function/Memoize.hs:176:16: error:
    • No instance for (Memoizable [Int])
        arising from a use of ‘memoize’
    • In the first argument of ‘(.)’, namely
        ‘memoize (f . fromIntBase)’
      In the expression: memoize (f . fromIntBase) . toIntBase
      In an equation for ‘memoize’:
          memoize f = memoize (f . fromIntBase) . toIntBase
    |
176 |    memoize f = memoize (f . fromIntBase) . toIntBase
    |                ^^^^^^^
[ 7 of 23] Compiling Data.List.Split.Internals ( Data/List/Split/Internals.hs, Data/List/Split/Internals.o, Data/List/Split/Internals.dyn_o )
[ 8 of 23] Compiling Data.List.Split  ( Data/List/Split.hs, Data/List/Split.o, Data/List/Split.dyn_o )
[ 9 of 23] Compiling FileQuoter       ( FileQuoter.hs, FileQuoter.o, FileQuoter.dyn_o )
[11 of 23] Compiling Hs               ( Hs.hs, Hs.o, Hs.dyn_o )
[12 of 23] Compiling SmartList        ( SmartList.hs, SmartList.o, SmartList.dyn_o )
[13 of 23] Compiling Types            ( Types.hs, Types.o, Types.dyn_o )
[14 of 23] Compiling Polylib          ( Polylib.hs, Polylib.o, Polylib.dyn_o )
[15 of 23] Compiling Expr             ( Expr.hs, Expr.o, Expr.dyn_o )

Expr.hs:3:1: error:
    Could not find module ‘State’
    Use -v (or `:set -v` in ghci) to see a list of the files searched for.
  |
3 | import State
  | ^^^^^^^^^^^^
```

明らかにエラーが発生している。
手順を再度確認していると、こんなものが目に入った。

> You will need GHC <b>8.0.2 - 8.10.7</b>.


GHCのバージョンを確認してみる。

```shell-session
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 9.4.8
```

なるほど。じゃあ8.10.7を入れよう。

#### GHCのバージョンを変える

```shell-session
$ ghcup install ghc 8.10.7
```

8.0.2 - 8.10.7ならどれでもいいと思われるが、とりあえずその中で最新のものを入れた。

```shell-session
[ Info  ] GHC installation successful
```
こんなのが表示されたら完了。
このままだとまだバージョンが変わっていないので、変更する。


```shell-session
$ ghcup set ghc 8.10.7
```


```shell-session
[ Info  ] GHC 8.10.7 successfully set as default version
```
こんなのが表示されたら完了。
バージョンを確認する。

```shell-session
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.10.7
```

よし、これでインストールできるはずだ。

#### インストール失敗(2回目)

さっきと同じように公式の手順に沿って作業。

```shell-session
$ ghc -O -package ghc *.hs
<no location info>: error:
    Warning: Couldn't figure out LLVM version!
             Make sure you have installed LLVM between [9 and 13)

<no location info>: error:
    Warning: Couldn't figure out LLVM version!
             Make sure you have installed LLVM between [9 and 13)

<no location info>: error:
    Warning: Couldn't figure out LLVM version!
             Make sure you have installed LLVM between [9 and 13)

<no location info>: error:
    Warning: Couldn't figure out LLVM version!
             Make sure you have installed LLVM between [9 and 13)
[ 1 of 22] Compiling Data.DList.Internal ( Data/DList/Internal.hs, Data/DList/Internal.o, Data/DList/Internal.dyn_o )

<no location info>: error:
    Warning: Couldn't figure out LLVM version!
             Make sure you have installed LLVM between [9 and 13)
ghc: could not execute: opt
```

明らかにエラーが発生している。

> Warning: Couldn't figure out LLVM version!

で検索するとこのようなissueが出てきた。

https://github.com/haskell/vscode-haskell/issues/667

このissue起票者のコメントに以下のようなものがあった。

> For that I had to do a `brew install llvm@12` and added `/opt/homebrew/opt/llvm@12/bin` to `PATH` (in `.zshrc`)

なるほど。これを読んで思ったのだが、私はそもそもllvmをインストールしていないような気がする。もしかしたらすでに入っているかもしれないが、とりあえずインストールしてみる。

```shell-session
$ brew install llvm@12
```
して
```shell-session
$ echo 'export PATH="/opt/homebrew/opt/llvm@12/bin:$PATH"' >> ~/.zshrc
```
して
```shell-session
$ source ~/.zshrc
```

これでどうですかね。


```shell-session
$ ghc -O -package ghc *.hs
<no location info>: error:
    Warning: Couldn't figure out LLVM version!
             Make sure you have installed LLVM between [9 and 13)

<no location info>: error:
    Warning: Couldn't figure out LLVM version!
             Make sure you have installed LLVM between [9 and 13)

<no location info>: error:
    Warning: Couldn't figure out LLVM version!
             Make sure you have installed LLVM between [9 and 13)

<no location info>: error:
    Warning: Couldn't figure out LLVM version!
             Make sure you have installed LLVM between [9 and 13)
[ 1 of 22] Compiling Data.DList.Internal ( Data/DList/Internal.hs, Data/DList/Internal.o, Data/DList/Internal.dyn_o )

<no location info>: error:
    Warning: Couldn't figure out LLVM version!
             Make sure you have installed LLVM between [9 and 13)
ghc: could not execute: opt
```

変わらないじゃん！

さっきのissueではみんなパスのことを話していた。
`.zshrc`を見てみる。

```.zshrc
... 中略 ...
[ -f "/Users/[ユーザー名]/.ghcup/env" ] && . "/Users/[ユーザー名]/.ghcup/env" # ghcup-envexport PATH="/opt/homebrew/opt/llvm@12/bin:$PATH"
```

おかしなことが起きていた。

```.zshrc
... 中略 ...
[ -f "/Users/[ユーザー名]/.ghcup/env" ] && . "/Users/[ユーザー名]/.ghcup/env" # ghcup-env
export PATH="/opt/homebrew/opt/llvm@12/bin:$PATH"
```

これで保存して再度`source ~/.zshrc`する

```shell-session
$ source ~/.zshrc
```

#### インストール成功

```shell-session
$ ghc -O -package ghc *.hs
[ 1 of 22] Compiling Data.DList.Internal ( Data/DList/Internal.hs, Data/DList/Internal.o, Data/DList/Internal.dyn_o )
[ 2 of 22] Compiling Data.DList       ( Data/DList.hs, Data/DList.o, Data/DList.dyn_o )
[ 3 of 22] Compiling Data.Digest.Murmur64 ( Data/Digest/Murmur64.hs, Data/Digest/Murmur64.o, Data/Digest/Murmur64.dyn_o )
[ 4 of 22] Compiling Data.Function.Memoize.Class ( Data/Function/Memoize/Class.hs, Data/Function/Memoize/Class.o, Data/Function/Memoize/Class.dyn_o )
[ 5 of 22] Compiling Data.Function.Memoize.TH ( Data/Function/Memoize/TH.hs, Data/Function/Memoize/TH.o, Data/Function/Memoize/TH.dyn_o )
[ 6 of 22] Compiling Data.Function.Memoize ( Data/Function/Memoize.hs, Data/Function/Memoize.o, Data/Function/Memoize.dyn_o )
[ 7 of 22] Compiling Data.List.Split.Internals ( Data/List/Split/Internals.hs, Data/List/Split/Internals.o, Data/List/Split/Internals.dyn_o )
[ 8 of 22] Compiling Data.List.Split  ( Data/List/Split.hs, Data/List/Split.o, Data/List/Split.dyn_o )
[ 9 of 22] Compiling FileQuoter       ( FileQuoter.hs, FileQuoter.o, FileQuoter.dyn_o )
[10 of 22] Compiling Header           ( Header.hs, Header.o, Header.dyn_o )
[11 of 22] Compiling Hs               ( Hs.hs, Hs.o, Hs.dyn_o )
[12 of 22] Compiling SmartList        ( SmartList.hs, SmartList.o, SmartList.dyn_o )
[13 of 22] Compiling Types            ( Types.hs, Types.o, Types.dyn_o )
[14 of 22] Compiling Polylib          ( Polylib.hs, Polylib.o, Polylib.dyn_o )
[15 of 22] Compiling Expr             ( Expr.hs, Expr.o, Expr.dyn_o )
[16 of 22] Compiling Parse            ( Parse.hs, Parse.o, Parse.dyn_o )
[17 of 22] Compiling ParseArgs        ( ParseArgs.hs, ParseArgs.o, ParseArgs.dyn_o )
[18 of 22] Compiling OpsHelper        ( OpsHelper.hs, OpsHelper.o, OpsHelper.dyn_o )
[19 of 22] Compiling Args             ( Args.hs, Args.o, Args.dyn_o )
[20 of 22] Compiling Ops              ( Ops.hs, Ops.o, Ops.dyn_o )
[21 of 22] Compiling Compile          ( Compile.hs, Compile.o, Compile.dyn_o )
[22 of 22] Compiling Main             ( nibbles.hs, nibbles.o, nibbles.dyn_o )
Linking nibbles ...
```
できたっぽい。

公式にはこの後にこのように書いてある。

> This creates the `nibbles` binary. You will need to run nibbles code from this directory unless you install the libraries below.

次に書いてるライブラリをインストールすれば、このディレクトリ以外でも実行できるよということか。



### ライブラリのインストール

書いてある通り実行した。

```shell-session
$ rm -r Data

$ cabal install --lib dlist split murmur-hash memoize
Resolving dependencies...
Build profile: -w ghc-8.10.7 -O1
In order, the following will be built (use -v for more details):
 - dlist-1.0 (lib) (requires download & build)
 - memoize-1.1.2 (lib) (requires download & build)
 - murmur-hash-0.1.0.10 (lib) (requires download & build)
 - split-0.2.5 (lib) (requires download & build)
Downloading  split-0.2.5
Downloaded   split-0.2.5
Downloading  murmur-hash-0.1.0.10
Starting     split-0.2.5 (lib)
Downloaded   murmur-hash-0.1.0.10
Downloading  dlist-1.0
Starting     murmur-hash-0.1.0.10 (lib)
Downloaded   dlist-1.0
Downloading  memoize-1.1.2
Starting     dlist-1.0 (lib)
Building     split-0.2.5 (lib)
Building     murmur-hash-0.1.0.10 (lib)
Building     dlist-1.0 (lib)
Downloaded   memoize-1.1.2
Starting     memoize-1.1.2 (lib)
Installing   murmur-hash-0.1.0.10 (lib)
Completed    murmur-hash-0.1.0.10 (lib)
Building     memoize-1.1.2 (lib)
Installing   split-0.2.5 (lib)
Completed    split-0.2.5 (lib)
Installing   dlist-1.0 (lib)
Completed    dlist-1.0 (lib)
Installing   memoize-1.1.2 (lib)
Completed    memoize-1.1.2 (lib)
```

うまくいったっぽい。


## プログラム実行

nibblesディレクトリを見ていたところ、testディレクトリの中に`integration_test.nbl`というファイルを発見した。
手始めにこれを実行してみることにした。

```shell-session
$  nibbles integration_test.nbl
zsh: command not found: nibbles
```

そうですか。。。


### .zshrcに追記

なんとなく原因の想像がついたので`.zshrc`に以下を追記

```.zshrc
export PATH="/Users/[ユーザー名]/nibbles:$PATH"
```

`source ~/.zshrc`する。

```shell-session
$ source ~/.zshrc
```

### 再度実行

```shell-session
$ nibbles integration_test.nbl
size = 17 nibbles (8.5 bytes)

out.hs:13:1: error:
    Could not load module ‘Data.Set’
    It is a member of the hidden package ‘containers-0.6.5.1’.
    You can run ‘:set -package containers’ to expose it.
    (Note: this unloads all the modules in the current scope.)
    Use -v (or `:set -v` in ghci) to see a list of the files searched for.
   |
13 | import qualified Data.Set as Set
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

out.hs:19:1: error:
    Could not load module ‘Data.ByteString’
    It is a member of the hidden package ‘bytestring-0.10.12.0’.
    You can run ‘:set -package bytestring’ to expose it.
    (Note: this unloads all the modules in the current scope.)
    Use -v (or `:set -v` in ghci) to see a list of the files searched for.
   |
19 | import qualified Data.ByteString as B8
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
nibbles: failed to compile hs (likely an internal nibbles bug, please report it!)
CallStack (from HasCallStack):
  error, called at nibbles.hs:163:24 in main:Main
  ```

moduleが無いらしい。

### 不足しているライブラリをインストール

バージョン指定してインストールする。

```shell-session
$ cabal install --lib containers-0.6.5.1
Resolving dependencies...

$ cabal install --lib bytestring-0.10.12.0
Resolving dependencies...
```

### 実行成功

再度実行してみる。

```shell-session
$ nibbles integration_test.nbl
size = 17 nibbles (8.5 bytes)
a♯b
```

おお、うまくいってるっぽいぞ！


## nibblesディレクトリ以外で実行

[この工程](#ライブラリのインストール)を踏んでいるので、nibblesディレクトリ以外でも実行できるはず。
ホームディレクトリに`nibbles_test`ディレクトリを作成して、`intro.nbl`ファイルを作成する。

```shell-session
$ cd ~

$ mkdir nibbles_test

$ cd nibbles_test

$ touch intro.nbl
```

`intro.nbl`の中身は[チュートリアル](http://www.golfscript.com/nibbles/tutorial_basics.html)の最初の内容で以下の通りにした。

```intro.nbl
+2 1
```

### 実行

```
$ nibbles intro.nbl
size = 5 nibbles (2.5 bytes)
3
```

チュートリアルと見比べてみる。


> size = 5 nibbles (3 bytes)
3

なんか少し違う。。。けどこんなことが書いてあった。

> A file appears named intro.nbb (nbb is the file extension for binary Nibbles programs) which has a size of 3 bytes. That compactification was not as good as the promised divide by 2... but it would have been 2.5 bytes if we were measuring that way (and small numbers are a bad example). FYI you can automatically expand .nbb files back to literate form using -e.

許されたことにしておく。
ひとまず環境構築は完了。

## 終わりに

最初のプログラムが実行できるようになるまでに結構手こずった。
この後は構文など試してみてみる&AtCoderの問題を解いてみようと思う。