[Haskell] asTypeOf の使い方と仕組み
想定読者
-
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 :: Bool
、showMin 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 拡張を利用して、型変数のスコープを関数本体まで広げる方法もある。
-
asTypeOf
はa -> a -> a
に型が制限されたconst
関数。
参考記事
P.S.
最近は Swift プログラマのための Haskell 入門 という連載ブログ記事を書いてます。
Discussion