Итак, на днях я понял, как написать эту функцию (требуется base-4.7.0.0
или новее):
{-# LANGUAGE ScopedTypeVariables, TypeOperators, GADTs #-}
import Data.Typeable
-- | Test dynamically whether the argument is a 'String', and boast of our
-- exploit if so.
mwahaha :: forall a. Typeable a => a -> a
mwahaha a = case eqT :: Maybe (a :~: String) of
Just Refl -> "mwahaha!"
Nothing -> a
Итак, я увлекся и решил попробовать это, чтобы написать функцию, которая проверяет, является ли ее тип аргумента экземпляром Show
. Это, если я правильно понимаю, не должно работать, потому что TypeRep
существует только для мономорфных типов. Таким образом, это определение, естественно, не имеет вида typecheck:
isShow :: forall a b. (Typeable a, Typeable b, Show b) => a -> Bool
isShow a = case eqT :: Maybe (a :~: b) of
Just Refl -> True
Nothing -> False
{-
/Users/luis.casillas/src/scratch.hs:10:11:
Could not deduce (Typeable b0)
arising from the ambiguity check for ‘isShow’
from the context (Typeable a, Typeable b, Show b)
bound by the type signature for
isShow :: (Typeable a, Typeable b, Show b) => a -> Bool
at /Users/luis.casillas/src/scratch.hs:10:11-67
The type variable ‘b0’ is ambiguous
In the ambiguity check for:
forall a b. (Typeable a, Typeable b, Show b) => a -> Bool
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature for ‘isShow’:
isShow :: forall a b. (Typeable a, Typeable b, Show b) => a -> Bool
-}
Обратите внимание на сообщение To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
. Если я включу эту прагму, определение typechecks, но...
{-# LANGUAGE ScopedTypeVariables, TypeOperators, GADTs #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
import Data.Typeable
isShow :: forall a b. (Typeable a, Typeable b, Show b) => a -> Bool
isShow a = case eqT :: Maybe (a :~: b) of
Just Refl -> True
Nothing -> False
{- Typechecks, but...
>>> isShow 5
False
>>> isShow (id :: String -> String)
False
-}
Что здесь происходит? Какой тип компилятор выбирает для b
? Является ли это переменной типа Сколема, à la ExistentialTypes
?
О, ну, я просто задал вопрос и быстро понял, как ответить на него:
whatsTheTypeRep :: forall a b. (Typeable a, Typeable b, Show b) => a -> TypeRep
whatsTheTypeRep a = typeRep (Proxy :: Proxy b)
{-
>>> whatsTheTypeRep 5
()
>>> isShow ()
True
-}
Мне все еще интересно узнать, что происходит здесь. Это правило по умолчанию?