简体   繁体   中英

Using Char does not compile without FlexibleContexts

sierpinski :: Int -> Array (Int,Int) Char
sierpinski l = runSTArray $ do
  arr <- newArray ((1,1),(63,32)) '_'
  let drawLine x y len = forM_ [-len..len] $ \s -> writeArray arr (x+s,y-len) '1' --this part does not compile without FlexibleContexts \\* Non type-variable argument in the constraint: MArray a0 Char m (Use FlexibleContexts to permit this)
  let rek n x y h | n == 0 = forM_ [0..h-1] (drawLine x y)
                  | otherwise = do
                    rek (n-1) x y (div h 2)
                    rek (n-1) (x + div h 2) (y - div h 2) (div h 2)
                    rek (n-1) (x - div h 2) (y - div h 2) (div h 2)
  rek l 32 32 32
  return arr

However changing array type from Char to Int is okay

sierpinski :: Int -> Array (Int,Int) Int
sierpinski l = runSTArray $ do
  arr <- newArray ((1,1),(63,32)) 0
  let drawLine x y len = forM_ [-len..len] $ \s -> writeArray arr (x+s,y-len) 1 --now ok
  let rek n x y h | n == 0 = forM_ [0..h-1] (drawLine x y)
                  | otherwise = do
                    rek (n-1) x y (div h 2)
                    rek (n-1) (x + div h 2) (y - div h 2) (div h 2)
                    rek (n-1) (x - div h 2) (y - div h 2) (div h 2)
  rek l 32 32 32
  return arr

Can someone clarify why is this happening?

Thanks.

The reason is actually in the error message.

Non type-variable argument in the constraint: 
  MArray a0 Char m (Use FlexibleContexts to permit this)

When you use the literals '_' and '1' , Haskell infers that their type is Char , then it runs up against the most typical problem that you get with constraints that are MultiParamTypeClasses - constraints arguments aren't all type variables, yet that is what the Haskell report requires them to be. The signature Haskell wants to infer is something like

drawLine :: MArray a Char m => ...

This wasn't a problem before MultiParamTypeClasses since all classes before had only one argument. Then, if that argument was not a type variable, all you had to do was check that that instance was derivable and if it was a type variable, you'd keep it as a constraint. With MultiParamTypeClasses you often end up with in-between cases - some of the arguments are instantiated, some aren't. FlexibleContexts lets you have this sort of mixture (and even some more). This is a safe extension (which I would love to see be part of Haskell 2020).

But what about Int ? Why doesn't it give me the same error?

It actually isn't directly about Int . When Haskell sees the literals 0 and 1 , since numeric literals are overloaded, it only decides on a general type: Num a => a . That means that the constraint Haskell infers is just

drawLine :: (Num n, MArray a n m) => ...

And this doesn't even need FlexibleInstances since all constraints have arguments that are type variables.

Just a thought based on my understanding - when you pass in a Char value, it is already a 'specialised' type. On the other hand, since you pass in numbers without explicitly typing, I believe that could be interpreted as passing a more general type ie (Num a) => a . That is why GHC complains for explicit Char literal, but not for the numbers.

Basing this off my understanding of the documentation here :

Haskell computes a type for each variable bound by let or where, and then generalizes this type. In Haskell 98, the allowed contexts are restricted, so contexts are reduced using instance declarations (and duplicate assertions and those implied by class contexts are removed) until either they are in the allowed form or no instance is applicable, in which case an error is reported.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM