如何以及何时使用Statefunctor和Stateapplicative?
我已经看到代码中使用了Maybe
andEither
函子(和 applicative),这是有道理的,但是我很难想出一个State
函子和 applicative的例子。也许它们不是很有用,只是因为State
monad 需要函子和应用程序而存在?有很多关于它们的实现的解释,但在代码中使用它们时没有任何示例,因此我正在寻找有关它们如何单独使用的说明。
回答
我能想到几个例子。
首先,一个常见的用途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)
THE END
二维码