Haskell 自定义类型的 `bind` 实例
Haskell instance of `bind` for a custom type
我正在尝试为自定义类型 ST a
的绑定运算符 (>>=)
创建一个实例
我找到了这种方法,但我不喜欢那种硬编码 0。
有没有什么方法可以在不使用硬编码 0 并且不考虑函数类型的情况下实现它?
newtype ST a = S (Int -> (a, Int))
-- This may be useful to implement ">>=" (bind), but it is not mandatory to use it
runState :: ST a -> Int -> (a, Int)
runState (S s) = s
instance Monad ST where
return :: a -> ST a
return x = S (\n -> (x, n))
(>>=) :: ST a -> (a -> ST b) -> ST b
s >>= f = f (fst (runState s 0))
要牢记的最重要的一点是,您的 ST
类型是 函数 的包装器。如果您开始定义为 (>>=) = \s -> \f -> S (\n -> ... )
会怎样?在那里为 s
和 f
参数编写单独的 lambda 可能(好吧,是)有点傻,但我这样做是为了表明它们与 n
参数。您可以在 (>>=)
.
的定义中使用它
我经常发现使用某种类型的伪代码重写更容易遵循此类代码,如下所示:从
开始
instance Monad ST where
return :: a -> ST a
return x = S (\n -> (x, n))
我们到了
runState (return x) n = (x, n)
表达的完全一样。它现在是一种定义,通过它必须遵循的相互作用定律。这让我可以忽略围绕基本内容的“噪音”/包装。
同理,那么,我们有
(>>=) :: ST a -> (a -> ST b) -> ST b
s >>= f = -- f (fst (runState s 0)) -- nah, 0? what's that?
--
-- runState (s >>= f) n = runState (f a) i where
-- (a, i) = runState s n
--
S $ \ n -> let (a, i) = runState s n in
runState (f a) i
因为现在我们看到了 Int
(即在范围内),n
,当组合计算 将 提供给我们 s >>= f
将“运行”。我的意思是,什么时候 runState
.
当然,在 main
调用之前,运行 实际上什么也没有。但它可以成为一个有用的比喻。
我们定义它的方式既是最简单的也是最通用的,这通常是要走的路。不过,还有更多方法可以使类型适合。
一个是使用 n
两次,在第二个 runState
的输入中也是如此,但这将使 i
挂起而未被使用。
另一种方法是将时间箭头翻转 w.r.t。状态通过,
S $ \ n -> let (a, i2) = runState s i
(b, i ) = runState (f a) n
in (b, i2)
至少可以说这有点奇怪。 s
仍然 运行 首先(正如 s >>= f
组合所预期的那样)产生值 a
,f
从中创建第二个计算阶段,但是状态正在向相反方向传递。
我正在尝试为自定义类型 ST a
(>>=)
创建一个实例
我找到了这种方法,但我不喜欢那种硬编码 0。
有没有什么方法可以在不使用硬编码 0 并且不考虑函数类型的情况下实现它?
newtype ST a = S (Int -> (a, Int))
-- This may be useful to implement ">>=" (bind), but it is not mandatory to use it
runState :: ST a -> Int -> (a, Int)
runState (S s) = s
instance Monad ST where
return :: a -> ST a
return x = S (\n -> (x, n))
(>>=) :: ST a -> (a -> ST b) -> ST b
s >>= f = f (fst (runState s 0))
要牢记的最重要的一点是,您的 ST
类型是 函数 的包装器。如果您开始定义为 (>>=) = \s -> \f -> S (\n -> ... )
会怎样?在那里为 s
和 f
参数编写单独的 lambda 可能(好吧,是)有点傻,但我这样做是为了表明它们与 n
参数。您可以在 (>>=)
.
我经常发现使用某种类型的伪代码重写更容易遵循此类代码,如下所示:从
开始instance Monad ST where
return :: a -> ST a
return x = S (\n -> (x, n))
我们到了
runState (return x) n = (x, n)
表达的完全一样。它现在是一种定义,通过它必须遵循的相互作用定律。这让我可以忽略围绕基本内容的“噪音”/包装。
同理,那么,我们有
(>>=) :: ST a -> (a -> ST b) -> ST b
s >>= f = -- f (fst (runState s 0)) -- nah, 0? what's that?
--
-- runState (s >>= f) n = runState (f a) i where
-- (a, i) = runState s n
--
S $ \ n -> let (a, i) = runState s n in
runState (f a) i
因为现在我们看到了 Int
(即在范围内),n
,当组合计算 将 提供给我们 s >>= f
将“运行”。我的意思是,什么时候 runState
.
当然,在 main
调用之前,运行 实际上什么也没有。但它可以成为一个有用的比喻。
我们定义它的方式既是最简单的也是最通用的,这通常是要走的路。不过,还有更多方法可以使类型适合。
一个是使用 n
两次,在第二个 runState
的输入中也是如此,但这将使 i
挂起而未被使用。
另一种方法是将时间箭头翻转 w.r.t。状态通过,
S $ \ n -> let (a, i2) = runState s i
(b, i ) = runState (f a) n
in (b, i2)
至少可以说这有点奇怪。 s
仍然 运行 首先(正如 s >>= f
组合所预期的那样)产生值 a
,f
从中创建第二个计算阶段,但是状态正在向相反方向传递。