GHC использует универсальный экземпляр, если ограничение отсутствует?

Я экспериментировал с этой простой реализацией HLists и функцией hasInt который возвращается True если Int является членом списка:

{-# LANGUAGE FlexibleInstances #-}

data HNil = HNil
  deriving (Show, Read)

data HCons a b = HCons a b
  deriving (Show, Read)

class HasInt a where
  hasInt :: a -> Bool

instance HasInt HNil where
  hasInt _ = False

instance HasInt as => HasInt (HCons a as) where
  hasInt (HCons a as) =  (isInt a) || (hasInt as)

class IsInt a where
  isInt :: a -> Bool

instance IsInt Int where
  isInt _ = True

instance {-# OVERLAPPABLE #-} IsInt a where
  isInt _ = False

three = 3 :: Int

main = do
  putStrLn $ "isInt three = " ++ show (isInt three) -- True
  putStrLn $ "isInt True  = " ++ show (isInt True)  -- False
  print $ hasInt $ HCons three $ HCons True HNil    -- False ???

Это не дает ожидаемых результатов. Тем не менее, это похоже на работу, если я изменяю:

    instance HasInt as => HasInt (HCons a as) where

чтобы:

    instance (IsInt a, HasInt as) => HasInt (HCons a as) where

С другой стороны, я обычно ожидаю, что GHC будет жаловаться, если я использую функцию класса type, но не включаю ограничение, и я не получаю никаких таких указаний в этом случае.

Понятно, что это должно что-то делать с экземпляром catch-all IsInt a, Я получу Could not deduce (IsInt a) arising from a use of 'isInt'ошибка, если я заменяю универсальный экземпляр на:

instance IsInt Bool where isInt _ = False
instance IsInt HNil where isInt _ = False

У меня такой вопрос: является ли это ожидаемым поведением GHC - что он будет беззвучно использовать универсальный экземпляр, если нет явного ограничения класса типа?

1 ответ

Решение

Да, это ожидаемое поведение. Если ты пишешь

instance Foo a

вы заявляете, что все типы являются Fooи GHC вам верит.

Это на 100% аналогично следующему:

foo :: Int -> Bool
foo x = x > 0

Даже если у вас нет Ord Int в контексте GHC знает, что есть такой случай. Аналогично, в:

bar :: a -> b
bar x = {- use the Foo instance for x here -}

Даже если у вас нет Foo a в контексте GHC знает, что есть такой случай.

Другие вопросы по тегам