【发布时间】:2014-05-26 17:54:20
【问题描述】:
今天找到this post on Quora,声称是
factorial(n) = def $ do
assert (n<=0) "Negative factorial"
ret <- var 1
i <- var n
while i $ do
ret *= i
i -= 1
return ret
可能是正确的 Haskell 代码。我很好奇,ended up with
factorial :: Integer -> Integer
factorial n = def $ do
assert (n >= 0) "Negative factorial"
ret <- var 1
i <- var n
while i $ do
ret *= i
i -= 1
return ret
使用var = newSTRef,def、assert 和 while 的规范定义,以及
a *= b = readSTRef b >>= \b -> modifySTRef a ((*) b)
a -= b = modifySTRef a ((+) (negate b))
但是,(*=) 和 (-=) 有不同的类型:
(-=) :: Num a => STRef s a -> a -> ST s ()
(*=) :: Num a => STRef s a -> STRef s a -> ST s ()
所以ret -= i 不起作用。我试图为此创建一个合适的类型类:
class (Monad m) => NumMod l r m where
(+=) :: l -> r -> m ()
(-=) :: l -> r -> m ()
(*=) :: l -> r -> m ()
instance Num a => NumMod (STRef s a) (STRef s a) (ST s) where
a += b = readSTRef b >>= \b -> modifySTRef a ((+) b)
a -= b = readSTRef b >>= \b -> modifySTRef a ((+) (negate b))
a *= b = readSTRef b >>= \b -> modifySTRef a ((*) b)
instance (Num a) => NumMod (STRef s a) a (ST s) where
a += b = modifySTRef a ((+) (b))
a -= b = modifySTRef a ((+) (negate b))
a *= b = modifySTRef a ((*) (b))
这确实有效,但前提是 factorial 返回 Integer。一旦我将返回类型更改为其他类型,它就会失败。我试图创建另一个实例
instance (Num a, Integral b) => NumMod (STRef s a) b (ST s) where
a += b = modifySTRef a ((+) (fromIntegral $ b))
a -= b = modifySTRef a ((+) (negate . fromIntegral $ b))
a *= b = modifySTRef a ((*) (fromIntegral b))
由于实例重叠而失败。
真的有可能创建一个合适的类型类和实例来让factorial 为任何Integral a 运行吗?还是会一直出现这个问题?
【问题讨论】:
-
一种可能的解决方案是使用不可判定的实例。
-
@ThomasM.DuBuisson:上面的代码实际上已经使用了
UndecidableInstances(虽然最初用于不同的东西,即class Booleanizeable b where toBool :: b -> Bool用于while,但它仍然存在)。 -
@Zeta 为了清楚起见,您将阶乘更改为具有 sig
factorial :: Int -> Int并且编译失败? -
我认为真正的问题是您尝试做的事情几乎没有意义。将值与对这些值的引用混为一谈,即使你能以某种方式使其工作,也不过是麻烦。
-
我现在不想写出一个完整的答案,所以我只是将您指向this amusing blog post,它为非常类似于 C 的 Haskell 提供了一个 DSL。它与@fizruk 的答案基本相似,但具有一些额外的聪明之处,可以使事物作为左值和右值正常工作。
标签: haskell