ViewPatterns влияет на проверку типов непредсказуемым образом - PullRequest
0 голосов
/ 14 ноября 2018

Рассмотрим следующий фрагмент кода:

import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Generic as V

bar :: Int -> UV.Vector Char -> (Text, Text)
bar i v = (t_pre, t_post)
  where
    f = T.pack . V.toList
    (t_pre, t_post) = (\(x, y) -> (f x, f y)) $ V.splitAt i v

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

{-# LANGUAGE ViewPatterns #-}

import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Generic as V

bar :: Int -> UV.Vector Char -> (Text, Text)
bar i v = (t_pre, t_post)
  where
    f = T.pack . V.toList
    (f -> t_pre, f -> t_post) = V.splitAt i v

Будет напечатано следующее сообщение (с -fprint-potential-instances):

    • Ambiguous type variable ‘v0’ arising from a use of ‘V.toList’
      prevents the constraint ‘(V.Vector v0 Char)’ from being solved.
      Relevant bindings include
    f :: v0 Char -> Text (bound at Weird.hs:11:5)
      Probable fix: use a type annotation to specify what ‘v0’ should be.
      These potential instances exist:
    instance V.Vector UV.Vector Char
      -- Defined in ‘Data.Vector.Unboxed.Base’
    ...plus one instance involving out-of-scope types
      instance primitive-0.6.3.0:Data.Primitive.Types.Prim a =>
               V.Vector Data.Vector.Primitive.Vector a
        -- Defined in ‘Data.Vector.Primitive’
    • In the second argument of ‘(.)’, namely ‘V.toList’
      In the expression: T.pack . V.toList
      In an equation for ‘f’: f = T.pack . V.toList
   |
11 |     f = T.pack . V.toList
   |                  ^^^^^^^^

Weird.hs:13:6: error:
    Variable not in scope: f :: UV.Vector Char -> t
   |
13 |     (f -> t_pre, f -> t_post) = V.splitAt i v
   |      ^

Weird.hs:13:18: error:
    Variable not in scope: f :: UV.Vector Char -> t1
   |
13 |     (f -> t_pre, f -> t_post) = V.splitAt i v
   |                  ^

Насколько я понимаю, оба способа выражения вещей полностью эквивалентны, потому что шаблон представления - это просто приложение функции без именования связанной переменной. Я неправильно понимаю шаблоны представления? Является ли десагеринг неожиданным образом взаимодействует с средством проверки типов? Если я приведу определение f на обоих сайтах вызовов, ошибка типа исчезнет.

Я проверял это с GHCi 8.4.3.


Обновление: это ошибка компилятора. См. GHC Trac # 14293 для получения более подробной информации.

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

У вас есть проблема с f, которая вызвана ограничением мономорфизма. Если вы развернете f, дадите ему сигнатуру типа или включите NoMonomorphismRestriction, эта ошибка исчезнет.

Но вы все еще остаетесь с этими ошибками, которые меня удивили!

Weird.hs:13:6: error:
    Variable not in scope: f :: UV.Vector Char -> t
   |
13 |     (f -> t_pre, f -> t_post) = V.splitAt i v
   |      ^

Weird.hs:13:18: error:
    Variable not in scope: f :: UV.Vector Char -> t1
   |
13 |     (f -> t_pre, f -> t_post) = V.splitAt i v
   |                  ^

Полагаю, шаблоны представления не работают, если они определены в одной и той же области видимости. Чтобы увидеть, нужно ли использовать шаблоны представления на верхнем уровне, я попытался

bar :: Int -> UV.Vector Char -> (Text, Text)
bar i v = let (f -> t_pre, f -> t_post) = V.splitAt i v in (t_pre, t_post)
  where
    f = T.pack . V.toList

, который работал нормально. Поэтому я попытался

f = T.pack . V.toList
(f -> t_pre, f -> t_post) = V.splitAt 0 UV.empty

Что не получается, если f не находится в области видимости.

Наконец, если я помещу эти шаблоны в вызов функции

f = T.pack . V.toList
g (f -> t_pre, f -> t_post) = V.splitAt 0 UV.empty

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

0 голосов
/ 14 ноября 2018

Это действительно странно. Это может быть ошибка.

Изменение исходного кода следующим образом

where
f x = T.pack (V.toList x)
(f -> t_pre, f -> t_post) = V.splitAt i v

заставляет GHC запрашивать FlexibleContexts. После этого мы получаем очень странную ошибку:

    Variable not in scope: f :: UV.Vector Char -> t
   |
12 |     (f -> t_pre, f -> t_post) = V.splitAt i v
   |      ^
    Variable not in scope: f :: UV.Vector Char -> t1
   |
12 |     (f -> t_pre, f -> t_post) = V.splitAt i v
   |                  ^

Мне это кажется ошибкой. f там должно быть в области видимости.

Перемещение f в глобальную область:

   ...
   where
   (f -> t_pre, f -> t_post) = V.splitAt i v

f x = T.pack (V.toList x)

Код теперь работает просто отлично. Это даже работает, если мы вернем глобальный f к определению без точек.

Использование явной аннотации типа, как в

where
f :: UV.Vector Char -> Text
f x = T.pack (V.toList x)
(f -> t_pre, f -> t_post) = V.splitAt i v

выдает удивительное сообщение об ошибке

• Variable not in scope: f :: UV.Vector Char -> t
• Perhaps you meant ‘f’ (line 12)

Я не могу понять, что на самом деле происходит. В GHCi оба эти прекрасно работают

> let f = id ; foo (f -> x) = x in foo ()
()
> let bar = foo () where {f = id ; foo (f -> x) = x} in bar
()

Следовательно, мы можем использовать локальный f в шаблонах представления. Тем не менее, когда типу f требуется более осторожный вывод типа (?), Его нельзя использовать в шаблонах представления. Это похоже на ошибку. По крайней мере, сообщение об ошибке должно быть более четким.

...