Haskell 有助于理解这个 State monad 代码:runState 在哪里定义的?
Haskell help to understand this State monad code: where is runState defined?
我是 Haskell 的新手,正在尝试了解 monad。我要通过 this code
放在这里以供快速参考
newtype State s a = State { runState :: s -> (a,s) }
instance Monad (State s) where
return a = State $ \s -> (a, s)
State act >>= k = State $ \s ->
let (a, s') = act s
in runState (k a) s'
get :: State s s
get = State $ \s -> (s, s)
put :: s -> State s ()
put s = State $ \_ -> ((), s)
modify :: (s -> s) -> State s ()
modify f = get >>= \x -> put (f x)
evalState :: State s a -> s -> a
evalState act = fst . runState act
execState :: State s a -> s -> s
execState act = snd . runState act
我没有找到函数 runState
的定义位置。它的类型似乎在 newtype State s a = State { runState :: s -> (a,s) }
中声明。
最令人费解的部分是这段代码编译没有任何错误。
runState
在哪里定义的?它的代码在哪里?
是的,你是对的,它在这里定义:
newtype State s a = State { runState :: s -> (a,s) }
基本上,根据这样的定义,您可以免费获得两个 函数:
State :: (s -> (a,s)) -> State s a
构造函数(命名与类型相同)- 在这里你可以 wrap 在函数中进入 newtype
来自记录语法内部的 - 和
runState :: State s a -> (s -> (a,s))
(与 State s a -> s -> (a,s)
相同) - 这将从第一个参数中取出 wrapped 函数并应用它到第二个参数(或 return 函数返回 - currying 使我们能够很好地处理这两种解释)
从某种意义上说,你在创建一个新的State s a
值时编写它的代码——它会取这个值,得到内容 并应用它。
所以,例如,如果你这样做
runState get "Hello"
{ def. get }
= runState (State (\s -> (s,s))) "Hello"
{ apply runState }
= (\s -> (s,s)) "Hello"
{ apply }
= ("Hello", "Hello")
关于你的问题——据我所知——记录语法等于:
newtype State s a = State (s -> (a,s))
runState :: State s a -> s -> (a,s)
runState (State f) s = f s
您似乎缺少的是记录语法 提供的功能。在Haskell中,定义数据类型有多种方式。您可能熟悉如下语句:
data MyType = MyType String Int
其中定义了类型 MyType
,它是求和类型(即此类型的任何值都具有 、String
和 Int
字段已填充)。如果我们想使用这种类型,我们可能想分别查看 String
和 Int
组件,因此我们会为此定义访问器函数:
getLabel :: MyType -> String
getLabel (MyType s _) = s
getNum :: MyType -> Int
getNum (MyType _ i) = i
对于大型数据类型(有很多字段),这会变得非常乏味,编写这种函数通常很常见:我们通常有一个简单的类型,其中构造函数包装内容,我们想要一个方便的以 "own" 类型(即没有包装器)访问此内容的方法。
由于这种访问字段的需求非常普遍,Haskell 提供了记录语法。当您使用记录语法时,您实质上是在数据类型中命名字段,并且自动生成用于提取这些字段中的值的函数。所以我们可以重新定义我们之前的数据类型:
data MyType' = MyType' { myLabel :: String, myNum :: Int }
和 Haskell 会自动生成访问器函数 myLabel :: MyType' -> String
和 myNum :: MyType' -> Int
。这些函数与我们之前定义的 getLabel
和 getNum
函数具有相同的功能。
以State
类型为例,我们可以这样定义:
newtype State' s a = State' (s -> (a, s))
并编写了一个函数来访问内容:
getStateFun :: State' s a -> (s -> (a, s))
getStateFun (State' f) = f
这将允许我们从我们的值周围删除 State'
"wrapping"。但这不如使用示例中使用的记录语法方便:存储在 State
包装器内的值被赋予字段名 runState
,并且 Haskell 生成访问器函数,它这就是代码编译和运行没有问题的原因。 runState
然后基本上与 getStateFun
相同。
这在 this chapter from Learn You A Haskell For Great Good 的 "Record syntax" 部分也有很清楚的解释。
我是 Haskell 的新手,正在尝试了解 monad。我要通过 this code 放在这里以供快速参考
newtype State s a = State { runState :: s -> (a,s) }
instance Monad (State s) where
return a = State $ \s -> (a, s)
State act >>= k = State $ \s ->
let (a, s') = act s
in runState (k a) s'
get :: State s s
get = State $ \s -> (s, s)
put :: s -> State s ()
put s = State $ \_ -> ((), s)
modify :: (s -> s) -> State s ()
modify f = get >>= \x -> put (f x)
evalState :: State s a -> s -> a
evalState act = fst . runState act
execState :: State s a -> s -> s
execState act = snd . runState act
我没有找到函数 runState
的定义位置。它的类型似乎在 newtype State s a = State { runState :: s -> (a,s) }
中声明。
最令人费解的部分是这段代码编译没有任何错误。
runState
在哪里定义的?它的代码在哪里?
是的,你是对的,它在这里定义:
newtype State s a = State { runState :: s -> (a,s) }
基本上,根据这样的定义,您可以免费获得两个 函数:
State :: (s -> (a,s)) -> State s a
构造函数(命名与类型相同)- 在这里你可以 wrap 在函数中进入newtype
来自记录语法内部的 - 和
runState :: State s a -> (s -> (a,s))
(与State s a -> s -> (a,s)
相同) - 这将从第一个参数中取出 wrapped 函数并应用它到第二个参数(或 return 函数返回 - currying 使我们能够很好地处理这两种解释)
从某种意义上说,你在创建一个新的State s a
值时编写它的代码——它会取这个值,得到内容 并应用它。
所以,例如,如果你这样做
runState get "Hello"
{ def. get }
= runState (State (\s -> (s,s))) "Hello"
{ apply runState }
= (\s -> (s,s)) "Hello"
{ apply }
= ("Hello", "Hello")
关于你的问题——据我所知——记录语法等于:
newtype State s a = State (s -> (a,s))
runState :: State s a -> s -> (a,s)
runState (State f) s = f s
您似乎缺少的是记录语法 提供的功能。在Haskell中,定义数据类型有多种方式。您可能熟悉如下语句:
data MyType = MyType String Int
其中定义了类型 MyType
,它是求和类型(即此类型的任何值都具有 、String
和 Int
字段已填充)。如果我们想使用这种类型,我们可能想分别查看 String
和 Int
组件,因此我们会为此定义访问器函数:
getLabel :: MyType -> String
getLabel (MyType s _) = s
getNum :: MyType -> Int
getNum (MyType _ i) = i
对于大型数据类型(有很多字段),这会变得非常乏味,编写这种函数通常很常见:我们通常有一个简单的类型,其中构造函数包装内容,我们想要一个方便的以 "own" 类型(即没有包装器)访问此内容的方法。
由于这种访问字段的需求非常普遍,Haskell 提供了记录语法。当您使用记录语法时,您实质上是在数据类型中命名字段,并且自动生成用于提取这些字段中的值的函数。所以我们可以重新定义我们之前的数据类型:
data MyType' = MyType' { myLabel :: String, myNum :: Int }
和 Haskell 会自动生成访问器函数 myLabel :: MyType' -> String
和 myNum :: MyType' -> Int
。这些函数与我们之前定义的 getLabel
和 getNum
函数具有相同的功能。
以State
类型为例,我们可以这样定义:
newtype State' s a = State' (s -> (a, s))
并编写了一个函数来访问内容:
getStateFun :: State' s a -> (s -> (a, s))
getStateFun (State' f) = f
这将允许我们从我们的值周围删除 State'
"wrapping"。但这不如使用示例中使用的记录语法方便:存储在 State
包装器内的值被赋予字段名 runState
,并且 Haskell 生成访问器函数,它这就是代码编译和运行没有问题的原因。 runState
然后基本上与 getStateFun
相同。
这在 this chapter from Learn You A Haskell For Great Good 的 "Record syntax" 部分也有很清楚的解释。