如何以及何时使用Statefunctor和Stateapplicative?

我已经看到代码中使用了MaybeandEither函子(和 applicative),这是有道理的,但是我很难想出一个State函子和 applicative的例子。也许它们不是很有用,只是因为Statemonad 需要函子和应用程序而存在?有很多关于它们的实现的解释,但在代码中使用它们时没有任何示例,因此我正在寻找有关它们如何单独使用的说明。

回答

我能想到几个例子。

首先,一个常见的用途State是管理一个计数器,目的是使某些“标识符”集变得唯一。因此,状态本身是一个Int,主要的原始状态操作是检索计数器的当前值并将其递增:

-- the state
type S = Int

newInt :: State S Int
newInt = state (s -> (s, s+1))

函子实例是对不同类型的标识符使用相同计数器的简洁方式,例如某些语言中的术语和类型级变量:

type Prefix = String
data Var = Var Prefix Int
data TypeVar = TypeVar Prefix Int

您可以在其中生成新的标识符,如下所示:

newVar :: Prefix -> State S Var
newVar s = Var s <$> newInt

newTypeVar :: Prefix -> State S TypeVar
newTypeVar s = TypeVar s <$> newInt

applicative 实例有助于编写由此类唯一标识符构造的表达式。例如,我在编写类型检查器时经常使用这种方法,它通常会使用新变量构造类型,如下所示:

typeCheckAFunction = ...
    let freshFunctionType = ArrowType <$> newTypeVar <*> newTypeVar
    ...

这里,freshFunctionType是一种新的a -> b风格类型与清爽型的变量a,并b可以沿着一个统一的步骤通过。

其次, 的另一个用途State是管理用于随机数生成的种子。例如,如果你想要一个低质量但超快的 LCG 生成器,你可以这样写:

lcg :: Word32 -> Word32
lcg x = (a * x + c)
  where a = 1664525
        c = 1013904223

-- monad for random numbers
type L = State Word32

randWord32 :: L Word32
randWord32 = state $ s -> let s' = lcg s in (s', s')

函子实例可用于Word32使用纯转换函数修改输出:

randUniform :: L Double
randUniform = toUnit <$> randWord32
  where toUnit w = fromIntegral w / fromIntegral (maxBound `asTypeOf` w)

而 applicative 实例可用于编写依赖于多个Word32输出的原语:

randUniform2 :: L (Double, Double)
randUniform2 = (,) <$> randUniform  <*> randUniform

和以相当自然的方式使用随机数的表达式:

-- area of a random triangle, say
a = areaOf <$> (Triangle <$> randUniform2 <*> randUniform2 <$> randUniform2)


以上是如何以及何时使用Statefunctor和Stateapplicative?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>