Как распаковать экзистенциальный тип haskell?
Экспериментирование с экзистенциальными типами. Кажется, это отличный способ получить гибкость в типе.
У меня проблема с распаковкой экзистенциального типа после того, как я его завернул. Мой код выглядит следующим образом:
{-# 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 Alpha. Как перейти к распаковке/распаковке экзистенциального типа?
Ответы
Ответ 1
Действительно, типы экзистенции не могут быть распакованы, потому что их суть состоит в том, что код, ожидающий существования экзистенциального типа, должен работать абсолютно одинаково (в смысле параметрического полиморфизма), независимо от того, какой точный тип - инстанцирован.
Вы можете понять это лучше, понимая, что
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 на экзистенциальные типы. Он описывает перевод, который я предоставил.
Ответ 2
Вы не можете * специализировать тип после того, как скрыли его. Добавьте ограничение или метод в 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)
...
* Я бы очень рекомендовал против этого, но если вы действительно хотели & hellip;
{-# 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"
Ответ 3
Насколько я знаю, вы не можете этого сделать. Вся суть экзистенциальных типов заключается в том, чтобы скрыть тип, так что вы можете получить доступ ко всем "экземплярам" равномерно (вроде динамической отправки методов подкласса в Java и других объектно-ориентированных языках).
Итак, в вашем примере ваш "интерфейс" BlurbBox
, и вы будете использовать его для равномерного применения одного метода к различным BlurbBoxes, не беспокоясь о том, что такое внутренний тип a
(например, если Blurb
подклассы Show
, то вы можете иметь [BlurbBox]
и печатать каждый элемент списка без необходимости знать точный внутренний тип каждого BlurbBox
в списке).