mnt*_*123 11 haskell state-monad
我是Haskell的新手并试图了解monad.我将通过此代码 将其放在这里以供快速参考
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
Run Code Online (Sandbox Code Playgroud)
我没有得到runState定义函数的位置.似乎它的类型是声明的newtype State s a = State { runState :: s -> (a,s) }.最令人费解的部分是这段代码编译没有任何错误.
在哪里runState定义?它的代码在哪里?
Car*_*ten 12
是的,你是对的,它在这里定义:
newtype State s a = State { runState :: s -> (a,s) }
Run Code Online (Sandbox Code Playgroud)
基本上从这样的定义你可以免费得到两个函数:
State :: (s -> (a,s)) -> State s a构造函数(命名与类型相同) - 在这里你可以将函数包装到newtyperunState :: State s a -> (s -> (a,s))(相同State s a -> s -> (a,s)) - 这将把包装的函数从第一个参数中取出并将它应用到第二个参数(或者返回函数 - currying使我们可以很好地处理这两个解释)从某种意义上说,您在创建新State s a值时编写代码- 它将获取此值,获取内容并应用它.
所以,例如,如果你这样做
runState get "Hello"
{ def. get }
= runState (State (\s -> (s,s))) "Hello"
{ apply runState }
= (\s -> (s,s)) "Hello"
{ apply }
= ("Hello", "Hello")
Run Code Online (Sandbox Code Playgroud)
对你的问题 - 根据我的理解 - 记录语法等于:
newtype State s a = State (s -> (a,s))
runState :: State s a -> s -> (a,s)
runState (State f) s = f s
Run Code Online (Sandbox Code Playgroud)
您似乎缺少的是记录语法提供的功能.在Haskell中,有多种方法可以定义数据类型.您可能熟悉以下语句:
data MyType = MyType String Int
Run Code Online (Sandbox Code Playgroud)
其中MyType定义了一个类型,它是一个和类型(即此类型的任何值都填充了String和Int字段).如果我们想使用这种类型,我们可能想要分别查看String和Int组件,因此我们将为此定义访问器函数:
getLabel :: MyType -> String
getLabel (MyType s _) = s
getNum :: MyType -> Int
getNum (MyType _ i) = i
Run Code Online (Sandbox Code Playgroud)
对于大型数据类型(包含许多字段),这可能非常繁琐,编写这种函数通常非常常见:通常我们有一个简单的类型,构造函数包装内容,我们想要一种方便的方法来访问这些内容在其"自己的"类型(即没有包装).
因为这种访问字段的愿望非常普遍,所以Haskell提供了记录语法.使用记录语法时,实质上是将数据类型中的字段命名,并自动生成在这些字段中提取值的函数.所以我们可以从之前重新定义我们的数据类型:
data MyType' = MyType' { myLabel :: String, myNum :: Int }
Run Code Online (Sandbox Code Playgroud)
和Haskell会产生存取功能myLabel :: MyType' -> String和myNum :: MyType' -> Int自动.这些函数具有getLabel与getNum我们之前定义的函数相同的功能.
以State类型为例,我们可以将其定义如下:
newtype State' s a = State' (s -> (a, s))
Run Code Online (Sandbox Code Playgroud)
并编写了一个访问内容的函数:
getStateFun :: State' s a -> (s -> (a, s))
getStateFun (State' f) = f
Run Code Online (Sandbox Code Playgroud)
这将允许我们State'从我们的价值周围删除"包装".但是这比使用记录语法更不方便,记录语法在你的例子中使用:存储在State包装器中的值给出了fieldname runState,而Haskell生成了访问器函数,这就是代码编译和运行没有问题的原因.runState然后基本上做同样的事情getStateFun.
这一点在本章的 "记录语法"一节中也非常清楚地解释了,从" 了解你是一个好的Haskell".
| 归档时间: |
|
| 查看次数: |
1206 次 |
| 最近记录: |