🍣

GHC 9.10の新機能

2024/03/20に公開

GHC 9.10.1が2024年5月11日にリリースされました。

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

です。

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

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 IntTuple2 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]IntString からなるタプルの型は (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.ExperimentalPrelude.Experimental などのモジュールを含みます。

ライブラリーの変化

  • foldl'Prelude からエクスポートされます。
  • List 型が Data.List からエクスポートされます。
脚注
  1. GHCのプリミティブを触りたい場合はghc-primパッケージの GHC.Prim モジュールではなく、baseパッケージの GHC.Exts モジュールを利用するべきです。 ↩︎

Discussion