我试图提出一个从功能组合示例派生的State Monad的实现。在这里,我想出了什么:
首先衍生出Monad的概念:
<table>
<thead>
<tr class="title">
<th colspan="7">Size Table</th>
</tr>
<tr class="sub-title">
<th rowspan="2"></th>
<th colspan="2">Bust</th>
<th colspan="2">Bust</th>
<th colspan="2">Bust</th>
</tr>
<tr class="sub-title">
<th>CH</th>
<th>CM</th>
<th>CH</th>
<th>CM</th>
<th>CH</th>
<th>CM</th>
</tr>
</thead>
<tbody>
<tr>
<td>S</td>
<td>col1</td>
<td>col2</td>
<td>col3</td>
<td>Col4</td>
<td>Col5</td>
<td>Col6</td>
</tr>
<tr>
<td>M</td>
<td>col1</td>
<td>col2</td>
<td>col3</td>
<td>Col4</td>
<td>Col5</td>
<td>Col6</td>
</tr>
<tr>
<td>L</td>
<td>col1</td>
<td>col2</td>
<td>col3</td>
<td>Col4</td>
<td>Col5</td>
<td>Col6</td>
</tr>
</tbody>
<tfoot>
<tr class="footer">
<th colspan="7">description</th>
</tr>
</tfoot>
</table>
我们可以具有如下组成这些功能的功能:
data Maybe' a = Nothing' | Just' a deriving Show
sqrt' :: (Floating a, Ord a) => a -> Maybe' a
sqrt' x = if x < 0 then Nothing' else Just' (sqrt x)
inv' :: (Floating a, Ord a) => a -> Maybe' a
inv' x = if x == 0 then Nothing' else Just' (1/x)
log' :: (Floating a, Ord a) => a -> Maybe' a
log' x = if x == 0 then Nothing' else Just' (log x)
可以通过排除case语句和函数应用程序来简化此操作:
sqrtInvLog' :: (Floating a, Ord a) => a -> Maybe' a
sqrtInvLog' x = case (sqrt' x) of
Nothing' -> Nothing'
(Just' y) -> case (inv' y) of
Nothing' -> Nothing'
(Just' z) -> log' z
现在,通过定义Monad =>
,我们可以将概念推广到任何类型,而不仅仅是Maybe'。fMaybe' :: (Maybe' a) -> (a -> Maybe' b) -> Maybe' b
fMaybe' Nothing' _ = Nothing'
fMaybe' (Just' x) f = f x
-- Applying fMaybe' =>
sqrtInvLog'' :: (Floating a, Ord a) => a -> Maybe' a
sqrtInvLog'' x = (sqrt' x) `fMaybe'` (inv') `fMaybe'` (log')`
使用Monad的实现,sqrtInvLog可以写为:
class Monad' m where
bind' :: m a -> (a -> m b) -> m b
return' :: a -> m a
instance Monad' Maybe' where
bind' Nothing' _ = Nothing'
bind' (Just' x) f = f x
return' x = Just' x
尝试应用概念维护状态,我定义了如下所示的内容:
sqrtInvLog''' :: (Floating a, Ord a) => a -> Maybe' a
sqrtInvLog''' x = (sqrt' x) \bind'` (inv') `bind'` (log')`
无法使用上述定义来定义monad,因为bind需要定义为采用单一类型“ ma”。
基于Haskell对State Monad的定义的第二次尝试:
data St a s = St (a,s) deriving Show
sqrtLogInvSt' :: (Floating a, Ord a) => St a a -> St (Maybe' a) a
sqrtLogInvSt' (St (x,s)) = case (sqrt' x) of
Nothing' -> St (Nothing', s)
(Just' y) -> case (log' y) of
Nothing' -> St (Nothing', s+y)
(Just' z) -> St (inv' z, s+y+z)
首次尝试定义使用组合函数构建并保持状态的函数:
newtype State s a = State { runState :: s -> (a, s) }
一个组合函数:
fex1 :: Int->State Int Int
fex1 x = State { runState = \s->(r,(s+r)) } where r = x `mod` 2`
fex2 :: Int->State Int Int
fex2 x = State { runState = \s-> (r,s+r)} where r = x * 5
但是定义fex3 x = (runState (fex2 y)) st where (st, y) = (runState (fex1 x)) 0
不适合绑定的newtype State s a = State { runState :: s -> (a, s) }
模式
可以尝试如下:
m a -> (a -> m b) -> m b
上面未定义bind',因为我不知道如何实现。
我可以得出单子为什么有用的理由,并将其应用于第一个示例(也许是),但似乎无法将其应用于州。理解我如何使用上面定义的概念来推导州联结将很有用。
请注意,我之前曾问过类似的问题:Haskell - Unable to define a State monad like function using a Monad like definition,但我在这里进行了扩展并添加了更多详细信息。
答案 0 :(得分:3)
您的组合函数fex3
的类型错误:
fex3 :: Int -> (Int, Int)
与您的sqrtInvLog'
的{{1}}示例不同,Maybe
不会以State
的类型出现。
我们可以将其定义为
fex3
您定义的主要区别在于,我们传递自己的状态fex3 :: Int -> State Int Int
fex3 x = State { runState = \s ->
let (y, st) = runState (fex1 x) s in
runState (fex2 y) st }
而不是将0
硬编码为初始状态。
如果(例如在您的s
示例中)我们想组成三个函数怎么办?在这里,我将重用Maybe
而不是引入另一个中间函数:
fex2
SPOILERS:
广义版本fex4 :: Int -> State Int Int
fex4 x = State { runState = \s ->
let (y, st) = runState (fex1 x) s in
let (z, st') = runState (fex2 y) st in
runState (fex2 z) st' }
可以按以下方式提取:
bindState
我们也可以从bindState m f = State { runState = \s ->
let (x, st) = runState m s in
runState (f x) st }
fex3' x = fex1 x `bindState` fex2
fex4' x = fex1 x `bindState` fex2 `bindState` fex2
和类型开始。
Monad'
定义中的m
应用于一个类型参数(Monad'
,m a
)。我们无法设置m b
,因为m = State
需要两个参数。另一方面,部分应用程序对于类型是完全有效的:State
实际上意味着State s a
,因此我们可以设置(State s) a
:
m = State s
答案 1 :(得分:0)
看看this。总结并扩展一点。
如果您有功能
ta -> tb
并想在其中添加“状态”,那么您应该传递该状态,并具有
(ta, ts) -> (tb, ts)
您可以通过以下方式对其进行转换:
ta -> ts -> (tb, ts)
如果将其与原始ta -> tb
进行比较,我们将获得(添加括号)
ta -> (ts -> (tb, ts))
总而言之,如果函数从tb
(即ta
)返回ta -> tb
,则“有状态”
版本将从ts -> (tb, ts)
(即ta
)返回(ta -> (ts -> (tb, ts))
)
因此,“状态计算”(仅一个函数或处理状态的一系列函数)必须返回/产生以下内容:
(ts -> (tb, ts))
这是一堆int的典型情况。
[Int]
是国家
pop :: [Int] -> (Int, [Int]) -- remove top
pop (v:s) -> (v, s)
push :: Int -> [Int] -> (int, [Int]) -- add to the top
push v s -> (v, v:s)
对于push
,push 5
的类型与pop :: [Int] -> (Int, [Int])
的类型相同。
因此,我们想结合/加入这个基本操作,以得到
duplicateTop =
v <- pop
push v
push v
请注意,根据需要,duplicateTop :: [Int] -> (Int, [Int])
现在:如何将两个有状态计算结合起来以得到一个新的计算?
让我们开始吧(警告:此定义与
用于单子(bind
)的>>=
,但它是等效的。
组合:
f :: ta -> (ts -> (tb, ts))
使用
g :: tb -> (ts -> (tc, ts))
获得
h :: ta -> (ts -> (tc, ts))
这是h(在伪haskell中)的构造
h = \a -> ( \s -> (c, s') )
我们必须计算(c, s')
(表达式中的其余部分只是参数a
和s
)。这是这样的:
-- 1. run f using a and s
l1 = f a -- use the parameter a to get the function (ts -> (tb, ts)) returned by f
(b, s1) = l1 s -- use the parameter s to get the pair that l1 returns ( :: (tb, ts) )
-- 2. run g using f output, b and s1
l2 = g b -- use b to get the function (ts -> (tb, ts)) returned by g
(c, s') = l2 s1 -- use s1 to get the pair that l2 returns ( :: (tc, ts) )