Andriy Polishchuk (isorecursive) wrote,
Andriy Polishchuk
isorecursive

Haskell: Generalized Existential Types

С появлением расширения ConstraintKinds появилась возможность написать универсальный экзистеншл под все констрейнты:
data Any c = ∀ a. c a ⇒ Lift a

Здесь 'c' имеет кайнд 'Constraint'. Если сильно хочется, его можно указать явно, импортировав имя 'Constraint' из GHC.Prim и заиспользовав расширение KindSignatures:
data Any (c ∷ * → Constraint) = ∀ a. c a ⇒ Lift a

Погружать внутрь значения можно так же, как в случае обычных экзистеншлов:
a, b, c ∷ Any Show
a = Lift '1'
b = Lift "23"
c = Lift 4

Я несколько раз сталкивался с людьми, которые думают, что каждый раз когда пишешь экзистеншл-обёртку для констрейнта C, нужно писать инстанс C для этой обёртки, который попросту делегирует вызовы завёрнутому значению. Что-то типа такого:
data Showable = ∀ a. Show a ⇒ Showable a

instance Show Showable where
  show (Showable a) = show a

Лучше воздержаться от производства такого бройлерплейта. Ведь с ним даже нельзя будет без распаковывания по-месту-использования вызывать констрейнтанутые функции, не принадлежащие классу. Раньше хорошим решением было под каждый экзистеншл писать, используя Rank2Types, распаковывающий аппликатор, но теперь, когда у нас универсальный экзистеншл, мы можем написать один обобщённый:
(^$) ∷ ∀ c b. (∀ a. c a ⇒ a → b) → Any c → b
f ^$ (Lift x) = f x

Вуаля! Теперь можно писать просто:
d ∷ String
d = show ^$ a ++ concatMap (show ^$) [b, c]

- и так для любых констрейнтов!
Tags: constraint kinds, constraint signatures, existential types, haskell, rank 2 types
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 0 comments