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
有人可以澄清为什么会这样吗?
感谢。
答案 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看到文字Int
和0
时,由于数字文字被重载,它只决定一般类型: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中,允许的上下文是受限制的,因此使用实例声明减少了上下文(并且删除了重复的断言和类上下文隐含的那些),直到它们处于允许的形式或者没有适用的实例,在这种情况下错误是报道。