[Haskell] asTypeOf の使い方と仕組み

2021/03/13に公開

想定読者

  • asTypeOf の使い方や仕組みが知りたい。
  • asTypeOf の代わりに ScopedTypeVariables 拡張を使う方法を知りたい。

基本的な動作の確認

Haskell は型推論によって、型を明示しないで済むケースが殆どですが、read関数のように返り値の型を明示する必要のあるケースもあります。

main = do
    print (read "123") -- 変換先の型が特定できないためコンパイルエラー

これを解決する通常の方法は型注釈を利用することです。

main = do
    print (read "123" :: Int)

Prelude モジュールに用意された asTypeOf 関数は、同じように型情報をヒントとして与えることができます。

main = do
    print (read "123" `asTypeOf` (1 :: Int))

asTypeOf は単に第1引数に指定された値を返す関数ですが、第1引数と第2引数の型が一致することが強制されます。

このケースでは read “123" が結果として返りますが、その型が第2引数の (1 :: Int) から Int 型であると推論されるため、型注釈を利用したコードと等価になっています。

実用的なケース

さきほどの例では、返り値の型が事前に Int と決まっていたため型注釈を利用すれば十分でした。しかし、型が動的に決まるケースでは事前に型注釈で明示する方法が使えません。

例として、以下のような関数を考えてみます。

-- |
-- >>> showMin True
-- "False"
-- >>> showMin GT
-- "LT"
--
showMin :: (Show a, Bounded a) => a -> String
showMin _ = show (minBound :: a) -- コンパイルエラー

minBound :: a と型を指定しているため一見するとコンパイルできそうですが、残念ながらコンパイルエラーになります。これは Haskell の関数シグネチャの型変数のスコープは宣言内で閉じられており、関数本体からは参照することができないためです。

このようなケースで asTypeOf を利用できます。

showMin :: (Show a, Bounded a) => a -> String
showMin x = show (minBound `asTypeOf` x) -- 引数に渡された`x`から`minBound`の型を決定

minBound の返り値が、値 x の型であることを明示することでコンパイルが通せています。

showMin True とした場合は minBound :: BoolshowMin GT とした場合は minBound :: Ordering と指定されるイメージです。minBound as type of x と英語っぽく読むこともできますね。

ScopedTypeVariables 拡張

さきほど関数シグネチャの型変数のスコープは宣言内で閉じられていると書きましたが、それを関数本体まで広げる ScopedTypeVariables という GHC 拡張もあります。

それを利用すると以下のように書けます。

{-# LANGUAGE ScopedTypeVariables #-}

minShow :: forall a. (Show a, Bounded a) => a -> String -- forall キーワードが必要
minShow x = show (minBound :: a)

型変数 a のスコープが関数本体まで広げられ、minBound :: a と型注釈として利用できているのが分かります。ただし、関数シグネチャにおいて forall キーワードを指定する必要がある点に注意しましょう。

asTypeOf の仕組み

ところで asTypeOf 関数の実装はどうなっているのでしょうか?

実は、以下のように単なる const として実装されています。

asTypeOf :: a -> a -> a
asTypeOf = const

const は2引数を受け取るものの、常に第1引数を返す関数です。

> :t const
const :: a -> b -> a
> const 1 2
1
> const "42" 'a'
"42"

常に第1引数の値を返すという点では asTypeOf と似ていますが、上記のコードから分かるように2引数の型が同じでなくても構いません。言い換えると、 2引数が同一であるように限定されたバージョンの const が asTypeOf ということになります。

これによって第1引数の型が曖昧でもあっても、第2引数の型と一致するという情報から、第1引数の型を決定できるというのが asTypeOf の仕組みです。

まとめ

  • asTypeOf は値から型情報を与えるのに使える。
  • 第1引数のみが結果として返され、第2引数は型推論の材料として利用される。
  • ScopedTypeVariables 拡張を利用して、型変数のスコープを関数本体まで広げる方法もある。
  • asTypeOfa -> a -> a に型が制限された const 関数。

参考記事

P.S.

最近は Swift プログラマのための Haskell 入門 という連載ブログ記事を書いてます。

Discussion