リテラルにTypeApplicationsを使えない理由とその対策、あるいはTypeApplicationsの注意点

最近のGHCにはTypeApplicationsという拡張があります。これを使うと `show @Int 42` というふうに多相関数の型を明示的に指定できます（**型適用**）。

ところで、Haskellの数値リテラルや、OverloadedStrings拡張下での文字列リテラルはオーバーロードされた型を持ちます。オーバーロードは便利ですが、状況によっては型が曖昧となってしまいます。

曖昧さを解決するには、型を明示すれば良いですね。しかし、通常の型注釈で `42 :: Int` とは書けても、型適用を使って `42 @Int` とは書けません。

通常の型注釈 `::` には、優先順位が低いという問題があります。そのため、例えば「xの3乗」を型の曖昧さを排除して書こうとしたら `x ^ (3 :: Int)` となってしまいます。

型適用は結合の優先順位が高いので、型適用が使えれば `x ^ 3 @Int` と書けそうなものですが、残念ながらそれはできません。

どうしてもカッコを書きたくない場合は `id` を使って `id @Int 3` と書くこともできますが、タイプ数が増えてしまいます。

この記事では、TypeApplicationsについて解説し、なぜリテラルに型適用できないかを説明します。そして、適切に状況を設定すればリテラルにも型適用を使えるようになることを見ていきます。

# TypeApplicationsの挙動

まずは多相関数の型注釈について確認し、それがTypeApplicationsとどう関わるか見ていきます。

## 多相関数と型注釈

Haskellでは

```haskell
bar :: a -> b -> (a, b)
bar x y = (x, y)
```

という風な型変数を含む関数を書くと、 `a` と `b` は暗黙に全称量化されます。つまり、 `bar` は `a` と `b` に任意の型を当てはめられる、多相的な関数となります。

同じ関数を型注釈を使わずに

```haskell
foo x y = (x, y)
```

と書いても、型推論により最も一般的な型である `a -> b -> (a, b)` が推論されます。

さて、 ExplicitForAll 拡張を使うと、 `forall` キーワードを使って型変数を明示的に全称量化できます。

```haskell
bar :: forall a b. a -> b -> (a, b)
```

型変数の順番を変えて、次のようにしても良いでしょう。

```haskell
bar' :: forall b a. a -> b -> (a, b)
```

一方、 RankNTypes 拡張を使うと、関数の型の途中に `forall` を書くことができるようになります。

```haskell
baz :: forall a. a -> forall b. b -> (a, b)
```

通常のHaskellでは、 `foo` と `bar`, それに `bar'` と `baz` のいずれも全く同じように振舞います。実際、 `apply :: (forall a b. a -> b -> (a, b)) -> (Int, Double)` という関数には `foo`, `bar`, `bar'`, `baz` のいずれも渡すことができます。

例：

```haskell
{-# LANGUAGE RankNTypes #-}

-- Inferred type: a -> b -> (a, b)
foo x y = (x, y)

-- Rank-1 type (ExplicitForAll)
bar :: forall a b. a -> b -> (a, b)
bar x y = (x, y)

-- Rank-1 type (ExplicitForAll)
bar' :: forall b a. a -> b -> (a, b)
bar' x y = (x, y)

-- Rank-1 type (requires RankNTypes)
baz :: forall a. a -> forall b. b -> (a, b)
baz x y = (x, y)

-- Rank-2 type (requires RankNTypes)
apply :: (forall b. Int -> b -> (Int, b)) -> (Int, Double)
apply f = f 42 3.14

-- Rank-2 type (requires RankNTypes)
apply' :: (Int -> forall b. b -> (Int, b)) -> (Int, Double)
apply' f = f 42 3.14

main = do
  print $ apply foo -- OK
  print $ apply bar -- OK
  print $ apply bar' -- OK
  print $ apply baz -- OK
  print $ apply' foo -- OK
  print $ apply' bar -- OK
  print $ apply' bar' -- OK
  print $ apply' baz -- OK
```

## TypeApplications

先ほど「通常のHaskellでは、 `foo` と `bar` と `bar'` と `baz` は全く同じように振舞います。」と書きましたが、よく考えるとGHC拡張を使っている時点で「通常のHaskell」と呼べるかは怪しいですね。より正確にいうと、「TypeApplications拡張を使わないHaskellでは」というところでしょうか。

実は、TypeApplications拡張の下では、 `foo` と `bar` と `bar'` と `baz` に違いが生まれます。

先ほどのコードを次のように変えてみましょう：

```haskell
{-# LANGUAGE TypeApplications #-}

...

main = do
  print $ foo @Int @Double 42 3.14
  print $ bar @Int @Double 42 3.14
  print $ bar' @Int @Double 42 3.14
  print $ baz @Int @Double 42 3.14
```

この中でコンパイルが通るのは `bar @Int @Double` だけで、残りの3つは型エラーとなります。

まず、 `bar' :: forall b a. a -> b -> (a, b)` に対して `bar' @Int @Double` が型エラーとなるのはなんとなく納得できますよね。型変数の順番的に `b = Int`, `a = Double` となるはずなので、 `Int` 型の引数に `3.14` を渡すことになってしまうのでエラーとなります。

一方、 `foo` は型注釈を書きませんでした。**型注釈が書かれていない場合、型変数の順番は「曖昧」と判断され、型適用は使えません**。推論された型に含まれる型変数の個数が1個でも同じです。

最後に、 `baz` ですが、**値レベルの引数と型引数 (`forall b.`) の順番が一致していないと型適用は失敗します**。

というわけで、**TypeApplications以後のHaskellでは型注釈の有無や型引数の順番が重要となります**。

## GHCiでの確認方法

GHCiで「どの型変数に型適用できるのか」を確認するには、 `:set -fprint-explicit-foralls` を実行して `:type +v` （または `:t +v`）を使います。

通常の状態で型を確認しても違いはわかりませんが、

```haskell:GHCi
*Main> :type foo
foo :: a -> b -> (a, b)
*Main> :type bar
bar :: a -> b -> (a, b)
*Main> :type bar'
bar' :: a -> b -> (a, b)
*Main> :type baz
baz :: a -> b -> (a, b)
```

`:set -fprint-explicit-foralls` を実行して `:t +v` を使うと「どの型変数にどの順番で型適用できるのか」がわかるようになります。具体的には、型適用できない型変数は波括弧で表示されます。

```haskell:GHCi
*Main> :set -fprint-explicit-foralls 
*Main> :type +v foo
foo :: forall {a} {b}. a -> b -> (a, b)
*Main> :type +v bar
bar :: forall a b. a -> b -> (a, b)
*Main> :type +v bar'
bar' :: forall b a. a -> b -> (a, b)
*Main> :type +v baz
baz :: forall a. a -> forall b. b -> (a, b)
```

詳しくはGHC User's Guideを読んでください：

* [9.19.1. Inferred vs. specified type variables](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#inferred-vs-specified-type-variables)

# リテラルの脱糖

TypeApplicationsについて勉強したので、リテラルに型適用を使えない理由が説明できます。

まず、 `42` というリテラルは `fromInteger <Integer型の42>` に脱糖されます。なので、 `42 @Int` と書くと `fromInteger <Integer型の42> @Int` と同じ意味になります。

```haskell
42 @Int ~~(脱糖)~> fromInteger 42 @Int
```

これに対し、 `fromInteger` の型は `forall a. Num a => Integer -> a` なので、「値レベルの引数と型引数の順番が一致しない」ためにエラーとなる、というわけです。

```haskell
-- fromInteger の型
fromInteger :: forall a. Num a => Integer -> a

42 @Int ~~(脱糖)~> fromInteger 42 @Int -- エラー！型引数の順番が違う
fromInteger @Int 42 -- これなら動く
```

ちなみにリテラルの型をGHCiで前述の方法で確認すると

```haskell:GHCi
*Main> :type +v 42
42 :: forall {p}. Num p => p
*Main> :type +v 3.1415
3.1415 :: forall {p}. Fractional p => p
```

となって、型適用が使えないことがわかります。

# リテラルにTypeApplicationsを使う方法

こうなると解決法も見えてきます。リテラルの脱糖に使われる `fromInteger` の型を `fromInteger :: Integer -> forall a. Num a => a` にすればリテラルに型適用できるはずです。

リテラルの脱糖に使われる `fromInteger` を自前のものに差し替えるには RebindableSyntax を使います。

というわけで、次のコードはコンパイルが通ります。

```haskell
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE RankNTypes #-}
import Prelude hiding (fromInteger)
import qualified Prelude

fromInteger :: Integer -> forall a. Num a => a
fromInteger = Prelude.fromInteger

main :: IO ()
main = do
  let x = 123 @Integer -- リテラルに
  print (x ^ 2 @Int)   -- 型適用が
                       -- できている
                       -- (575)
```

ちなみに、GHC Proposalsには「リテラルにTypeApplicationsできるようにしようぜ！」という提案があったようなのですが、却下されています。

* [TypeApplications for Overloaded Literals #129](https://github.com/ghc-proposals/ghc-proposals/pull/129)
* [Type Application on literals : haskell](https://www.reddit.com/r/haskell/comments/60seia/type_application_on_literals/)

# おまけ：TypeApplicationsの罠

TypeApplicationsについて説明したついでに、TypeApplicationsの罠っぽいものにも触れておきます。わざわざ別の記事を書くのが面倒なので…。

こういう「型名を表示する」関数があったとします：

```haskell
{-# LANGUAGE ExplicitForAll #-}
import Data.Typeable

typeName :: forall a. Typeable a => Proxy a -> String
typeName proxy = show (typeRep proxy)
```

この `typeName` 関数は `Type` カインドにしか使えません。つまり、DataKinds下で `typeName (Proxy :: Proxy 123)` とするとコンパイルエラーとなります。

そこで、コードを次のように変えてみましょう：

```haskell
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ExplicitForAll #-}
import Data.Typeable

typeName :: forall a. Typeable a => Proxy a -> String
typeName proxy = show (typeRep proxy)

typeName' :: forall (a :: k). Typeable a => Proxy a -> String
typeName' proxy = show (typeRep proxy)
```

新しく作った `typeName'` にはDataKinds下で `typeName' (Proxy :: Proxy 123)` とすることができます。

実はこの場合はカインド注釈を書かなくてもPolyKindsのおかげで元の `typeName` の `a` のカインドも一般化されます。

さて、 `typeName` と `typeName'` はほとんど同等です。ですが、例によってTypeApplicationsの下では違いが生まれます。GHCiで確認してみましょう。

```haskell:GHCi
*Main> :set -fprint-explicit-foralls 
*Main> :type +v typeName
typeName :: forall {k} (a :: k). Typeable a => Proxy a -> String
*Main> :type +v typeName'
typeName' :: forall k (a :: k). Typeable a => Proxy a -> String
```

元の `typeName` 関数はカインド変数 `k` が暗黙であり型適用できませんが、コード中に `(a :: k)` と書いた `typeName'` はカインド変数 `k` にも型適用できます。

教訓としては、「**型や関数をカインド多相にしようとして迂闊にカインド変数を足すとTypeApplicationsを使ったコードが壊れる可能性があるぞ**」ということです。

一般論として、関数の型を一般化するとTypeApplicationsを使ったコードは壊れがちです。筆者がハマった例としては、 `sum` 関数

```haskell
sum :: Num a => [a] -> a
```

に `sum @Int` と型適用したところコンパイルエラーが起こりました。GHCの `sum` 関数の型がリストじゃなくて `Foldable` に一般化されているせいです。あ〜あ

筆者に言わせれば、**型推論を前提に設計された言語に対するTypeApplicationsというのは異質な拡張なのです**。したがって、TypeApplicationsの使用は最小限に控えるべきだと個人的には思っています。`Proxy` 引数を排するために `AllowAmbiguousTypes` と組み合わせるやつも個人的には好きではありません。便利で簡潔なのはわかるけどさ……。

筆者の感覚的に、TypeApplicationsを使っても良いかなと思えるのは、以下の状況です。

* read 系（使えないと辛い）
* `Proxy`（タイプ数節約のため）
* 型クラスのメソッド
    * メソッドについた型注釈にかかわらず、型クラスの型が最初の型引数となります。
    * GNDみたいなことを手動でやりたくなった時に `method = coerce (method @BaseType)` と書くと楽です。