Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewPatterns affects typechecking in an unpredictable manner

Tags:

haskell

ghc

Consider the following code snippet:

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

This compiles ok as you might expect. However, if you replace the function calls with view patterns, you get a type error.

{-# 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

This prints the following message (with -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
   |                  ^

My understanding is that both the ways of expressing things are entirely equivalent, because a view pattern is just function application without naming the bound variable. Am I misunderstanding view patterns? Is it the desugaring that is interacting with the type checker in an unexpected manner? If I inline the definition of f at both the call sites, the type error goes away.

I've tested this with GHCi 8.4.3.


Update: This is a compiler bug. See GHC Trac #14293 for more details.

like image 534
typesanitizer Avatar asked Mar 05 '23 01:03

typesanitizer


1 Answers

You have a problem with f which is caused by the monomorphism restriction. If you eta expand f, give it a type signature, or turn on NoMonomorphismRestriction, then this error will go away.

But you're still left with these errors, which came as a surprise to me!

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
   |                  ^

I guess view patterns don't work if they are defined in the same scope. To see if view patterns needed to be top-level, I tried

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

which worked fine. So I tried

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

Which fails with f not in scope.

Finally, if I put those patterns under a function call

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

then it's fine again. So I guess the rule is that a "value" pattern binding can't use a view pattern which is defined in the same scope. I find that weird, it might even be a bug.

like image 106
luqui Avatar answered Apr 30 '23 12:04

luqui