Sam*_*vas 1 random haskell monad-transformers
我在RVarT用随机符包裹大脑时遇到困难。就像一种心理锻炼一样,我尝试使用monad变压器Maybe x随机生成并合并到其中Maybe (x, x)
我努力做到这一点,对我来说似乎很直观
maybeRandSome :: (MaybeT RVar) Int
maybeRandSome = lift $ return 1
maybeRandNone :: (MaybeT RVar) Int
maybeRandNone = MaybeT . return $ Nothing
maybeTwoRands :: (MaybeT RVar) (Int, Int)
maybeTwoRands =
do
x <- maybeRandSome
y <- maybeRandNone
return (x, y)
Run Code Online (Sandbox Code Playgroud)
并可以在IO中进行采样
> sample $ runMaybeT maybeTwoRands
Nothing
Run Code Online (Sandbox Code Playgroud)
但是我无法弄清楚是否可以逆转:
reverseMaybeRandSome :: (RVarT Maybe) Int
reverseMaybeRandSome = lift $ Just 1
reverseMaybeRandNone :: (RVarT Maybe) Int
reverseMaybeRandNone = lift Nothing
reverseMaybeTwoRands :: (RVarT Maybe) (Int, Int)
reverseMaybeTwoRands =
do
x <- Random.sample reverseMaybeRandSome
y <- Random.sample reverseMaybeRandNone
return (x, y)
Run Code Online (Sandbox Code Playgroud)
这需要我从电梯Maybe m到MonadRandom m不知何故,我想不通,如果是有道理的,或者如果我做一些不健全的开始。
是的,您几乎在做一些不合理的事情。 对任何monad MaybeT m a都是同构的m (Maybe a),包括m = RVar,所以a MaybeT RVar a实际上只是an RVar (Maybe a),它表示采用的值的随机变量Maybe a。有了这个,就很容易想象对两个Maybe a值的随机变量进行采样,然后Maybe (a,a)以通常的方式将它们组合为一个值随机变量(即,如果其中一个或两个都是Nothing,结果为Nothing,如果它们分别为Just x和Just y,是Just (x,y))。这就是您的第一段代码正在做的事情。
但是,RVarT Maybe a是不同的。它是一个有a值(无 Maybe a值)随机变量,可以使用基本Maybe单子的设施生成其值,只要可以以某种明智的方式将其提升到最终单子即可,在该单子中可以实现随机变量的“随机性” 。
要了解这意味着什么,我们必须更详细地研究类型RVar和RVarT。
该类型RVar a表示一个a值随机变量。为了将这种表示形式实际转换为真正的随机值,必须使用以下命令运行它:
runRVar :: RandomSource m s => RVar a -> s -> m a
Run Code Online (Sandbox Code Playgroud)
这有点太笼统了,所以可以想象它专门用于:
runRVar :: RVar a -> StdRandom -> IO a
Run Code Online (Sandbox Code Playgroud)
请注意,这StdRandom是StdRandom此处唯一有效的值,因此我们将始终写入runRVar something StdRandom,也可以写入sample something。
有了这种专业化,您应该查看RVar a为一元配方使用一组有限的随机原语构建一个随机变量是runRVar转换成IO即实现随机元相对于一个全局随机数发生器作用。这种到IO操作的转换使配方可以生成实际的采样随机值。如果您有兴趣,可以在中找到有限的随机基元集Data.Random.Internal.Source。
类似地,RVarT n a也是一个具有a值的随机变量(即,使用有限的一组随机化基元来构造随机变量的配方),该值也可以访问“另一个基本monad的功能n”。这个配方可以,可以实现任何最终单子内部运行两个随机化原语和基单子的设施n。在一般情况下,您可以使用以下命令运行它:
runRVarTWith :: MonadRandom m =>
(forall t. n t -> m t) -> RVarT n a -> s -> m a
Run Code Online (Sandbox Code Playgroud)
它具有明确的提升功能,解释了如何将基本单子的设施提升n到最终单子m。
如果基本monad n为Maybe,则它的“便利性”是发出错误或计算失败的信号。您可能会使用这些工具来构造以下有点愚蠢的随机变量:
sqrtNormal :: RVarT Maybe Double
sqrtNormal = do
z <- stdNormalT
if z < 0
then lift Nothing -- use Maybe facilities to signal error
else return $ sqrt z
Run Code Online (Sandbox Code Playgroud)
请注意,重要的是,sqrtNormal它不代表Maybe Double要生成的值随机变量。相反,它表示Double带有值的随机变量,其生成可能会由于基本Maybemonad 的功能而失败。
为了实现此随机变量(即对其进行采样),我们需要在适当的最终monad中运行它。最终的monad需要同时支持随机基元和从Maybemonad中适当提升的失败概念。
如果适当的故障概念是运行时错误,则IO可以正常工作:
liftMaybeToIO :: Maybe a -> IO a
liftMaybeToIO Nothing = error "simulation failed!"
liftMaybeToIO (Just x) = return x
Run Code Online (Sandbox Code Playgroud)
之后:
main1 :: IO ()
main1 = print =<< runRVarTWith liftMaybeToIO sqrtNormal StdRandom
Run Code Online (Sandbox Code Playgroud)
大约一半时间生成正标准高斯的平方根,另一半抛出运行时错误。
如果要以纯形式(Maybe例如以)捕获故障,则需要考虑RVar以适当的monad 实现。单子:
MaybeT IO a
Run Code Online (Sandbox Code Playgroud)
会成功的 它与相同IO (Maybe a),因此具有可用的IO设施(需要实现随机化原语),并能够通过return来指示失败Nothing。如果我们写:
main2 :: IO ()
main2 = print =<< runMaybeT act
where act :: MaybeT IO Double
act = sampleRVarTWith liftMaybe sqrtNormal
Run Code Online (Sandbox Code Playgroud)
我们会收到一个错误,指出没有实例MonadRandom (MaybeT IO)。我们可以如下创建一个:
import Control.Monad.Trans (liftIO)
instance MonadRandom (MaybeT IO) where
getRandomPrim = liftIO . getRandomPrim
Run Code Online (Sandbox Code Playgroud)
以及适当的提升功能:
liftMaybe :: Maybe a -> MaybeT IO a
liftMaybe = MaybeT . return
Run Code Online (Sandbox Code Playgroud)
之后,main2将返回Nothing大约一半的时间,Just而正一半的高斯平方根将返回另一半。
完整代码:
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE FlexibleInstances #-}
import Control.Monad.Trans (liftIO)
import Control.Monad.Trans.Maybe (MaybeT(..))
import Data.Random
import Data.Random.Lift
import Data.Random.Internal.Source
sqrtNormal :: RVarT Maybe Double
sqrtNormal = do
z <- stdNormalT
if z < 0
then lift Nothing -- use Maybe facilities to signal error
else return $ sqrt z
liftMaybeToIO :: Maybe a -> IO a
liftMaybeToIO Nothing = error "simulation failed!"
liftMaybeToIO (Just x) = return x
main1 :: IO ()
main1 = print =<< runRVarTWith liftMaybeToIO sqrtNormal StdRandom
instance MonadRandom (MaybeT IO) where
getRandomPrim = liftIO . getRandomPrim
main2 :: IO ()
main2 = print =<< runMaybeT act
where act :: MaybeT IO Double
act = runRVarTWith liftMaybe sqrtNormal StdRandom
liftMaybe :: Maybe a -> MaybeT IO a
liftMaybe = MaybeT . return
Run Code Online (Sandbox Code Playgroud)
这一切都适用于您的第二个示例的方式看起来像这样,它将始终打印Nothing:
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE FlexibleInstances #-}
import Control.Monad.Trans (liftIO)
import Control.Monad.Trans.Maybe (MaybeT(..))
import Data.Random
import Data.Random.Lift
import Data.Random.Internal.Source
reverseMaybeRandSome :: RVarT Maybe Int
reverseMaybeRandSome = return 1
reverseMaybeRandNone :: RVarT Maybe Int
reverseMaybeRandNone = lift Nothing
reverseMaybeTwoRands :: RVarT Maybe (Int, Int)
reverseMaybeTwoRands =
do
x <- reverseMaybeRandSome
y <- reverseMaybeRandNone
return (x, y)
instance MonadRandom (MaybeT IO) where
getRandomPrim = liftIO . getRandomPrim
runRVarTMaybe :: RVarT Maybe a -> IO (Maybe a)
runRVarTMaybe act = runMaybeT $ runRVarTWith liftMaybe act StdRandom
where
liftMaybe :: Maybe a -> MaybeT IO a
liftMaybe = MaybeT . return
main :: IO ()
main = print =<< runRVarTMaybe reverseMaybeTwoRands
Run Code Online (Sandbox Code Playgroud)