Closed19

Haskell 入門者 (→ 競プロスクラップへ)

toyboot4etoyboot4e

プロジェクトの用意

古い方法でもいいので、拙速に行きます!

コンパイラ

ひとまず ghc を入れます。 ghcup がある そうですが、情報収集は後回しにします。

$ brew install ghc

ビルドツール

今回は stack を入れてみます:

$ brew install stack
$ stack upgrade
$ stack setup

stack コマンドの使い方は quick start guidetldr, man で調べます。

stack プロジェクト作成

リポジトリを作ります:

$ stack new rogue-hs
$ cd rogue-hs
$ git init
$ gh repo create

最初にやること:

$ git add .gitignore
$ git commit -m 'initial commit'
$ license mit > LICENSE
$ git add LICENSE
$ git commit -m 'add LICENSE'
toyboot4etoyboot4e

Hello, world!

stack プロジェクトの確認

設定ファイルが謎です。

$ fd -d 1 | as-tree
.
├── ChangeLog.md
├── LICENSE
├── README.md
├── Setup.hs
├── app # 実行ファイルのソース
├── package.yaml
├── rogue-hs.cabal
├── src # ライブラリのソース
├── stack.yaml
├── stack.yaml.lock
└── test # テスト用ソース

とりあえず全部入れておきました:

$ git add app src *.yaml *.cabel Setup.sh
$ git commit -m 'run `stack new`'

実行

$ stack build && stack exec rogue-hs-exe
someFunc

手間なので、一旦スクリプトを用意しておきます:

x
#!/usr/bin/env bash -euE

cd "$(dirname "$0")"
stack build && stack exec "$(basename "$(pwd)")-exe"
$ chmod +x ./x
toyboot4etoyboot4e

エディタの設定

LSP

haskell-language-server を入れます。 ghcup が勧められていますが、ひとまず brew で入れます:

$ brew install haskell-language-server

Emacs

leaf でサクっと入ります。今の NeoVim / Emacs は VSCode よりも簡単ですね。

(leaf haskell-mode
    :url "https://github.com/haskell/haskell-mode"
    :hook (haskell-mode-hook . lsp-deferred)
    :hook (haskell-literate-mode-hook . lsp-deferred)
    :config
    (leaf lsp-haskell
        :after lsp-mode
        :url "https://github.com/emacs-lsp/lsp-haskell"
        ))

ところが、 Haskell 言語サーバからエラーが!

hie.yaml

このファイルが無いと言語サーバが動かないようです。 ghcup, stack, cabal, Setup.hs, hie.yaml... どれがなに?!

implicit-hie でプロジェクトの hie.yaml が生成できるようです:

$ stack install implicit-hie

やっぱり ghcup

ここで haskell-language-serverghc のバージョンのミスマッチらしきことが起き、サーバの起動に失敗しまた。 ghcup を使うべきだったか……

toyboot4etoyboot4e

エディタの設定 2

ghcup で各ツールを入れ直しました。バージョンのミスマッチもなおったようです。

初動は遅いですが動いてますね〜。 HLS = haskell-language-server

toyboot4etoyboot4e

ウィンドウを出す

依存性追加 (stack)

package.yaml
dependencies:
- base >= 4.7 && < 5
- sdl2
- linear

ウィンドウ表示

Gettting startedapp/Main.hs にコピペしてみました。ウィンドウの大きさと色だけ変えて今日は寝ます。

toyboot4etoyboot4e

モナドの音が近づいている

すごい H 本を 65% まで読みました。というか以前から読んでいました。ベイビーちゃん卒業の道を這いつくばっていこう!

アルゴ式をやってみる予定

Haskell に限っては stdin / stdout の処理も面白い気がしてノっています。 Haskell は競技プログラミングに向いているなんて (何かを間違った) 噂にも希望を感じますね!

コミュニティ

Haskell-jp Slack でアホな質問を撒き散らします。違うんです! そんなつもりは無かったんです!

でもそんな時期が回答のおかげで一番伸びる時期でもあります。ありがとうございます。

入力 1 2 3 から 6 を得る

普通にやってみる:

module Main where

main = do
  line <- getLine
  print $ sum . map read . words $ line

あるいは IO アクション?を applciative functor とみて fmap する:

module Main where

main = do
  x <- sum . map read . words <$> getLine
  print x

<$>$ + 左辺の fmap 化という感じですか。これは 244 ページに載っている内容ですから、僕も65% くらい Haskell に入門したかもしれません。おいおい自分の才能が怖いぜ (圏への果てしない道から目を逸らしながら)

toyboot4etoyboot4e

Gloss 入門以前

そういえば『関数プログラミング 珠玉のアルゴリズムデザイン』を持っていたのでペラペラめくりつつ、好き勝手にできる場所も欲しいということで、 2D ゲームライブラリ gloss を触っていきます。

依存性の追加

gloss パッケージを使いたいのですが、ファイル構成が未だに謎です。 Stack User guideextra-edps 辺りを覗いてみました。

stack.yaml

パッケージの resolver を指定することで、 Hackage の snapshot (package set) を指定します。この snapshot 内のパッケージは package.yaml で指定します。また、この snapshot 外のパッケージは stack.yaml 内で指定します (extra-deps):

# Resolver to choose a 'specific' stackage snapshot or a compiler version.
resolver: lts-19.7 # LTS: long term support

# User packages to be built.
packages:
- .

# Dependency packages to be pulled from upstream that are not in the resolver.
extra-deps:

package.yaml

stack.yaml で指定された snapshot 内の依存パッケージを指定します:

dependencies:
- base >= 4.7 && < 5
- gloss

gloss は Haskell LTS に載っていたので package.yaml で追加しました。

<project-name>.cabal

依存パッケージのロックファイルのような雰囲気があります。

なぜこのような形になっているのか?

分からん! ユーザーガイドをちゃんと読めば分かりそうです。

Hello, gloss!

短いな〜〜

module Main where

import Graphics.Gloss

main :: IO ()
main = display (InWindow "rogue-hs" (320, 180) (10, 10)) white (Circle 80)

これはクリエイティブプログラミング (プログラムで絵を描くやつ) に良さそうですね

次は簡単な gloss 製ゲームのソースなど読んで行きます。

toyboot4etoyboot4e

環境構築 2

競技プログラミングでは標準入出力を使って問題に答えます。その手の問題をローカルで複数解きたい。いかにもテストフレームワークの出番ですが、今回は 1 つのプロジェクトで複数の実行ファイルを作ることにします。

解くべき問題

10 問解くやつはやっておきたいですね……

複数の実行ファイルを用意する

たとえば app/ 以下に 2 つの問題の回答スクリプトがあるとして:

app
├── P01.hs
└── P02.hs

package.yaml に以下のように書けば、

package.yaml
# DRY for package.yaml executables
# https://www.reddit.com/r/haskell/comments/haeqin/dry_for_packageyaml_executables/
_exe-defs: &exe-defaults
  source-dirs: app
  # dependencies:
  ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
  other-modules: []

executables:
  P01-exe:
    <<: *exe-defaults
    main:                P01.hs

  P02-exe:
    <<: *exe-defaults
    main:                P02.hs

それぞれのファイルを実行できます:

$ stack build && stack exec P01-exe
$ stack build && stack exec P02-exe

bash で stdin を与えるなら、 () を使います。この構文の名前を忘れた……:

$ echo '3 4' | (stack build && stack exec P01-exe)

スクリプトにまとめて:

x
#!/usr/bin/env bash -euE

cd "$(dirname "$0")"
stack build && stack exec "$1-exe"
$ echo '3 4' | ./x P01
toyboot4etoyboot4e

買い物が買い物を呼ぶ

本日も完全に Twitter な投稿です。

仮に 300 万円分の本を買って良いとすると、平均 3,000 円の本を 1,000 冊購入することができます。毎月 1 冊読んでも 100 年弱はかかるわけで、本よりも余命が先に尽きます。

つまり本は何冊買っても良い?! 今日は プログラミング Haskell 第二版 及び n 月間ラムダノート が届きました。これがまた別の本に繋がっていくのでしょうか。

プログラミング Haskell (第二版) について

IO モナドの謎を晴らして欲しいと思います。 IO という型クラスがあるわけではない……? 基本的な機能に関しても、取りこぼしを回収できるのはありがたいです。

冒頭 Eric の『本書に寄せて』が良かったです。サイトには載っていないようですね。これがお金の力! 味を占めてきましたよ。

n 月間ラムダノート (Vol. 3, No. 1) について

継続の話が載っているので買いました。なんかデザインもいいぞ! 全巻装丁が良かったら買い揃えると思います。

なんでも継続 を積んでいるので、継続の話は本当に楽しみです。このくらいの内容をサクサク読んでいけば、今よりも遥かに強くなれる気がします。

toyboot4etoyboot4e

let 式のシャドーイングが Rust とは違う

素晴らしい質問がありました:

https://stackoverflow.com/questions/11902413/why-does-this-code-using-shadowing-let-bindings-hang

-- こういう式はまずい!
let x = x * x in
-- こういう式もまずい!
let x = zipThree ts xs y

Let expressions have the general form let { d1 ; … ; dn } in e, and introduce a nested, lexically-scoped, mutually-recursive list of declarations (let is often called letrec in other languages). The scope of the declarations is the expression e and the right hand side of the declarations.

toyboot4etoyboot4e

Haskellで戦う競技プログラミング

良書でした。パラパラ読めて役に立つという Zenn にもありがちなタイプの本です。

Atcoder ABC の 4 問目くらいから TLE (time limit exceed) が出るようになります。人の解答を見ると、 ST モナド (stateモナド) とか Vector で謎のコードになっていました。ライブラリを習得する必要があります。

  • 1. ByteStringStringBuilder
    チートシートに追加しました。

  • 2. 全探索
    リスト + sequence

  • 3. モジュラー計算
    wip

  • 4. 正確評価
    Haskell では thunk (未評価の計算) を扱うためにすべての値が boxed になる可能性があります。たとえば String*u8 の連接リストに近い。つら過ぎる。

  • 5. Vector
    ok

  • 6. 手続き型プログラミング
    うーむ

  • 7. 動的計画法
    wip ナップサック問題がある!

toyboot4etoyboot4e

言語拡張 (WIP)

{-# LANGUAGE BangPatterns #-}

正確評価用

{-# LANGUAGE MultiWayIf #-}

ガードを if 式で書けるようになります。これは Rust に組み込みで欲しいですね (マクロではなく) 。

{-# LANGUAGE PatternGuard #-}

ガードで式の評価とパタンマッチができます。 https://wiki.haskell.org/Pattern_guard

toyboot4etoyboot4e

lens などに興味はありますが、しばらく見ることも無さそうです。閉じます。

このスクラップは2022/08/14にクローズされました