Как распаковать экзистенциальный тип 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 в списке).