🙆

GHC 9.6の新機能

2023/03/12に公開

GHC 9.6.1が2023年3月12日にリリースされました。

この記事では、GHC 9.6の新機能を確認していきます。過去の類似の記事は

です。

この記事は網羅的な紹介記事とはなっていません。是非公式のリリースノート類も参照してください:

GHC 9.6に入る機能

JavaScriptバックエンド

GHCにJavaScriptバックエンドを追加します。実質的にGHCJSのマージです。

現段階ではTemplate Haskellやforeign exportなど、色々欠けているようです。今後に期待しましょう。

GHC 9.6の段階ではGHCは「実行時のオプションでターゲットを切り替えられる」ようにはなっていないので、普通のGHCではなく、JSをターゲットとするクロスコンパイラーとしてビルドされたGHCが必要になります。ビルド手順は

あたりを参考にすると良いでしょう。

手前味噌ですが、私が書いたDockerfileを以下で公開しています:

WebAssemblyバックエンド

GHCにWebAssemblyバックエンドを追加します。実質的にAsteriusのマージです。

WebAssemblyといえばWebですが、GHC 9.6の段階ではWASI専用のようです。将来的には foreign import/export javascript でJavaScriptとやりとりできるようになるでしょう。

GHC 9.6の段階ではGHCは「実行時のオプションでターゲットを切り替えられる」ようにはなっていないので、普通のGHCではなく、wasmをターゲットとするクロスコンパイラーとしてビルドされたGHCが必要になります。

手元で試したい方は、以下にNixでバイナリーを入れる手順やビルド手順が載っています:

私もDockerfileを用意するつもりです。 上に書いたリポジトリーにwasm32-wasi向けのGHCをビルドするDockerfileを置きました。GHCだけでなくLLVMのビルドも含まれますが、試してみたい方はどうぞ。AArch64のマシンで動作確認しています。

HaskellのDiscourseにGHC WebAssembly Weekly Updateが投稿されています:

限定継続のプリミティブ

限定継続については浅井先生の

や私の記事

を見てください。

以下の型と関数が GHC.Exts モジュールから使えるようになります:

type PromptTag# :: Type -> UnliftedType

newPromptTag# :: forall (a :: Type). State# RealWorld -> (# State# RealWorld, PromptTag# a #)

prompt#
  :: forall (a :: Type)
   . PromptTag# a
  -> (State# RealWorld -> (# State# RealWorld, a #))
  -> State# RealWorld -> (# State# RealWorld, a #)

control0#
  :: forall (a :: Type) (r :: RuntimeRep) (b :: TYPE r)
   . PromptTag# a
  -> (((State# RealWorld -> (# State# RealWorld, b #))
       -> State# RealWorld -> (# State# RealWorld, a #))
      -> State# RealWorld -> (# State# RealWorld, a #))
  -> State# RealWorld -> (# State# RealWorld, b #)

型が分かりにくいですが、 State# RealWorld -> (# State# RealWorld, a #)IO a と思って読み替えると

newPromptTag :: IO (PromptTag a)
prompt :: PromptTag a -> IO a -> IO a
control0 :: PromptTag a -> ((IO b -> IO a) -> IO a) -> IO b

となります。ただ、既存の withFile 等のリソース管理と限定継続の兼ね合いが微妙なので IO でラップしたものは標準ライブラリーからは提供されません。

限定継続の操作は副作用なので、Haskellで使うには何らかのモナドを使う必要があります。したがって、他の言語での継続のサンプルコードを移植すると書き心地が悪く感じるかもしれません。

限定継続プリミティブを利用した拡張可能エフェクトのライブラリーが開発中のようです(最終更新が2020年なのでちょっと怪しいですが):

限定継続を使ってalgebraic effectをやる記事も出ています:

Haskell Error Indexへの対応

GHCや周辺ツールがエラーメッセージに [GHC-12345] のようなコードを割り振って、オンラインで関連情報を参照できるようにしようという取り組みが始まりました。というわけで、GHC 9.6からエラーに番号がつくようになります。

Rustの同様の仕組みにインスパイアされたらしいです。

データ構築子を伴わない型・カインド定義: TypeData 拡張

DataKind 拡張を使うと、型をカインドに、データ構築子を型レベルに持ち上げることができます。むしろ、データ型は要らないけど独自のカインドと型を定義するために data 宣言を使う、という場面があります。

TypeData 拡張を使うと、型・データ構築子を定義することなくカインドと型を定義することができるようになります。構文はこんな感じです:

type data T = MkT

例:

ghci> :set -XTypeData
ghci> type data T = MkT
ghci> :type MkT

<interactive>:1:1: error: [GHC-31891]Illegal term-level use of the type constructor or classMkT’
    • defined at <interactive>:2:15In the expression: MkT
ghci> :kind MkT
MkT :: T

OverloadedLabels のラベル名の制限の緩和

これまでは # の後に識別子が来る必要がありましたが、これからは #3 とか #"Foo" とか色々書けるようになります。

CApiFFIのための ConstPtr

capi 擬似呼び出し規約を使うと、CのマクロやAArch64 Darwinの可変長引数関数など、普通のC FFIが対応していない関数っぽいものを呼び出すことができます。これは仲介役となるCコードをGHCが出力し、Cコンパイラーにコンパイルさせることで実現されています。

この際、Cの型注釈はHaskellの型注釈を基に決定されますが、const 修飾されたポインターはHaskell側で記述できないためCの型が合わないという問題がありました。

この問題を解決するために、今回、const 修飾されたポインターを表す ConstPtr 型が追加されました。

リスト型に紛らわしくない名前を与える:List

従来、Haskellのリスト型は [] で表記されてきました。一方、リストの項(値)レベルの表記にも [] が使われます。

通常は型の表記と項の表記は混ざらないのでこれでよかったのですが、将来のHaskell -- Dependent Haskell -- では項と型の区別がますます曖昧になっていく見込みです。

すでに現在、項と型の表記が混ざる場面があります。 DataKinds 拡張です。次のコードを見てみましょう:

ghci> :kind []
[] :: Type -> Type
ghci> :kind [Int]
[Int] :: Type

これは大丈夫ですよね。リスト型構築子と具体的なリスト型のカインドを表示させただけです。

一方、「要素」の数を3つ以上にしたらどうなるでしょうか:

ghci> :kind [Int,Int]
[Int,Int] :: [Type]
ghci> :kind [Int,Int,Int]
[Int,Int,Int] :: [Type]
ghci> :kind [Int,Int,Int,Int]
[Int,Int,Int,Int] :: [Type]

なんとエラーにはなりません。これは、項レベルの [] を型レベルに昇格させたもの、型レベルリストと判断されたのです。

空の型レベルリストおよび、要素数1の型レベルリストを表記するには、従来は頭に ' をつけるしかありませんでした:

ghci> :kind '[]
'[] :: [a]
ghci> :kind '[Int]
'[Int] :: [Type]

一方、項レベルの式の中に型を混在させたい時はどうなるでしょうか。将来のHaskellでは関数の引数に @ なしで直接型を渡せるようになる(ような言語拡張 RequiredTypeArguments が追加される)見込みです:

{-# LANGUAGE RequiredTypeArguments #-}

sizeOf :: forall a -> Sized a => Int

main = print (sizeOf Int)

ここで、[Int]Int のリスト)という型を sizeOf [Int] と渡そうとすると項の文法が優先されて、1要素の型レベルリストが引数になってしまいます!

まあ項レベルの名前を型で使うことを明示する ' という表記があるのと同じように型レベルの名前を項で使うことを明示する (type ...) という表記が導入される予定なのでそれはいいのですが、そもそもリスト型とリストリテラルの表記が別であればこの件に関しては問題は起こらなかったわけです。

というわけで、常にリスト型を表す名前として List が導入されます。以下の定義が組み込まれると思って構いません:

module GHC.Exts where

-- 本当は型エイリアスではない(TypeSynonymInstances/FlexibleInstancesなしでもインスタンスを定義できる)
type List = []

タプルについても同じことが言えて、常にタプル型を表す名前として Unit, Tuple<n> などが導入される予定です。1要素タプル Solo はデータ構築子が MkSolo に改名されます。

最終的には []() が型を表さなくなる拡張 NoListTuplePuns が導入される計画です。

とりあえずGHC 9.6には List 型が入り、 Solo :: a -> Solo aMkSolo に改名されます。タプルの名前や NoListTuplePuns はもう少し先になりそうです。

ライブラリーのその他の変化

ビルドシステムがHadrianのみになる

従来はGHCをビルドする方法は二つありました:

  • 伝統的なmake
  • Haskell/Shakeで書かれたHadrian

ですが今回、伝統的なmakeベースのビルドシステムは削除されました。

Hadrianを使ってビルドするには

$ ./boot # Gitから取ってきたソースをビルドする場合
$ ./configure
$ hadrian/build -j

みたいな感じのコマンドを使います。詳しくはhadrian/README.mdとか hadrian/build --help を見てください。

Discussion