GHC 9.6の新機能
GHC 9.6.1が2023年3月12日にリリースされました。
この記事では、GHC 9.6の新機能を確認していきます。過去の類似の記事は
です。
この記事は網羅的な紹介記事とはなっていません。是非公式のリリースノート類も参照してください:
- 2.1. Version 9.6.1 — Glasgow Haskell Compiler 9.6.1 User's Guide
- Changelog for base-4.18.0.0 | Hackage
- GHC 9.6.x Migration Guide
GHC 9.6に入る機能
JavaScriptバックエンド
- javascript backend · Wiki · Glasgow Haskell Compiler / GHC · GitLab
- JavaScript backend merged into GHC | IOG Engineering
GHCにJavaScriptバックエンドを追加します。実質的にGHCJSのマージです。
現段階ではTemplate Haskellやforeign exportなど、色々欠けているようです。今後に期待しましょう。
GHC 9.6の段階ではGHCは「実行時のオプションでターゲットを切り替えられる」ようにはなっていないので、普通のGHCではなく、JSをターゲットとするクロスコンパイラーとしてビルドされたGHCが必要になります。ビルド手順は
あたりを参考にすると良いでしょう。
手前味噌ですが、私が書いたDockerfileを以下で公開しています:
WebAssemblyバックエンド
- WebAssembly backend · Wiki · Glasgow Haskell Compiler / GHC · GitLab
- WebAssembly goals · Wiki · Glasgow Haskell Compiler / GHC · GitLab
- WebAssembly backend merged into GHC - Tweag
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への対応
- The Haskell Error Index — Haskell Error Index
- Announcing the Haskell Error Index - Haskell Foundation - Haskell Community
GHCや周辺ツールがエラーメッセージに [GHC-12345]
のようなコードを割り振って、オンラインで関連情報を参照できるようにしようという取り組みが始まりました。というわけで、GHC 9.6からエラーに番号がつくようになります。
Rustの同様の仕組みにインスパイアされたらしいです。
TypeData
拡張
データ構築子を伴わない型・カインド定義: - Define Kinds Without Promotion
- Allow defining kinds alone, without a datatype (#6024) · Issues · Glasgow Haskell Compiler / GHC · GitLab
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 class ‘MkT’
• defined at <interactive>:2:15
• In the expression: MkT
ghci> :kind MkT
MkT :: T
OverloadedLabels
のラベル名の制限の緩和
これまでは #
の後に識別子が来る必要がありましたが、これからは #3
とか #"Foo"
とか色々書けるようになります。
ConstPtr
型
CApiFFIのための - #22043: CApiFFI does not account for const qualifier · Issues · Glasgow Haskell Compiler / GHC · GitLab
- Foreign.C.ConstPtr
capi
擬似呼び出し規約を使うと、CのマクロやAArch64 Darwinの可変長引数関数など、普通のC FFIが対応していない関数っぽいものを呼び出すことができます。これは仲介役となるCコードをGHCが出力し、Cコンパイラーにコンパイルさせることで実現されています。
この際、Cの型注釈はHaskellの型注釈を基に決定されますが、const
修飾されたポインターはHaskell側で記述できないためCの型が合わないという問題がありました。
この問題を解決するために、今回、const
修飾されたポインターを表す ConstPtr
型が追加されました。
List
型
リスト型に紛らわしくない名前を与える:- Non-punning list and tuple syntax
- Non-punning list and tuple syntax (-XListTuplePuns, -XNoListTuplePuns) (#21294) · Issues · Glasgow Haskell Compiler / GHC · GitLab
従来、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 a
が MkSolo
に改名されます。タプルの名前や NoListTuplePuns
はもう少し先になりそうです。
ライブラリーのその他の変化
-
Applicative
クラスのliftA2
がPrelude
からエクスポートされる -
Data.Char.isUpperCase
/isLowerCase
- 既存のものとはUnicode的な性質がちょっと違うらしいです。
-
GHC.TypeLits
/GHC.TypeNats
のKnown
系の型クラスからnatSing
,symbolSing
,charSing
をエクスポートする。SNat
,SSymbol
,SChar
型もエクスポートされる。
ビルドシステムがHadrianのみになる
従来はGHCをビルドする方法は二つありました:
- 伝統的なmake
- Haskell/Shakeで書かれたHadrian
ですが今回、伝統的なmakeベースのビルドシステムは削除されました。
Hadrianを使ってビルドするには
$ ./boot # Gitから取ってきたソースをビルドする場合
$ ./configure
$ hadrian/build -j
みたいな感じのコマンドを使います。詳しくはhadrian/README.mdとか hadrian/build --help
を見てください。
Discussion