Sha*_*ger 7 haskell functional-programming
我目前正在研究monad转换器,并试图真正理解类型签名,并且感到有些困惑。让我们使用以下堆栈进行讨论:
newtype Stack s m a = Stack { runStack :: ReaderT s (StateT s IO) a }
Run Code Online (Sandbox Code Playgroud)
我试图逐层检查并写出未包装的类型签名,但被卡住了:
newtype Stack s m a = Stack {
runStack :: ReaderT s (StateT s IO) a }
-- ReaderT s m a
-- s -> m a
-- s -> (StateT s IO) a
-- StateT s m a
-- s -> (s -> IO (a, s)) a
Run Code Online (Sandbox Code Playgroud)
只是看起来不像最后一行上的有效返回类型签名,我们本质上有一个函数,该函数接受s并返回紧靠a
?的函数。
我知道内部函数最终会得出Monad的值,这就是为什么它是m
in 的原因ReaderT r m a
,但是它使我的大脑弯曲。
谁能提供任何见解,我是否已经分析了类型,而我只是必须接受那s -> (s -> IO (a, s)) a
确实有效?
谢谢
您编写的堆栈有点奇怪,因为它的参数m
设置在左侧,而专用IO
于右侧,所以让我们看一下完全m
参数化的变体:
newtype Stack s m a = Stack { runStack :: ReaderT s (StateT s m) a }
Run Code Online (Sandbox Code Playgroud)
现在runStack
这只是一个字段名称,因此我们可以删除它并编写等效的newtype
定义:
newtype Stack s m a = Stack (ReaderT s (StateT s m) a)
Run Code Online (Sandbox Code Playgroud)
我们还有以下库新类型定义,跳过字段名称。我还使用了新鲜的变量,因此a
在扩展时,我们不会做一些愚蠢的事情,例如混淆两个不同范围的s:
newtype ReaderT r1 m1 a1 = ReaderT (r1 -> m1 a1)
newtype StateT s2 m2 a2 = StateT (s2 -> m2 (a2, s2))
Run Code Online (Sandbox Code Playgroud)
当然,如果我们只对直到同构的类型感兴趣,那么新类型包装器就没有关系,因此只需将它们重写为类型别名即可:
type Stack s m a = ReaderT s (StateT s m) a
type ReaderT r1 m1 a1 = r1 -> m1 a1
type StateT s2 m2 a2 = s2 -> m2 (a2, s2)
Run Code Online (Sandbox Code Playgroud)
现在,很容易扩展Stack
类型:
Stack s m a
= ReaderT s (StateT s m) a
-- expand ReaderT with r1=s, m1=StateT s m, a1=a
= s -> (StateT s m) a
= s -> StateT s m a
-- expand StateT with s2=s m2=m a2=a
= s -> (s -> m (a, s))
= s -> s -> m (a, s)
Run Code Online (Sandbox Code Playgroud)
正如@duplode指出的,这里没有多余的内容a
。
直观地,这Stack
是从s
(第一个参数)读取的,其类型为初始状态s
(第二个参数),并在m
(例如IO
)中返回一个单子动作,该动作可以返回type的值和type a
的更新状态s
。
归档时间: |
|
查看次数: |
74 次 |
最近记录: |