Использование монадического типа ранга 2

Вот код:

{-# LANGUAGE RankNTypes, FlexibleContexts, ScopedTypeVariables #-}

module Foo where

import Data.Vector.Generic.Mutable as M
import Data.Vector.Generic as V
import Control.Monad.ST
import Control.Monad.Primitive
import Control.Monad

data DimFun v s r = 
  DimFun {dim::Int, func :: v (PrimState s) r -> s ()}

runFun :: (Vector v r) => 
  (forall s . (PrimMonad s) => DimFun (Mutable v) s r) -> v r -> v r
runFun t x = runST $ do
  y <- thaw x
  evalFun t y
  unsafeFreeze y

evalFun :: (PrimMonad s, MVector v r) => DimFun v s r -> v (PrimState s) r -> s ()
evalFun (DimFun dim f) y | dim == M.length y = f y

fm :: (MVector v r, PrimMonad s, Num r, Monad m) => m (DimFun v s r)
fm = error ""

f :: forall v r m . (Vector v r, Num r, Monad m) => m (v r -> v r)
f = liftM runFun $ (fm :: forall s . (PrimMonad s) => m (DimFun (Mutable v) s r))

Это приводит к ошибкам:

Couldn't match type ‘DimFun (Mutable v) s0 r’
              with ‘forall (s :: * -> *). PrimMonad s => DimFun (Mutable v) s r’
Expected type: DimFun (Mutable v) s0 r -> v r -> v r
  Actual type: (forall (s :: * -> *).
                PrimMonad s =>
                DimFun (Mutable v) s r)
               -> v r -> v r
Relevant bindings include
  f :: m (v r -> v r) (bound at Testing/Foo.hs:36:1)
In the first argument of ‘liftM’, namely ‘runFun’
In the expression: liftM runFun

Однако я не уверен, как исправить или диагностировать проблему. Это может быть так же просто, как удачно расположенная (и хорошо написанная) подпись типа.

Пытаясь понять, что происходит, я пишу немонадную версию (бесполезную для меня), но она компилируется:

gm :: (MVector v r, PrimMonad s, Num r) => DimFun v s r
gm = error ""

g :: forall v r m . (Vector v r, Num r) => v r -> v r
g = runFun (gm :: forall s . (PrimMonad s) => DimFun (Mutable v) s r)

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


person crockeea    schedule 14.07.2014    source источник
comment
Похоже, вы сделали двойной пост. Это, вероятно, следует удалить, и ответы идут здесь: stackoverflow.com/questions/24744294/   -  person jberryman    schedule 15.07.2014
comment
@jberryman Для меня не очевидно, что проблемы в этих двух вопросах связаны (хотя некоторые подмножества кода на самом деле одинаковы). Вот почему я разместил как два вопроса.   -  person crockeea    schedule 15.07.2014
comment
Используйте переменные типа с областью действия, чтобы исправить тип первого аргумента. Кроме того, попробуйте вывести forall в глобальную область видимости в типе для runFun.   -  person nomen    schedule 15.07.2014
comment
Возможно, это проблема непредикативного полиморфизма. Возможно, вы могли бы уйти, обернув свой универсальный квантифицированный тип в контейнер.   -  person J. Abrahamson    schedule 15.07.2014
comment
@nomen Что конкретно вы имеете в виду? У меня есть явная подпись для первого аргумента funFun в f. Сам runFun не компилируется без аргумента ранга 2: мы выполняем вычисления в конкретной монаде ST.   -  person crockeea    schedule 15.07.2014
comment
@ J.Abrahamson Я попробовал ваше предложение и заставил его работать. Конечно, мне бы не хотелось явно обертывать тип ранга 2. Хотя я мало что понимаю в непредикативных типах, добавление расширения само по себе не решило проблему.   -  person crockeea    schedule 15.07.2014
comment
Просто добавление расширения просто допускает непредикативные типы, но, скорее всего, логический вывод по-прежнему не может работать в их присутствии. Скорее всего, вам понадобится больше аннотаций.   -  person J. Abrahamson    schedule 15.07.2014
comment
@ J.Abrahamson Я бы предпочел больше аннотаций, чем использовать обертку. Куда пойдут аннотации и как они могут выглядеть?   -  person crockeea    schedule 15.07.2014
comment
@Eric Как только вы увидите, сколько аннотаций вам нужно, чтобы это заработало, вы, вероятно, передумаете.   -  person Carl    schedule 15.07.2014
comment
@Eric В первом приближении средство вывода просто сдается всякий раз, когда сталкивается с непредикативным типом. Вам придется аннотировать каждую переменную.   -  person J. Abrahamson    schedule 15.07.2014


Ответы (1)


Одним из решений является перемещение ограничения PrimMonad внутрь типа данных DimFun.

data DimFun v r = DimFun 
   { dim  :: Int
   , func :: forall s . PrimMonad s => v (PrimState s) r -> s ()
   }

Остальной код компилируется как есть, удаляя параметр s из DimFun:

runFun :: Vector v r => DimFun (Mutable v) r -> v r -> v r
runFun = ...

evalFun :: (PrimMonad s, MVector v r) => DimFun v r -> v (PrimState s) r -> s () 
evalFun = ...

fm :: (MVector v r, Num r, Monad m) => m (DimFun v r)
fm = ...

f :: (Vector v r, Num r, Monad m) => m (v r -> v r)
f = liftM runFun fm

Перемещение ограничения класса в тип данных может показаться вам пугающим, но на самом деле у вас все равно уже было ограничение класса. PrimState — это связанное семейство типов PrimMonad, поэтому для создания или использования v (PrimState s) r вам необходимо ограничение PrimMonad.

Если вы все же хотите избежать этого, вам придется изменить тип чего-либо. Чтобы понять, почему ваша функция имеет неверный тип, рассмотрите следующее (для чего требуется ImpredictiveTypes):

fm :: (MVector v r, PrimMonad s, Num r, Monad m) => m (DimFun v s r)
fm = error ""

g :: (Vector v r, Monad m) 
  => m (forall s . PrimMonad s => DimFun (Mutable v) s r) -> m (v r -> v r)
g = liftM runFun 

Следует пояснить, почему g fm имеет неверный тип: g ожидает чего-то, где forall s . PrimMonad s => находится внутри m, чего нельзя сказать о fm. Вам нужно будет написать функцию типа:

fm' :: (MVector v r, Monad m, Num r) => m (forall s . PrimMonad s => DimFun v s r)
fm' = error ""

f :: forall v r m . (Vector v r, Num r, Monad m) => m (v r -> v r)
f = g fm'
person user2407038    schedule 14.07.2014
comment
Оба отличные решения. Я просто хочу, чтобы было более очевидно, что это то, что мне нужно было сделать! - person crockeea; 15.07.2014
comment
Это также отвечает на этот вопрос, так как мне больше не нужно сопоставлять шаблоны на ранг-2 типа. Единственная другая вещь, которую я мог бы попросить, это подсказка о том, как я должен знать, что это правильный поступок в будущем. Почему я должен предпочитать типы ранга 2 внутри данных, а не над данными? - person crockeea; 15.07.2014
comment
На мой взгляд, вы всегда должны выбирать поля с универсальной количественной оценкой в ​​типах данных, а не непредсказуемые типы. С непредсказуемыми типами почти всегда нелегко сказать, каким должен быть правильный тип, и, как вы видите, разница между правильным и неправильным типом часто очень мала. Что еще более важно, средство проверки типов для вас бесполезно, так как оно не может вывести непредсказуемые типы, а ошибки типов, которые вы получите, будут ужасны. - person user2407038; 15.07.2014