Один из способов получить эффект, на который, похоже, рассчитывает ваша выборка,
Сначала я хочу описать, как добиться того, к чему вы стремитесь.Давайте снова посмотрим на ваш последний пример кода:
-- Note, Frob is an instance of class Frobbable
getFrobbable :: (Frobbable a) => Frob -> a
getFrobbable x = x
По сути, это операция приведения.Он берет Frob
и просто забывает, что это такое, сохраняя только знание того, что у вас есть экземпляр Frobbable
.
. В Haskell есть идиома для достижения этой цели.Требуется расширение ExistentialQuantification
в GHC.Вот пример кода:
{-# LANGUAGE ExistentialQuantification #-}
module Foo where
class Frobbable a where
getInt :: a -> Int
data Frob = Frob Int
instance Frobbable Frob where
getInt (Frob i) = i
data FrobbableWrapper = forall a . Frobbable a => FW a
instance Frobbable FrobbableWrapper where
getInt (FW i) = getInt i
Ключевой частью является структура данных FrobbableWrapper
.С его помощью вы можете написать следующую версию вашей getFrobbable
функции приведения:
getFrobbable :: Frobbable a => a -> FrobbableWrapper
getFrobbable x = FW x
Эта идиома полезна, если вы хотите иметь гетерогенный список, элементы которого имеют общий класс типов, даже если они не могутподелитесь общим типом.Например, хотя Frobbable a => [a]
не позволяет вам смешивать различные экземпляры Frobbable
, список [FrobbableWrapper]
, безусловно, будет.
Почему код, который вы опубликовали, не разрешен
Теперь, почему вы не можете написать свою операцию приведения как есть?Все дело в том, чего можно достичь, если вашей исходной функции getFrobbable
разрешено проверять тип.
Уравнение getFrobbable x = x
действительно следует рассматривать как уравнение.x
никак не изменяется;таким образом, ни его тип.Это сделано по следующей причине:
Давайте сравним getFrobbable
с другим объектом.Рассмотрим
anonymousFrobbable :: Frobbable a => a
anonymousFrobbable = undefined
(код, включающий undefined
, является отличным источником неуклюжего поведения, когда вы действительно хотите проявить свою интуицию.)
Теперь предположим, что кто-то приходит и вводит определение данныхи такая функция, как
data Frob2 = Frob2 Int Int
instance Frobbable Frob2 where
getInt (Frob2 x y) = y
useFrobbable :: Frob2 -> [Int]
useFrobbable fb2 = []
Если мы перейдем в ghci, мы можем сделать следующее:
*Foo> useFrobbable anonymousFrobbable
[]
Нет проблем: подпись anonymousFrobbable
означает «Вы выбираете экземпляр Frobbableи я сделаю вид, что я из этого типа. "
Теперь, если мы попытаемся использовать вашу версию getFrobbable
, вызов типа
useFrobbable (getFrobbable someFrob)
приведет к следующему:
someFrob
должно быть типа Frob
, поскольку оно присваивается getFrobbable
. (getFrobbable someFrob)
должно иметь тип Frob2
, поскольку онодается useFrobbable
- Но по уравнению
getFrobbable someFrob = someFrob
мы знаем, что getFrobbable someFrob
и someFrob
имеют одинаковый тип.
Таким образом, система приходит к выводу, что Frob
и Frob2
относятся к одному и тому же типу, хотя это не так.Следовательно, эти рассуждения несостоятельны, что в конечном итоге является рациональным объяснением того, почему опубликованная вами версия getFrobbable
не проверяет тип.