2014-02-02 3 views
9

У меня есть следующий код, который собирает в моей программе:Записи GADT запись с стесненным типом

{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE GADTs #-} 
{-# LANGUAGE KindSignatures #-} 
{-# LANGUAGE StandaloneDeriving #-} 
{-# LANGUAGE TypeFamilies #-} 

class (Show (Data a)) => HasData (a :: *) where 
    type Data a :: * 

data Foo :: * -> * where 
    Foo :: (HasData a) => String -> Data a -> Int -> Foo a -- bunch of args 

deriving instance Show (Foo a) 

Поскольку число аргументов для Foo застройщика может быть много, я хотел бы написать код, используя синтаксис записи , но я не могу понять, как сделать это с помощью синтаксиса GADT (GHC осуждается типов данных контексты, поэтому я стараюсь избегать их):

data Foo :: * -> * where 
    Foo { 
     getStr :: String, 
     getData :: Data a, -- want (HasData a) 
     getInt :: Int 
    } :: Foo a -- want (HasData a) 

Мне нужно ограничить a в конструкторе Foo «s так же, как я сделал або без синтаксиса записи. Как я могу это сделать?

+0

Что такое 'Data a' for? Вы добавляете фантомный тип для какой-то цели, потому что у вас нет ничего типа 'a', просто материал типа' Data a'. (Например, если вы просто заменяете контекст 'Show' на свой конструктор, что немного упрощает ваши требования.) –

+0

У меня возникла проблема, связанная с этой проблемой. Я немного нуб, и я не мог понять, как заставить «GADTs» работать. Я добавил 2 заголовка для 'KindSignatures' и' GADTs', и теперь он работает. Соответствующая ошибка, так что этот комментарий является googleable, был: 'Незаконная подпись типа 'Free' (Используйте KindSignatures для разрешения подписи)', и теперь я знаю, что это означает, что заголовок должен состоять из '{- # ... # - } ' –

ответ

14

Вы можете добиться этого, объявив запись деструкторов в пределах типа конструктора следующим образом:

{-# LANGUAGE GADTs #-} 
{-# LANGUAGE StandaloneDeriving #-} 
{-# LANGUAGE KindSignatures #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE TypeFamilies #-} 

data Foo :: * -> * where 
    Foo :: HasData a => { -- look - the context declared upfront 
     getStr :: String, -- wow - function declaration in type sig! 
     getData :: Data a, -- only allowed with a HasData instance 
     getInt :: Int 
    } -> Foo a 

, но я подозреваю, что есть более простой способ достижения того, что вы собираетесь делать, если вы делаете что-то более хитрый с типом a чем кажется. Вот простой способ настоять на showable данных:

{-# LANGUAGE GADTs #-} 
{-# LANGUAGE StandaloneDeriving #-} 

data Bar a where 
    Bar :: Show a => { 
     getStr' :: String, 
     getData' :: a, -- can Show 
     getInt' :: Int 
    } -> Bar a -- have (Show a) 
deriving instance Show (Bar a) 

Этим способ имеет преимущество работы для произвольных Showable данных, без экземпляров HasData класса и не используя все эти прагмы компилятора, но это только вам помочь если ваш класс HasData был там, чтобы сделать вещи показательными. У вас может быть какая-то более глубокая цель, которую я не вижу.

(Возможно, вы рассмотрите возможность полного удаления контекста Show, за исключением случаев, когда вы его используете, чтобы упростить все больше и позволить вам создавать экземпляры Functor. Это было бы проще и не требовало бы каких-либо компиляторов. лет я стал задуматься о том, как хранить вещи вообще и делать экземпляры Functor.)

+0

В первом примере, какова цель' HasData a => Data a'? Не будет ли 'HasData a => {record}' выполнять эту работу? – crockeea

+0

Я думал, что OP захотел этого, но при повторном чтении я вижу, что именно там они помещают комментарий, что они хотят этого контекста, не указав, что они хотели написать его именно там. (Я чувствую, что мог бы сказать это более четко с меньшим количеством «тех», но не получилось.) Так что в основном я думал, что OP этого хочет, но теперь я вижу, что, вероятно, слишком много читаю и отредактирую. –

+0

@ Эрик Спасибо; одно меньшее расширение. RankNTypes был почти наверняка не тем, что был после OP, и только вызвал бы запутывающие ошибки компилятора. –

Смежные вопросы