GHC 9.10の新機能
GHC 9.10.1が2024年5月11日にリリースされました。
この記事では、GHC 9.10の新機能を確認していきます。過去の類似の記事は
です。
この記事は網羅的な紹介記事とはなっていません。是非、公式のリリースノート類も参照してください:
- 2.1. Version 9.10.1 — Glasgow Haskell Compiler 9.10.1 User's Guide
- libraries/base/changelog.md · ghc-9.10 · Glasgow Haskell Compiler / GHC · GitLab
- GHC 9.10.x Migration Guide
GHC 9.10に入る機能
GHC2024
以下の拡張が含まれます:
- DataKinds
- DerivingStrategies
- DisambiguateRecordFields
- ExplicitNamespaces
- GADTs
- MonoLocalBinds
- LambdaCase
- RoleAnnotations
GHC 9.10の時点では、デフォルト言語はGHC2021のままです。
GHC2024で有効になる拡張の中だとMonoLocalBindsは要注意です。例えば、以下のコードはMonoLocalBindsのせいでコンパイルが通りません:
{-# LANGUAGE GHC2024 #-}
import Control.Monad.ST
import Data.STRef
foo :: Int -> Int -> Int
foo a b = runST action
where
-- action :: ST s Int の s が多相にならない
action = do
counter <- newSTRef a
modifySTRef' counter (+ b)
readSTRef counter
main = print (foo 3 5)
RequiredTypeArguments拡張
「必須の型引数」です。"Visible forall in types of terms" とも呼ばれます。
従来のHaskellでは、関数に「型だけ」を渡したいときは、
- ダミーの引数を渡す
-
Proxy
で渡す - AllowAmbiguousTypesとTypeApplications拡張を組み合わせる
のいずれかを利用する必要がありました。コードで書けばそれぞれ次のようになります。
sizeOf :: Storable a => a -> Int
sizeOfProxy :: Storable a => Proxy a -> Int
sizeOfTypeApp :: Storable a => Int -- 要 AllowAmbiguousTypes
main = do
print $ sizeOf (undefined :: Int)
print $ sizeOfProxy (Proxy :: Proxy Int)
print $ sizeOfTypeApp @Int -- 要 TypeApplications
今回、新たな方法が追加されました。特徴は
-
forall a ->
という形の量化子を使う(これは型/カインドのレベルではすでに使えるようになっていました) - 引数はそのまま(
@
なしで)型名を書ける(type
構文で明示することもできる)
ことです。
-- 要 RequiredTypeArguments
sizeOfRTA :: forall a -> Storable a => Int
sizeOfRTA a = sizeOf (undefined :: a)
main = do
print $ sizeOfRTA Int
print $ sizeOfRTA (type Int) -- 要 ExplicitNamespaces
注意点として、[Int]
や (Int, String)
みたいな型をそのまま渡すと、項レベルのリストや項レベルのタプルを型に昇格したもの(型レベルリスト、型レベルタプル)と解釈されます(型レベルで書くと '[Int]
とか '(Int, String)
だったやつ)。リスト型やタプル型を渡したい場合は、ExplicitNamespaces拡張を使って (type ...)
と書くか、新しく導入された型エイリアスを使って List Int
や Tuple2 Int String
と書く必要があります。
残念ながら、型クラスのメソッドでは「自身の型」を forall ->
で受け取ることはできません。
class NewStorable a where
sizeOf :: forall a -> Int -- できない(NewStorable のインスタンスの a と引数の a は別物になる)
型の等式 ~
を使ったHackは思いつきましたが、どうでしょうか(Coreレベルでは同一にならないかもしれませんが)。
class NewStorable2 a where
sizeOf :: forall a' -> a ~ a' => Int
おまけですが、RequiredTypeArgumentsを使うと型注釈を新たな方法で書けるようになります:
as :: a -> forall a' -> a ~ a' => a
as x _ = x
main = print (42 `as` Integer)
追記:より詳しい記事を書いてみました→GHC 9.10で実装された可視なforallで遊ぶ
線形なlet/where束縛
LinearTypes拡張の下で、線形な let
あるいは where
束縛が使えるようになります。例えば、以下のコードのコンパイルが通ります:
{-# LANGUAGE LinearTypes #-}
f :: Int %1 -> Int
f x = let y = x
in y
main = print (f 42)
forallのキーワード化
これまでは forall
を項レベルの変数名として使うことができましたが、GHC 9.10ではエラーになるようになりました。型レベルでは以前からキーワード扱いだったので、これで forall
は常にキーワード扱いされるようになります。
fixity宣言やWARNING/DEPRECATEDプラグマにおいて型と項の名前空間を指定できる
型レベルと項レベルに同じ名前があるとき、infix
宣言や WARNING
/DEPRECATED
プラグマにおいて、型と項の片方だけを指定できます。
{-# LANGUAGE TypeOperators #-}
-- 型レベルの $$
type f $$ x = f x
-- 項レベルの $$
f $$ x = f x
infixr 9 type $$
infixr 0 data $$
main :: IO $$ () -- ここでの $$ は infixr 9
main = putStrLn $$ "Hello " ++ "world" -- ここでの $$ は infixr 0
NoListTuplePuns拡張
これまでは Int
を要素とするリストの型は [Int]
、Int
と String
からなるタプルの型は (Int, String)
と書いていましたが、NoListTuplePuns拡張を使うとこれらの記法を無効化できます。
リスト型を書きたいときは Data.List
からエクスポートされる List
型を使って List Int
と、2要素のタプル型を書きたいときは GHC.Tuple
あるいは Prelude.Experimental
からエクスポートされる Tuple2
型を使って Tuple2 Int String
という風に書くことができます。
関数定義における不可視の型束縛
TypeAbstractions拡張(GHC 9.8で導入)が拡張され、項レベルの関数を定義するときにも使えるようになりました。
{-# LANGUAGE TypeAbstractions #-}
id :: forall a. a -> a
id @a x = x :: a
JavaScriptバックエンド:Cソースのサポート
JavaScriptバックエンドが、Cソースをサポートするようになりました。CソースはEmscriptenによってWasmにコンパイルされます。
ちなみに、最近のGHCupはJavaScript向けGHCのインストールに実験的に対応しているようです:GHC JS cross bindists (experimental)
WebAssemblyバックエンド:JavaScript FFIのサポート
WebAssemblyバックエンドが、JavaScript FFIをサポートするようになりました。詳しくはドキュメントを参照してください。
ちなみに、最近のGHCupはWasm向けGHCのインストールに実験的に対応しているようです:GHC WASM cross bindists (experimental)
ghc-internalパッケージとghc-experimentalパッケージ
GHCに付属する基本的なパッケージとしては、Prelude
を含む標準ライブラリーであるbase、一般のユーザーが直接触ることは基本的にありませんがGHCのプリミティブを含む[1]ghc-prim、多倍長整数の実装を含むghc-bignumなどがありました。
今回、新たにghc-internalパッケージとghc-experimentalパッケージが追加されます。ghc-internalパッケージはGHCの内部的な機能を、ghc-experimentalパッケージは将来baseに入るかもしれない実験的な機能を含みます。
現在のところ、ghc-experimentalパッケージは Tuple2
などをエクスポートする Data.Tuple.Experimental
や Prelude.Experimental
などのモジュールを含みます。
ライブラリーの変化
-
foldl'
がPrelude
からエクスポートされます。 -
List
型がData.List
からエクスポートされます。
-
GHCのプリミティブを触りたい場合はghc-primパッケージの
GHC.Prim
モジュールではなく、baseパッケージのGHC.Exts
モジュールを利用するべきです。 ↩︎
Discussion