🐈

GHC 9.4の新機能

2022/08/14に公開

GHC 9.4.1が2022年8月7日にリリースされました(GHC 9.4.1 released — The Glasgow Haskell Compiler)。

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

です。

GHC 9.4に入る機能

ここでは筆者が独断と偏見で選んだ変更をリストしています。公式の変更リストは

を参照してください。

リリース管理のやつ:

型の等価性 ~ が普通の型演算子になる

従来のGHCでは GADTs 拡張または TypeFamilies 拡張を有効にすると型演算子 ~ が使えるようになり、 a ~ b という制約で「型 a と型 b が等しい」ことを表現できました。

GHC 9.4では、 ~ の扱いが次のように変わります:

  • 使用に TypeOperators 拡張が必要になる(一方で、 GADTsTypeFamilies は必要なくなる)
    • 当面の間、 TypeOperators なしで ~ を使った場合は警告が出る
  • ~Prelude および Data.Type.Equality からエクスポートされるようになる
  • Prelude からエクスポートされる定義を隠せば) ~ という名前を持ったユーザー定義の型演算子を定義できるようになる

\cases

LambdaCase 拡張の下で \cases という構文が使えるようになります。複数の引数にマッチさせることができます。

例えば

{-# LANGUAGE LambdaCase #-}

f = \cases
      (Left a) (b,c) -> a + b + c
      (Right a) (b,c) -> a * b + c

という感じです。

DeepSubsumption 拡張

GHC 9.0でsimplified subsumptionという変更が行われました。詳しくは

を見てください。

simplified subsumptionでは手動でη変換が必要になる場面が増えました。これはコンパイルの簡略化のためですが、手動でのη変換はやっぱり面倒なものです。

DeepSubsumption はsimplified subsumption以前の挙動を復活させます。これにより、自動でη変換される場面が増えます。

Haskell2010 の下では DeepSubsumption はデフォルトで有効、 GHC2021 の下では DeepSubsumption はデフォルトで無効です。

DeepSubsumption はGHC 9.2.4にもバックポートされています。GHCのマイナーリリースで新しくGHC拡張が実装されるのは異例のことです(GHC 9.2系がLTSということもあるのでしょうけど)。

ByteArrayMutableByteArray

GHCにはヒープで管理されたバイト列を表すプリミティブ型として ByteArray# 型および MutableByteArray# 型があります。これらはunliftedな型で普段使いには辛いので、liftedなラッパーがprimitiveパッケージなどで提供されていました。

今回、GHC付属のbaseパッケージでliftedなラッパーが提供されることになります。将来、primitiveパッケージが提供する型はbaseパッケージの再エクスポートになるかもしれません。

Levity-polymorphic arrays

GHC.ExtsArray# 型のカインドが

type Array# :: Type -> UnliftedType

から

type Array# :: TYPE ('BoxedRep l) -> UnliftedType

へ一般化されました。これによって、 Array# の要素として通常の型の他にunliftedな型(サンクを許容しない型)が使えるようになります。

これまでは ArrayArray# 型というunliftedな配列の配列みたいな組み込み型がありましたが、それが不要になります。

まあ名前に # のつく型なので一般ユーザーにはあまり影響はないかもしれません。

STMonadFail のインスタンスじゃなくなる

MonadFail の趣旨はdo構文中のパターンマッチ失敗時に暗黙に error を発生させるのをやめよう、という感じのやつでした。

ですが、 ST モナドはこの趣旨に反して fail = errorMonadFail インスタンスを持っていました。今回、これがなくなります。

ST モナドで失敗しうるパターンマッチを使っていた人は明示的にパターンマッチして error を呼ぶか、irrefutable patternを使うように書き換える必要があります。

magicDictwithDict になる

型クラスのインスタンスを動的に作れると便利なことがあります。型レベル自然数の辞書である KnownNat のインスタンスを Natural 型の値から作る、というようなやつです。

従来はこれをやるのにGHC組み込みの magicDict を使うか、みんな大好き unsafeCoerce を使うやり方がありました。base では magicDict が、reflectionsingletons 等のサードパーティーのライブラリーでは unsafeCoerce が好まれていた印象です。しかし、unsafeCoerce が遅くなる変更が最近(GHC 9.0)入ったため、サードパーティーも magicDict を使うようにしようという機運が高まりました。

ですが、 magicDict の型は

magicDict :: a

という情報量皆無な代物で、正しく使うのは大変でした。GHC 9.4では magicDict

withDict :: meth -> (cls => r) -> r

に置き換えられることになりました。

Int64/Word64 の内部表現が変わる

Word64 型の定義は、従来は

-- 32ビット環境
data Word64 = W64# Word64#

-- 64ビット環境
data Word64 = W64# Word#

でした。今回、これが

data Word64 = W64# Word64#

に統一されます。GHC 9.2では Word8, Word16, Word32 について似たような変更がありましたね。

Int64 についても同様です。

SPARC NCGの削除

前にこんな記事を書きましたが

SPARC NCGがついに削除されました。

xorとシフト演算子の追加

Haskellでのビット論理和とビット論理積はそれぞれ (.|.)(.&.) ですが、排他的論理和 (xor) やシフトは xorshiftL/shiftR という風にアルファベットの名前がついていました。

今回、 xorshiftL/shiftR に対応する記号の演算子が追加されました。他の関数や演算子のように Data.Bits からエクスポートされます。

  • (.^.) (infixl 6): xor
  • (.>>.) (infixl 8): shiftR
  • (.<<.) (infixl 8): shiftL
  • (!>>.) (infixl 8): unsafeShiftR
  • (!<<.) (infixl 8): unsafeShiftL

その他

他にも

  • Windows上でClangベースのツールチェインを使う
  • Semigroup, Monoid をderiveするのに便利な Generically, Generically1
  • multiple home packages
  • system-cxx-std-lib 疑似パッケージ
  • -dlint オプション
  • OPAQUE プラグマ

などなど新機能がありますが、今回の記事はこの辺にしておきます。

Discussion