隐藏 State monad 的 s 类型参数

Hiding a State monad's s type parameter

我试图在新类型中隐藏 State monad 的类型参数,但我很难将存在限定的 s 与 [=14= 统一起来] 将提供给 evalFoo。我已经尝试使用 ExistentialQuantificationGADTsRankNTypes,但我对这些扩展的工作原理的理解非常差。

如何使用惯用的 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 ;)