Функция "переименование" прерывает код

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

{-# LANGUAGE RankNTypes #-}

module Foo where

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

-- an in-place vector function with dimension
data DimFun v m r = 
  DimFun Int (v (PrimState m) r -> m ())

eval :: (PrimMonad m, MVector v r) => DimFun v m r -> v (PrimState m) r -> m ()
eval = error ""

iterateFunc :: (PrimMonad m, MVector v r)
            => (forall v' . (MVector v' r) => DimFun v' m r) -> DimFun v m r
iterateFunc = error ""

f :: (PrimMonad m, MVector v r)
      => DimFun v m r
f = error ""

iteratedF :: (MVector v r, PrimMonad m) 
           => v (PrimState m) r -> m ()
iteratedF y = 
    let f' = f
    in eval (iterateFunc f') y

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

Testing/Foo.hs:87:14:
    Could not deduce (MVector v0 r) arising from a use of ‘f’
    from the context (MVector v r, PrimMonad m)
      bound by the type signature for
                 iteratedF :: (MVector v r, PrimMonad m) =>
                              v (PrimState m) r -> m ()
      at Testing/Foo.hs:(84,14)-(85,39)
    The type variable ‘v0’ is ambiguous
    Relevant bindings include
      f' :: DimFun v0 m r (bound at Testing/Foo.hs:87:9)
      y :: v (PrimState m) r (bound at Testing/Foo.hs:86:11)
      iteratedF :: v (PrimState m) r -> m ()
        (bound at Testing/Foo.hs:86:1)
    In the expression: f
    In an equation for ‘f'’: f' = f
    In the expression: let f' = f in eval (iterateFunc f') y

Testing/Foo.hs:88:26:
    Couldn't match type ‘v0’ with ‘v'’
      because type variable ‘v'’ would escape its scope
    This (rigid, skolem) type variable is bound by
      a type expected by the context: MVector v' r => DimFun v' m r
      at Testing/Foo.hs:88:14-27
    Expected type: DimFun v' m r
      Actual type: DimFun v0 m r
    Relevant bindings include
      f' :: DimFun v0 m r (bound at Testing/Foo.hs:87:9)
    In the first argument of ‘iterateFunc’, namely ‘f'’
    In the first argument of ‘eval’, namely ‘(iterateFunc f')’
Failed, modules loaded: none.

Однако, если я изменил определение iteratedF на

iteratedF y = eval (iterateFunc f) y

код компилирует GHC 7.8.2. Этот вопрос касается не странно выглядящих сигнатур или типов данных, а просто: почему переименование f в f' нарушает код? Кажется, что это должно быть ошибкой для меня.

Ответ 1

Проблема, конечно, не в переименовании, а в привязке к новой переменной. Так как iterateFunc - Rank-2, ему нужна функция полиморфного аргумента. Конечно, f является полиморфным в v, поэтому его можно использовать. Но когда вы пишете f' = f, неясно, какой тип f' должен быть: тот же полиморфный тип, что и f, или некоторый мономорфный тип, возможно зависящий от некоторого отношения к другой переменной типа в iteratedF, который компилятор hasn ' t выведено еще.

Компилятор по умолчанию использует мономорфную опцию; так как ци говорит, что это ошибка ограничения мономорфизма здесь, поэтому, если вы отключите его, код действительно компилируется.

Тем не менее, та же проблема может появиться даже без ограничения мономорфизма в коде RankNTypes, ее нельзя полностью избежать. Единственное надежное исправление - это локальная подпись, обычно требующая ScopedTypeVariables.

Ответ 2

Отключив ограничение мономорфизма, я могу скомпилировать ваш код. Итак, просто добавьте

{-# LANGUAGE NoMonomorphismRestriction #-}

в начале файла.

Причиной ошибки типа является то, что определение

let f' = f

не использует шаблон функции (например, f' x y = ...), поэтому ограничение мономорфизма пинается и заставляет f' мономорфно, а iterateFunc требуется полиморфная функция.

В качестве альтернативы добавьте аннотацию типа

let f' :: (PrimMonad m, MVector v r) => DimFun v m r
    f' = f