Экспериментирование с экзистенциальными типами. Кажется, отличный способ получить некоторую гибкость типа.
Я поражаю проблему с распаковыванием экзистенциального типа после того, как я обернул его. Мой код следующим образом:
{-# LANGUAGE ExistentialQuantification #-}
class Eq a => Blurb a
data BlurbBox = forall a . Blurb a => BlurbBox a
data Greek = Alpha | Beta deriving Eq
instance Blurb Greek
data English = Ay | Bee deriving Eq
instance Blurb English
box1 :: BlurbBox
box1 = BlurbBox Alpha
box2 :: BlurbBox
box2 = BlurbBox Ay
main = do
case box1 of
BlurbBox Alpha -> putStrLn "Alpha"
BlurbBox Beta -> putStrLn "Beta"
BlurbBox Ay -> putStrLn "Ay"
BlurbBox Bee -> putStrLn "Bee"
Этот код компиляции до основного, затем жалуется на тип Альфы BlurbBox. Как я иду о распаковывании/распаковке экзистенциального типа?
Действительно, экзистенциальные типы не могут быть распакованы, потому что вся их суть в том, что код, ожидающий экзистенциального типа, должен работать абсолютно одинаково (в том смысле, что параметрического полиморфизма) независимо от того, с каким точным типом была создана переменная экзистенциального типа.
Вы можете лучше понять это, если поймете, что
data BlurbBox = forall a . Blurb a => BlurbBox a
переводится в
type BlurbBox = forall b . (forall a . Blurb a => a -> b) -> b
, то есть BlurbBox - это то, что, учитывая полиморфную функцию, которая работает абсолютно для всех Blurbs, может использоваться для получения результата применения этой функции. к какой-то (неизвестной) аннотации.
Таким образом, аналогично тому, как вы не можете написать функцию типа f :: a -> Int и иметь f String = 5 и f Bool = 3, вы не можете отправить по типу 'a' в BlurbBox.
Вы можете прочитать главу в TAPL о экзистенциальных типах. В нем описан перевод, который я предоставил.
Насколько я знаю, вы не можете этого сделать. Вся суть экзистенциальных типов заключается в том, чтобы скрыть тип, чтобы вы могли получить единообразный доступ ко всем «экземплярам» (что-то вроде динамической отправки методов подкласса в Java и других объектно-ориентированных языках).
Итак, в вашем примере вашим «интерфейсом» является BlurbBox
, и вы могли бы использовать его для единообразного применения некоторого метода к разным BlurbBox, не беспокоясь о том, какой внутренний тип a
(например, если Blurb
подклассы Показать
, тогда вы можете иметь [BlurbBox]
и печатать каждый элемент списка, не зная точный внутренний тип каждого BlurbBox
в списке).
Вы не можете * специализировать тип после того, как спрятали его. Добавьте ограничение или метод в Blurb
, если вам нужна такая операция.
-- choose one
class (Eq a, Show a) => Blurb a where
printBlurb :: a -> IO ()
instance Blurb Greek where
printBlurb Alpha = putStrLn "Alpha"
...
class (Eq a, Show a) => Blurb a
data Greek deriving (Eq, Show)
...
data BlurbBox = forall a. (Blurb a, Show a) => BlurbBox a
data Greek deriving (Eq, Show)
...
* Я бы очень рекомендовал против этого, но если вы действительно хотите…
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Dynamic
data Greek = Alpha | Beta deriving (Eq, Typeable)
data English = Ay | Bee deriving (Eq, Typeable)
box1 :: Dynamic
box1 = toDyn Alpha
box2 :: Dynamic
box2 = toDyn Ay
main = do
case fromDynamic box1 of
Just Alpha -> putStrLn "Alpha"
Just Beta -> putStrLn "Beta"
Nothing -> case fromDynamic box1 of
Just Ay -> putStrLn "Ay"
Just Bee -> putStrLn "Bee"