如何以及何时使用 State functor 和 State applicative?
How and when to use State functor and State applicative?
我看过代码中使用的 Maybe
和 Either
仿函数(和应用函数),这是有道理的,但我很难想出 State
函子和应用。也许它们不是很有用,只是因为 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
应用实例有助于编写由此类唯一标识符构造的表达式。例如,我在编写类型检查器时经常使用这种方法,它通常会使用新变量构造类型,如下所示:
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)
而应用实例可用于编写依赖于多个 Word32
输出的原语:
randUniform2 :: L (Double, Double)
randUniform2 = (,) <$> randUniform <*> randUniform
以及以合理自然的方式使用随机数的表达式:
-- area of a random triangle, say
a = areaOf <$> (Triangle <$> randUniform2 <*> randUniform2 <$> randUniform2)
我看过代码中使用的 Maybe
和 Either
仿函数(和应用函数),这是有道理的,但我很难想出 State
函子和应用。也许它们不是很有用,只是因为 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
应用实例有助于编写由此类唯一标识符构造的表达式。例如,我在编写类型检查器时经常使用这种方法,它通常会使用新变量构造类型,如下所示:
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)
而应用实例可用于编写依赖于多个 Word32
输出的原语:
randUniform2 :: L (Double, Double)
randUniform2 = (,) <$> randUniform <*> randUniform
以及以合理自然的方式使用随机数的表达式:
-- area of a random triangle, say
a = areaOf <$> (Triangle <$> randUniform2 <*> randUniform2 <$> randUniform2)