Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Char does not compile without FlexibleContexts

Tags:

haskell

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.

like image 651
XperianX Avatar asked Aug 19 '16 07:08

XperianX


1 Answers

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.

like image 160
Alec Avatar answered Oct 25 '22 01:10

Alec