如果没有FlexibleContexts,使用Char不会编译

时间:2016-08-19 07:27:42

标签: 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

然而,将数组类型从Char更改为Int是可以的

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

有人可以澄清为什么会这样吗?

感谢。

2 个答案:

答案 0 :(得分:10)

原因实际上是在错误消息中。

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

当您使用文字'_''1'时,Haskell会推断出它们的类型为Char,然后它会遇到{{{{{{{{约束参数不是所有类型变量,但这就是Haskell报告所要求的。签名Haskell 推断就像是

MultiParamTypeClasses

这不是drawLine :: MArray a Char m => ... 之前的问题,因为之前的所有类只有一个参数。然后,如果该参数不是类型变量,那么您所要做的就是检查该实例是否可派生,如果 是一个类型变量,则将其保留为约束。对于MultiParamTypeClasses,您经常会遇到中间情况 - 一些参数被实例化,一些则没有。 FlexibleContexts让你有这种混合(甚至更多)。这是一个安全的扩展(我希望看到它成为Haskell 2020的一部分)。

但是MultiParamTypeClasses呢?为什么不给我同样的错误?

实际上并非直接与Int有关。当Haskell看到文字Int0时,由于数字文字被重载,它只决定一般类型:1。这意味着约束Haskell推断只是

Num a => a

这甚至不需要drawLine :: (Num n, MArray a n m) => ... ,因为所有约束都有类型变量的参数。

答案 1 :(得分:0)

只是基于我的理解的想法 - 当您传入Char值时,它已经是'专业'类型。另一方面,由于您在没有明确键入的情况下传入数字,我相信可以解释为传递更通用的类型,即(Num a) => a。这就是为什么GHC抱怨明确的Char字面意思,而不是数字。

根据我对文档here

的理解
  

Haskell为let或where绑定的每个变量计算一个类型,然后概括这个类型。在Haskell 98中,允许的上下文是受限制的,因此使用实例声明减少了上下文(并且删除了重复的断言和类上下文隐含的那些),直到它们处于允许的形式或者没有适用的实例,在这种情况下错误是报道。