我有一些代码目前使用ST monad进行评估.我不喜欢将IO放在任何地方,因为该runST方法产生纯粹的结果,并指示这样的结果可以安全地调用(相对unsafePerformIO).但是,由于我的一些代码变得更长,我确实希望将调试打印语句放入.
是否有任何类提供双重个性monad [或类型类机制],可以是ST或IO(取决于其类型或"isDebug"标志)?我记得SPJ在他的"Fun with Type Functions"论文中引入了一个"Mutation"类,它使用关联类型将IO与IORef和ST关联到STRef.这样的包存在某个地方吗?
非常感谢[第n次],CA McCann!使用该解决方案,我能够为支持pdebug函数的monad引入一个额外的类.该ST单子会忽略这些呼叫,而IO将运行putStrLn.
class DebugMonad m where
pdebug :: String -> m ()
instance DebugMonad (ST s) where
pdebug _ = return ()
instance DebugMonad IO where
pdebug = putStrLn
test initV = do
v <- newRef initV
modifyRef v (+1)
pdebug "debug"
readRef v
testR v = runST $ test v
Run Code Online (Sandbox Code Playgroud)
这在ghci中有一个非常幸运的结果.由于默认情况下表达式是IO类型,因此运行类似"test 3"的操作将导致IO monad运行,因此您可以轻松地调试它,然后在您真正想要运行时使用类似"testR"的方法调用它它.
最初由Launchbury和Peyton Jones设计的ST monad允许Haskell程序员编写命令式代码(使用可变变量,数组等),同时获得该代码的纯接口.
更具体地说,入口点函数的多态类型
runST :: (forall s. ST s a) -> a
Run Code Online (Sandbox Code Playgroud)
确保ST包含计算的所有副作用,并且结果值是纯的.
这是否经过严格(甚至正式)证明?
这个最近的SO问题促使我在Haskell中编写了一个不安全且纯粹的ST monad仿真,这是一个稍微修改过的版本,你可以在下面看到:
{-# LANGUAGE DeriveFunctor, GeneralizedNewtypeDeriving, RankNTypes #-}
import Control.Monad.Trans.State
import GHC.Prim (Any)
import Unsafe.Coerce (unsafeCoerce)
import Data.List
newtype ST s a = ST (State ([Any], Int) a) deriving (Functor, Applicative, Monad)
newtype STRef s a = STRef Int deriving Show
newSTRef :: a -> ST s (STRef s a)
newSTRef a = ST $ do
(env, i) <- get
put (unsafeCoerce a : env, i + 1)
pure (STRef i)
update :: [a] -> (a -> a) …Run Code Online (Sandbox Code Playgroud) Haskell中的可变载体有三个元素级变异器:
read :: PrimMonad m => MVector (PrimState m) a -> Int -> m a
write :: PrimMonad m => MVector (PrimState m) a -> Int -> a -> m ()
swap :: PrimMonad m => MVector (PrimState m) a -> Int -> Int -> m ()
Run Code Online (Sandbox Code Playgroud)
现在我可以使用这些 -
import Data.Vector
import Data.Vector.Mutable
import Control.Monad.ST
import Control.Monad.Primitive
incrAt :: Vector Double -> Int -> Vector Double
incrAt vec i = runST $ do
mvec <- thaw vec
oldval <- read …Run Code Online (Sandbox Code Playgroud) 我在haskell中编写了一个小程序,使用State Monad with Vector计算Tree中所有的Int值的出现次数:
import Data.Vector
import Control.Monad.State
import Control.Monad.Identity
data Tree a = Null | Node (Tree a) a (Tree a) deriving Show
main :: IO ()
main = do
print $ runTraverse (Node Null 5 Null)
type MyMon a = StateT (Vector Int) Identity a
runTraverse :: Tree Int -> ((),Vector Int)
runTraverse t = runIdentity (runStateT (traverse t) (Data.Vector.replicate 7 0))
traverse :: Tree Int -> MyMon ()
traverse Null = return ()
traverse (Node l v …Run Code Online (Sandbox Code Playgroud) 我最近开始在Hackage上查看核心库,并且有一个我不理解的反复出现的习惯用法.以下是ST模块的示例:
instance Monad (ST s) where
{-# INLINE (>>=) #-}
(>>) = (*>)
(ST m) >>= k
= ST (\ s ->
case (m s) of { (# new_s, r #) ->
case (k r) of { ST k2 ->
(k2 new_s) }})
Run Code Online (Sandbox Code Playgroud)
特别是,我不明白(# new_s, r #).我假设第二个哈希是指一个未装箱的值,但其余的对我来说是一个谜(大概与"新状态"有关).
我有一个计算,我在其中将值插入Map,然后再次查找它们。我知道我从来没有在插入密钥之前使用过密钥,但是(!)无论如何随意使用都会使我感到紧张。我正在寻找一种获取总查询功能的方法,该方法不会返回Maybe,并且类型系统可以防止我意外滥用。
我的第一个想法是制作一个类似于的monad转换器StateT,其中状态为a Map,并且在monad中有用于插入和查找的特殊功能。插入函数返回一个新类型Receipt s k,其中s是STmonad 样式的幻像索引类型,并且k是键的类型,而查找函数则使用a Receipt而不是裸键。通过隐藏Receipt构造函数并使用类似于的量化运行函数runST,这应确保查找仅在插入同一映射后发生。(完整代码如下。)
但是我担心我已经重新发明了轮子,或者担心有另外一种获取安全的总地图查找的方法。在某个地方的公共包装中是否存在针对此问题的任何现有技术?
{-# LANGUAGE DeriveFunctor, LambdaCase, RankNTypes #-}
module KeyedStateT (KeyedStateT, Receipt, insert, lookup, receiptToKey, runKeyedStateT)
where
import Prelude hiding (lookup)
import Control.Arrow ((&&&))
import Control.Monad (ap, (>=>))
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (fromJust)
newtype KeyedStateT s k v m a = KeyedStateT (Map k v -> …Run Code Online (Sandbox Code Playgroud) 使用StateTmonad 转换器,我可以创建与StateT s [] a同构的类型s -> [(a, s)]。现在我更喜欢使用STTmonad 转换器,因为我想要多个不同类型的可变变量,并且希望能够根据早期计算的结果随意实例化它们。
但是,STT明确提及的链接文档:
这个 monad 转换器不应该与可以包含多个答案的 monad 一起使用,比如列表 monad。原因是状态标记将在不同的答案中重复,这会导致坏事发生(例如失去参考透明度)。安全的 monad 包括 monads State、Reader、Writer、Maybe 以及它们对应的 monad 转换器的组合。
那么我的选择是什么?
完全清楚:
编辑:(编辑编辑:下面的反例是无效的,因为ListT不应该应用于非可交换的 monadST和State。)我开始意识到按照 的方式STT行事的monad 转换器StateT本质上是不安全的。有了它,我们可以构建一个类型STT sloc (ListT (ST sglob)) a。这里,sglob是全局状态sloc的名称,而是局部状态的名称。* 现在我们可以使用全局状态在线程之间交换局部状态引用,从而潜在地获得对未初始化变量的引用。
*为了比较,对应的StateT构造是StateT sloc (ListT (State sglob)) a,与 同构sloc -> …
这是一个场景:给定的是一个C库,其核心是一些结构,其中的操作由丰富的C函数提供.
第1步:使用Haskell的FFI创建一个包装器.它具有像myCLibInit :: IO MyCLibObj,myCLibOp1 :: MyCLibObj -> ... -> IO ()等等功能.MyCLibObj是一个opaque类型,它携带(和隐藏)一个Ptr或ForeignPtr实际的C结构,例如,如本维基或RWH ch中所示.17.
第2步:使用unsafeIOToST从Control.Monad.ST.Unsafe将所有IO操作转换为ST操作.这是通过引入类似的东西来完成的
data STMyCLib s = STMyCLib MyCLibObj
Run Code Online (Sandbox Code Playgroud)
然后将所有IO函数包装在ST函数中,例如:
myCLibInit' :: ST s (STMyCLib s)
myCLibInit' = unsafeIOToST $ STMyCLib <$> myCLibInit
Run Code Online (Sandbox Code Playgroud)
这允许编写命令式程序,这些程序反映了类似OO的C库的使用,例如:
doSomething :: ST s Bool
doSomething = do
obj1 <- myCLibInit'
success1 <- myCLibOp1' obj1 …Run Code Online (Sandbox Code Playgroud) 在对文件Data.Vector.unsafeFreeze说:
不安全[ly]将可变向量转换为不可变向量而不进行复制.在此操作之后可能不使用可变载体.
我想详细描述这里"不安全"的含义.在实验上,它似乎"仅"意味着对原始可变载体的进一步修改将导致返回的不可变向量unsafeFreeze不再是纯的:
$ import qualified Data.Vector as V
$ import qualified Data.Vector.Mutable as MV
$ import Control.Monad.ST
$ :{
$ |runST $ do
$ | mv <- V.thaw $ V.fromList [0..10]
$ | v <- V.unsafeFreeze mv
$ | MV.write mv 0 (-1)
$ | MV.write mv 1 (-2)
$ | v' <- V.freeze mv
$ | v'' <- V.unsafeFreeze mv
$ | return (v, v', v'')
$ |:}
([-1,-2,2,3,4,5,6,7,8,9,10],[-1,-2,2,3,4,5,6,7,8,9,10],[-1,-2,2,3,4,5,6,7,8,9,10])
Run Code Online (Sandbox Code Playgroud)
我可以想象一个修改"不安全"冻结中使用的来源做各种粗糙的事情会导致更糟糕的行为,例如segfaulting.不幸的是,我很快就忘记了尝试阅读不安全操作的来源.
我可以依靠所说的杂质是这些操作"不安全"的唯一方式吗?
对于上下文:我需要在一个通常不可变的数据结构上实现各种修改算法,而不是在内部可变性范围内重用其面向公众的API会非常不方便(因为AFAICT没有办法一般地访问 …
我最近一两个月一直在学习Haskell,最近解决了这个编码问题。额外的挑战是在没有额外空间和线性时间内完成任务,我认为这不可能以纯函数的方式完成,所以很自然地我发现了 ST monad,我认为这将是一个很好的机会了解更多信息。不管怎样,这是我写的代码:
\n\nmodule FindDuplicates where\n\nimport Control.Monad (foldM)\nimport Control.Monad.ST\nimport Data.Array.ST\n\nxs = [4,3,2,7,8,2,3,1] :: [Int]\n\nfindDuplicates :: [Int] -> ST s [Int]\nfindDuplicates xs = do\n arr <- newListArray (1, length xs) xs :: ST s (STArray s Int Int)\n\n let go :: [Int] -> Int -> ST s [Int]\n go acc i = do x <- abs <$> readArray arr i\n y <- readArray arr x\n if y < 0\n then return (x:acc)\n else do writeArray arr x (-y)\n …Run Code Online (Sandbox Code Playgroud)