GHC の線形型プロトタイプのお試し環境を作る
Posted on 金 28 6月 2019 in プログラミング言語
現在, GHC に 線形型の追加提案 が出ている.その提案に合わせて,プロトタイプも公開されている.内容はあまり詳しく追ってないけど,お試し環境は作ってみたのでその備忘録.
線形型プロトタイプ実装
線形型の提案は, GHC Proposals (https://github.com/ghc-proposals/ghc-proposals/pull/111) で議論されている. https://arxiv.org/abs/1710.09756 の話を元にしているらしい.プロトタイプ実装は, Tweag の GHC フォーク (https://github.com/tweag/ghc) 内の linear-types ブランチで公開されている.
後,このプロトタイプを前提として,線形型用の base 代替パッケージ, linear-base パッケージ (https://github.com/tweag/linear-base) も公開されている.中には
- Prelude.Linear : Prelude の線形型版.色んな常用関数を線形型用に型を変えて提供してる.
- Control.Monad.Linear : 線形型版のモナドを提供してる.
- Control.Monad.Linear.Builder : 線形型版のモナドを RebindableSyntax によって do 構文で使えるようにするためのデータを提供する.
- System.IO.Linear : IO モナドを線形型を使って厳密に定義し直したやつを提供する.
- System.IO.Resource : リソース管理を線形型を使って型安全に行う仕組みを提供する.
- Unsafe.Linear : 線形型と普通の関数型のキャストを提供する.
みたいなモジュールがある (これが全部ではない).
プロトタイプの利用は GHC のビルドが必要だが,時間が相当かかるため,公開されてる Docker イメージで Stack の連動機能を使うことが推奨されている.
お試し Docker 環境を作る
ただ, Stack の連動機能を使わないで直接 Cabal を使って試したかったのでその環境を作った.公開されてる Docker イメージに, cabal-install を突っ込んで, linear-base パッケージを落としてきた状態にしただけだけど.
まず,次の Dockerfile を用意する:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | FROM tweag/linear-types:latest ENV CABAL_WORKDIR="/root" RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y curl \ && mkdir -p "${CABAL_WORKDIR}/.ghcup/bin" \ && curl https://gitlab.haskell.org/haskell/ghcup/raw/master/ghcup > "${CABAL_WORKDIR}/.ghcup/bin/ghcup" \ && chmod +x "${CABAL_WORKDIR}/.ghcup/bin/ghcup" \ && "${CABAL_WORKDIR}/.ghcup/bin/ghcup" install-cabal \ && apt-get remove -y curl \ && apt-get autoremove -y \ && rm -rf /var/lib/apt/* ENV PATH="${CABAL_WORKDIR}/.ghcup/bin:${CABAL_WORKDIR}/.cabal/bin:$PATH" ADD assets/Main.hs ${CABAL_WORKDIR}/Main.hs ADD assets/cabal.project ${CABAL_WORKDIR}/cabal.project ADD assets/playground.cabal ${CABAL_WORKDIR}/playground.cabal WORKDIR ${CABAL_WORKDIR} RUN mkdir -p src \ && cabal new-update \ && cabal new-build ENTRYPOINT ["cabal"] CMD ["new-repl"] |
tweag/linear-types が公開されてる Docker イメージで, Docker Hub に上がってる.こいつに ghcup で cabal-install を入れてるだけ.後適当に次のファイルも用意しとく:
$ cat assets/Main.hs module Main where main :: IO () main = putStrLn "Hello" $ cat assets/cabal.project source-repository-package type: git location: https://github.com/tweag/linear-base.git packages: ./ $ cat assets/playground.cabal cabal-version: 2.4 name: playground version: 0.0.0.1 build-type: Simple common general autogen-modules: Paths_playground other-modules: Paths_playground build-depends: base >= 4.7 && < 5, linear-base ghc-options: -Wall -Wcompat -Wincomplete-uni-patterns -Wincomplete-record-updates -Wredundant-constraints -Wpartial-fields -fprint-explicit-foralls -fprint-potential-instances -frefinement-level-hole-fits=1 -dcore-lint default-language: Haskell2010 executable playground import: general hs-source-dirs: . main-is: Main.hs
後は,イメージをビルドして tty 有効で起動してやると, linear-base が入った環境で REPL で色々できる.適当に docker-compose.yml も作っておく:
version: '3' services: playground: build: . volumes: - ./Main.hs:/root/Main.hs entrypoint: bash command: ["-c", "tail -f /var/log/lastlog"]
これで,カレントディレクトリに Main.hs を作って, Docker イメージ内でビルドしたり,読み込んだ状態で REPL 起動したりできる:
docker-compose up -d docker-compose exec playground cabal new-build # ビルド docker-compose exec playground cabal new-repl # REPL 起動
動作確認
試しに,ちょっとしたサンプルを書いて動かしてみる.以下のようなサンプルを Main.hs として用意した:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | {-# LANGUAGE BlockArguments #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RebindableSyntax #-} {-# LANGUAGE RecordWildCards #-} module Main where import Prelude hiding (Monad(..), MonadFail(..)) import Prelude.Linear (Unrestricted(..)) import qualified Control.Monad.Linear.Builder as Linear import qualified System.IO as System import qualified System.IO.Resource as RIO import Data.String (fromString) main :: IO () main = RIO.run $ writeSomething "something.txt" writeSomething :: FilePath -> RIO.RIO (Unrestricted ()) writeSomething path = do h0 <- RIO.openFile path System.WriteMode h1 <- RIO.hPutStr h0 "some" h2 <- RIO.hPutStr h1 "thing" RIO.hClose h2 return (Unrestricted ()) where Linear.Builder{..} = Linear.monadBuilder |
こいつを REPL で動かしてみる:
$ docker-compose exec playground cabal new-repl >>> :m Main >>> main >>> readFile "something.txt" "something" >>> :q
ちゃんと動いてそう.なお,フルセットは https://github.com/mizunashi-mana/docker-ghc-linear-types に上げた.