隐藏 State monad 的 s 类型参数
Hiding a State monad's s type parameter
我试图在新类型中隐藏 State
monad 的类型参数,但我很难将存在限定的 s
与 [=14= 统一起来] 将提供给 evalFoo
。我已经尝试使用 ExistentialQuantification
、GADTs
和 RankNTypes
,但我对这些扩展的工作原理的理解非常差。
如何使用惯用的 Haskell 方法来完成此外观?
谢谢!
{-# LANGUAGE GADTs #-}
import Control.Monad.State
import System.Random
data Foo a where
Foo :: RandomGen s => State s a -> Foo a
evalFoo :: RandomGen g => Foo a -> g -> a
evalFoo (Foo m) g = evalState m g
目标是实现这样的目标,但能够提供 RandomGen
:
的任何实例
myRNG :: Foo Double
myRNG = Foo $ do
u <- state random
return u
Prelude> evalFoo myRNG (mkStdGen 123)
0.7804356004944119
问题和你描述的差不多。您将无法将存在包装的随机种子与您的初始随机种子统一起来。最明显的方法是完全放弃存在量化,只使用这个:
runRandomly :: RandomGen g => State g a -> g -> a
runRandomly (Foo m) g = evalState m g
在这种情况下,我认为最明显的方法没有任何问题。如果你真的想从转换器中隐藏种子类型, 展示了如何正确地做到这一点。
在其他一些情况下,一些类似的存在性包装可以通过将种子与转换器一起包装来工作:
data Foo a where
Foo :: RandomGen s => State s a -> s -> Foo a
您可以在 foldl
包中看到类似的示例。
Foo
构造函数类型中的存在量化意味着对于类型 Foo
的每个值,都有一些 RandomGen
实例用作其状态。但是,你想要相反的东西:你想要给定任何值 foo :: Foo
,并且 any 实例 g
of RandomGen
,你可以使用 g
作为 foo
.
封装的计算状态
所以让我们改写:
{-# LANGUAGE Rank2Types #-}
import Control.Monad.State
import System.Random
newtype Foo a = MkFoo{ unFoo :: forall g. (RandomGen g) => State g a }
evalFoo :: RandomGen g => Foo a -> g -> a
evalFoo = evalState . unFoo
这个可以按预期使用:
myRNG :: Foo Double
myRNG = MkFoo $ do
u <- state random
return u
给予
*Main> evalFoo myRNG (mkStdGen 123)
0.43927189736460226
是的,不完全是 0.78 ;)
我试图在新类型中隐藏 State
monad 的类型参数,但我很难将存在限定的 s
与 [=14= 统一起来] 将提供给 evalFoo
。我已经尝试使用 ExistentialQuantification
、GADTs
和 RankNTypes
,但我对这些扩展的工作原理的理解非常差。
如何使用惯用的 Haskell 方法来完成此外观? 谢谢!
{-# LANGUAGE GADTs #-}
import Control.Monad.State
import System.Random
data Foo a where
Foo :: RandomGen s => State s a -> Foo a
evalFoo :: RandomGen g => Foo a -> g -> a
evalFoo (Foo m) g = evalState m g
目标是实现这样的目标,但能够提供 RandomGen
:
myRNG :: Foo Double
myRNG = Foo $ do
u <- state random
return u
Prelude> evalFoo myRNG (mkStdGen 123)
0.7804356004944119
问题和你描述的差不多。您将无法将存在包装的随机种子与您的初始随机种子统一起来。最明显的方法是完全放弃存在量化,只使用这个:
runRandomly :: RandomGen g => State g a -> g -> a
runRandomly (Foo m) g = evalState m g
在这种情况下,我认为最明显的方法没有任何问题。如果你真的想从转换器中隐藏种子类型,
在其他一些情况下,一些类似的存在性包装可以通过将种子与转换器一起包装来工作:
data Foo a where
Foo :: RandomGen s => State s a -> s -> Foo a
您可以在 foldl
包中看到类似的示例。
Foo
构造函数类型中的存在量化意味着对于类型 Foo
的每个值,都有一些 RandomGen
实例用作其状态。但是,你想要相反的东西:你想要给定任何值 foo :: Foo
,并且 any 实例 g
of RandomGen
,你可以使用 g
作为 foo
.
所以让我们改写:
{-# LANGUAGE Rank2Types #-}
import Control.Monad.State
import System.Random
newtype Foo a = MkFoo{ unFoo :: forall g. (RandomGen g) => State g a }
evalFoo :: RandomGen g => Foo a -> g -> a
evalFoo = evalState . unFoo
这个可以按预期使用:
myRNG :: Foo Double
myRNG = MkFoo $ do
u <- state random
return u
给予
*Main> evalFoo myRNG (mkStdGen 123)
0.43927189736460226
是的,不完全是 0.78 ;)