Можно ли использовать ограничение класса типов в определении нового типа?

Предположим, у нас есть следующее определение newtype:

newtype A = A { _run :: Monad m => A -> [Int] -> m Int }

Это не компилируется с GHC 8.0.2:

error: Not in scope: type variable ‘m’

Замена m конкретным классом типов, таким как IO или [], компилируется, как я и ожидал. Учитывая, что это нормально, почему GHC не разрешает подпись выше? Что не так с добавлением ограничения типа внутри этого newtype?


person Bill M    schedule 17.04.2017    source источник
comment
Работает следующее объявление: newtype A = A { _run :: A -> [Int] -> (forall m. Monad m => m Int) } (требуется расширение {-# LANGUAGE RankNTypes #-}).   -  person ZhekaKozlov    schedule 17.04.2017


Ответы (3)


Это возможно:

{-# LANGUAGE RankNTypes #-}
newtype A = A { _run :: forall m. Monad m => A -> [Int] -> m Int }

Трудно сказать, что вы хотите сделать, но это не очень удобно. Любое значение типа A должно работать для всех монад (вы не можете выбирать).

Это тоже возможно, с теми же ограничениями:

{-# LANGUAGE GADTs #-}
data A where A :: Monad m => (A -> [Int] -> m Int) -> A

Но, возможно, вы имеете в виду что-то более похожее на

newtype A m = A { _run :: A m -> [Int] -> m Int }

Это позволяет использовать значения разных типов A с использованием разных монад.

person ephemient    schedule 17.04.2017
comment
Спасибо, теперь это имеет для меня гораздо больше смысла. Есть ли существенная разница между подходом RankNTypes и подходом GADT? - person Bill M; 17.04.2017
comment
@BillM Помимо опечатки (исправленной) и имени вроде _run? Нет. - person ephemient; 17.04.2017
comment
@ephemient Я попробовал это в GHCi, и с моим определением f ниже способ GADT работал, но способ RankNTypes имел ошибку. - person zbw; 17.04.2017
comment
Я не уверен, как работает редактирование ответов других людей, но я отредактировал его с учетом этого. - person zbw; 17.04.2017
comment
Я пробовал это больше в GHCi, и всякий раз, когда я пытался получить доступ к члену функции из определения GADT, он жаловался, что type variable ‘m’ would escape its scope -- я думаю, что этот способ не сработает. - person zbw; 17.04.2017
comment
Между версиями data и newtype огромная разница. Они означают совершенно разные вещи. Версия data создает экзистенциал. Всякий раз, когда вы применяете конструктор, вы теряете тип m, и его невозможно восстановить. Версия newtype универсальна. Значение, хранимое конструктором, должно быть полиморфным для всех m с экземпляром Monad. Когда вы извлечете его, вы сможете использовать его для любого m. - person Carl; 17.04.2017

Это зависит от того, что вы пытаетесь сохранить в A.

Если вы пытаетесь сохранить любую подобную функцию, пока m является Monad, используйте ее как параметр типа и укажите это ограничение в своих функциях:

newtype A m = A { _run :: A m -> [Int] -> m Int }

myFunction :: Monad m => A m -> A m

Затем вы можете иметь такие вещи, как A [] -> [Int] -> [Int] или A Maybe -> [Int] -> Maybe Int внутри конструктора.

f :: A Maybe -> [Int] -> Maybe Int
f _ (x:_) = Just x
f _ [] = Nothing

g :: Monad m => A m -> [Int] -> m Int
g _ xs = return $ head xs

myA :: A Maybe
myA = A f  -- this works

myOtherA :: Monad m => A m
myOtherA = A g  -- this works too

С другой стороны, если вы хотите, чтобы данные, которые вы храните, были полиморфными, вы можете использовать расширение GHC RankNTypes.

{-# LANGUAGE RankNTypes #-}
newtype A = A { _run :: forall m. Monad m => A -> [Int] -> m Int }

myFunction :: A -> A

В конструкторе не может быть таких вещей, как A -> [Int] -> [Int] или A -> [Int] -> Maybe Int, потому что forall заставляет их быть общими по отношению к любому Monad m, поэтому он должен иметь тип Monad m => A -> [Int] -> Maybe Int.

f :: A -> [Int] -> Maybe Int
f _ (x:_) = Just x
f _ [] = Nothing

g :: Monad m => A -> [Int] -> m Int
g _ xs = return $ head xs

myA :: A
myA = A f  -- this does not work ; it wants forall m. Monad m => m, not []

myOtherA :: A
myOtherA = A g  -- this does work

Это будет действительно полезно, только если вы собираетесь использовать разные конкретные экземпляры Monad для A-значения. Например, линзы работают таким образом, используя разные функторы для разных действий с линзой.

person zbw    schedule 17.04.2017
comment
Спасибо за разъяснение того, как написать конструктор и установить связь с объективами, это заставило меня щелкнуть кучу вещей. - person Bill M; 17.04.2017

Как GHC узнает, какой экземпляр Monad использовать при создании фрагмента данных типа A?

Или, другими словами, переменная типа m не находится в левой части определения типа. Это означает, что он не знает, каким должно быть m, и не может его вычислить. Это неявно.

Я уверен, что есть какой-то способ сделать то, что вы хотите, с расширением, возможно, используя явный forall. (Расширение RankNTypes), однако нам нужно знать, что вам нужно немного больше.

person Julian Leviston    schedule 17.04.2017