このように f
を定義して実行したい。
f :: String -> String
f = show . read
main :: IO ()
main = putStrLn $ f "True"
エラーが出る。
Prelude> :l test.hs
[1 of 1] Compiling Main ( test.hs, interpreted )
test.hs:5:5: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘show’
prevents the constraint ‘(Show a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
...plus 22 others
...plus 16 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the first argument of ‘(.)’, namely ‘show’
In the expression: show . read
In an equation for ‘f’: f = show . read
test.hs:5:12: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘read’
prevents the constraint ‘(Read a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Read Ordering -- Defined in ‘GHC.Read’
instance Read Integer -- Defined in ‘GHC.Read’
instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’
...plus 22 others
...plus 7 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the second argument of ‘(.)’, namely ‘read’
In the expression: show . read
In an equation for ‘f’: f = show . read
Failed, modules loaded: none.
show
と read
の対象とする型 a0
がきちんと決まってないのが原因なので、これを f
に対して指定できるようにする。
f :: (Show a, Read a) => String -> String
f = (show :: a -> String) . read
再びエラーとなる。
Prelude> :r
[1 of 1] Compiling Main ( test.hs, interpreted )
test.hs:4:6: error:
• Could not deduce (Read a0)
from the context: (Show a, Read a)
bound by the type signature for:
f :: (Show a, Read a) => String -> String
at test.hs:4:6-41
The type variable ‘a0’ is ambiguous
• In the ambiguity check for ‘f’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature:
f :: (Show a, Read a) => String -> String
Failed, modules loaded: none.
a
が Show
と Read
のインスタンスだと言ってる割には、型の本文に a
が登場しないのでこれを決定する方法がない。
AllowAmbiguousTypes
を使うとこのチェックを遅らせられるよと教えてくれているので、有効にする。
が、これまたゴツいエラー。
Prelude> :r
[1 of 1] Compiling Main ( test.hs, interpreted )
test.hs:5:6: error:
• Could not deduce (Show a2) arising from a use of ‘show’
from the context: (Show a, Read a)
bound by the type signature for:
f :: (Show a, Read a) => String -> String
at test.hs:4:1-41
Possible fix:
add (Show a2) to the context of
an expression type signature:
a2 -> String
• In the first argument of ‘(.)’, namely ‘(show :: a -> String)’
In the expression: (show :: a -> String) . read
In an equation for ‘f’: f = (show :: a -> String) . read
test.hs:5:29: error:
• Could not deduce (Read a0) arising from a use of ‘read’
from the context: (Show a, Read a)
bound by the type signature for:
f :: (Show a, Read a) => String -> String
at test.hs:4:1-41
The type variable ‘a0’ is ambiguous
These potential instances exist:
instance Read Ordering -- Defined in ‘GHC.Read’
instance Read Integer -- Defined in ‘GHC.Read’
instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’
...plus 22 others
...plus 7 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the second argument of ‘(.)’, namely ‘read’
In the expression: (show :: a -> String) . read
In an equation for ‘f’: f = (show :: a -> String) . read
Failed, modules loaded: none.
これは show :: a -> String
が暗黙的に show :: forall a. a -> String
と解釈されているのが原因なので、 ScopedTypeVariables
拡張を使う。
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
f :: forall a. (Show a, Read a) => String -> String
f = (show :: a -> String) . read
main :: IO ()
main = putStrLn $ f "True"
f
の定義についてのエラーはなくなった。後は呼び出し側。
*Main> :r
[1 of 1] Compiling Main ( test.hs, interpreted )
test.hs:8:19: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘f’
prevents the constraint ‘(Read a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Read Ordering -- Defined in ‘GHC.Read’
instance Read Integer -- Defined in ‘GHC.Read’
instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’
...plus 22 others
...plus 7 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the second argument of ‘($)’, namely ‘f "True"’
In the expression: putStrLn $ f "True"
In an equation for ‘main’: main = putStrLn $ f "True"
Failed, modules loaded: none.
型 a
を指定すればいいのだから、ここで TypeApplications
の登場。
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
f :: forall a. (Show a, Read a) => String -> String
f = (show :: a -> String) . read
main :: IO ()
main = putStrLn $ f @Bool "True"
Prelude> :r
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> :main
True
めでたしめでたし。せっかく TypeApplications
を使うなら、 f
の定義ももう少し簡潔になる。
f :: forall a. (Show a, Read a) => String -> String
f = show @a . read
余談1. AllowAmbiguousTypes
の妥当性
型変数作って TypeApplications
使って適用したいだけなのに突然 AllowAmbiguousTypes
拡張が出てきてなんだよって思ったかもしれないが、
論文中ではこいつの必要性は書いてあって、そもそもは TypeApplications
を有効にすると自動的に有効になるようになってた。
このへんのメールのやり取り でない方がいいだろって話になって、
このコミットで消された。
余談2. Proxy
TypeApplications
を使えるコンパイラを持っていないあなたでも、型の役割をする式である Proxy
型を使うことができるのだ!
副作用で型の本文に a
が入るようになるので、 AllowAmbiguousTypes
拡張も不要になる。
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Proxy
f :: forall proxy a. (Show a, Read a) => proxy a -> String -> String
f _ = (show :: a -> String) . read
main :: IO ()
main = putStrLn $ f (Proxy :: Proxy Bool) "True"
だが、勘がいいあなたなら気がついてしまったかもしれない。 Proxy
は可読性をあげてくれるだけで、なくても困らないことを。
{-# LANGUAGE ScopedTypeVariables #-}
f :: forall proxy a. (Show a, Read a) => proxy a -> String -> String
f _ = (show :: a -> String) . read
main :: IO ()
main = putStrLn $ f [False] "True"
余談3. Type defaulting in GHCi
GHCiのデフォルト型の割り当ては強力 なので、なんと f
がそのままコンパイルできてしまうのだ!! ただし、勝手に ()
になってしまう。
*Main> f = show . read
*Main> f "()"
"()"
*Main> f "True"
"*** Exception: Prelude.read: no parse