【发布时间】:2015-11-28 19:04:12
【问题描述】:
这篇文章是识字的 Haskell。只需放入“pad.lhs”之类的文件,ghci 就可以运行它。
> {-# LANGUAGE GADTs, Rank2Types #-}
> import Control.Monad
> import Control.Monad.ST
> import Data.STRef
好的,所以我能够弄清楚如何用纯代码表示 ST monad。首先,我们从引用类型开始。它的具体值并不重要。最重要的是PT s a 不应该与任何其他类型forall s 同构。 (特别是,它应该与() 和Void 都不同构。)
> newtype PTRef s a = Ref {unref :: s a} -- This is defined liked this to make `toST'` work. It may be given a different definition.
s 的类型是 *->*,但现在这并不重要。可能是polykind,我们只关心。
> data PT s a where
> MkRef :: a -> PT s (PTRef s a)
> GetRef :: PTRef s a -> PT s a
> PutRef :: a -> PTRef s a -> PT s ()
> AndThen :: PT s a -> (a -> PT s b) -> PT s b
非常直接。 AndThen 允许我们将其用作Monad。您可能想知道return 是如何实现的。这是它的 monad 实例(它只遵守关于 runPF 的 monad 法则,稍后将定义):
> instance Monad (PT s) where
> (>>=) = AndThen
> return a = AndThen (MkRef a) GetRef --Sorry. I like minimalism.
> instance Functor (PT s) where
> fmap = liftM
> instance Applicative (PT s) where
> pure = return
> (<*>) = ap
现在我们可以将fib 定义为测试用例。
> fib :: Int -> PT s Integer
> fib n = do
> rold <- MkRef 0
> rnew <- MkRef 1
> replicateM_ n $ do
> old <- GetRef rold
> new <- GetRef rnew
> PutRef new rold
> PutRef (old+new) rnew
> GetRef rold
它会键入检查。欢呼!现在,我能够将其转换为 ST(我们现在明白为什么 s 必须是 * -> *)
> toST :: PT (STRef s) a -> ST s a
> toST (MkRef a ) = fmap Ref $ newSTRef a
> toST (GetRef (Ref r)) = readSTRef r
> toST (PutRef a (Ref r)) = writeSTRef r a
> toST (pa `AndThen` apb) = (toST pa) >>= (toST . apb)
现在我们可以定义一个函数来运行PT,而不需要引用ST:
> runPF :: (forall s. PT s a) -> a
> runPF p = runST $ toST p
runPF $ fib 7 给出13,这是正确的。
我的问题是我们可以在没有引用的情况下使用 ST 定义 runPF 吗?
有没有一种纯粹的方式来定义runPF? PTRef 的定义完全不重要;无论如何,它只是一个占位符类型。它可以被重新定义为使它工作的任何东西。
如果你不能纯粹定义runPF,请证明它不能。
性能不是问题(如果是,我不会让每个return 都有自己的参考)。
我认为存在类型可能有用。
注意:如果我们假设 a 是可动态的,这很简单。我正在寻找适用于所有a 的答案。
注意:事实上,答案甚至不一定与PT 有很大关系。它只需要像ST 一样强大而不使用魔法。 (从(forall s. PT s) 转换是一种测试答案是否有效。)
【问题讨论】:
-
您可以让
s ~ Const Int进入runPF并保留Map与Int键和a值。 -
我们可以在每个
PT s a中假设Typeable a吗?如果是这样,我猜这是适应IOSpec 的问题。 -
或者更简单地说,如果一切都是
Typeable,Map Int Dynamic应该足以代表STRefs 后台存储。在这种情况下,读/写需要通过部分(但纯)函数来实现。 -
@chi 这太容易了。
-
我所知道的最接近的是 Andrea Vezzosi 在 Agda 的安全 ST monad。 code.haskell.org/~Saizan/ST/ST.agda 我的理解是,在 Haskell 中,您需要在不使用开箱即用 ST 的情况下在三件事上做出妥协:要么 a.) 您需要一个更简单的 ref 类型世界,例如可键入,b.) 你需要接受 unsafeCoerce,或者 c.) 你需要使用某种形式的索引单子。
标签: haskell state monads ghc purely-functional