gpt4 book ai didi

haskell - 这样的功能是否已经存在? (或者,这个函数有什么更好的名字?)

转载 作者:行者123 更新时间:2023-12-03 15:27:14 25 4
gpt4 key购买 nike

我最近多次使用以下模式编写代码,并且想知道是否有更短的编写方法。

foo :: IO String
foo = do
x <- getLine
putStrLn x >> return x

为了让事情更简洁,我写了这个函数(虽然我不确定它是一个合适的名字):
constM :: (Monad m) => (a -> m b) -> a -> m a
constM f a = f a >> return a

然后我可以像这样制作 foo :
foo = getLine >>= constM putStrLn

是否已经存在这样的函数/习语?如果没有,我的 constM 有什么更好的名字?

最佳答案

好吧,让我们考虑可以简化这样的事情的方法。我猜非 monadic 版本看起来像 const' f a = const a (f a) ,显然等价于 flip const具有更具体的类型。然而,对于 monadic 版本,f a 的结果可以对仿函数的非参数结构做任意的事情(即,通常称为“副作用”),包括依赖于 a 的值的事情。 .这告诉我们的是,尽管我们假装丢弃了 f a 的结果,我们实际上什么都不做。返回 a不变,因为仿函数的参数部分不太重要,我们可以替换 return与其他东西并在概念上仍然具有相似的功能。

所以我们可以得出的第一件事是,它可以被看作是如下函数的一个特例:

doBoth :: (Monad m) => (a -> m b) -> (a -> m c) -> a -> m c
doBoth f g a = f a >> g a

从这里开始,有两种不同的方法可以寻找某种底层结构。

一种观点是识别 的模式在多个函数中拆分单个参数,然后重新组合结果 .这就是 Applicative所体现的理念。/ Monad函数的实例,像这样:
doBoth :: (Monad m) => (a -> m b) -> (a -> m c) -> a -> m c
doBoth f g = (>>) <$> f <*> g

...或者,如果您愿意:
doBoth :: (Monad m) => (a -> m b) -> (a -> m c) -> a -> m c
doBoth = liftA2 (>>)

当然, liftA2相当于 liftM2所以你可能想知道将 monad 上的操作提升到另一个 monad 是否与 monad 转换器有关;一般来说,那里的关系很尴尬,但在这种情况下,它很容易工作,给出如下内容:
doBoth :: (Monad m) => ReaderT a m b -> ReaderT a m c -> ReaderT a m c
doBoth = (>>)

...模适当的包装等,当然。专修回你原来的版本,原用 return现在需要是类型为 ReaderT a m a 的东西,这应该不会太难识别为 ask读者单子(monad)的功能。

另一个角度是识别具有类似 (Monad m) => a -> m b 类型的函数。 可以直接组合,很像纯函数 .函数 (<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)直接等价于函数组合 (.) :: (b -> c) -> (a -> b) -> (a -> c) ,或者您可以改为使用 Control.Categorynewtype wrapper Kleisli以通用的方式处理相同的事情。

然而,我们仍然需要拆分参数,所以我们在这里真正需要的是一个“分支”组合,即 Category一个人没有;通过使用 Control.Arrow我们也得到了 (&&&) ,让我们将函数改写如下:
doBoth :: (Monad m) => Kleisli m a b -> Kleisli m a c -> Kleisli m a (b, c)
doBoth f g = f &&& g

由于我们不关心第一个 Kleisli 箭头的结果,只关心它的副作用,我们可以以明显的方式丢弃元组的那一半:
doBoth :: (Monad m) => Kleisli m a b -> Kleisli m a c -> Kleisli m a c
doBoth f g = f &&& g >>> arr snd

这让我们回到通用形式。专注于您的原创, return现在变得简单 id :
constKleisli :: (Monad m) => Kleisli m a b -> Kleisli m a a
constKleisli f = f &&& id >>> arr snd

由于常规函数也是 Arrow s,如果您概括类型签名,上面的定义也适用于那里。但是,扩展纯函数的定义并简化如下可能会有所启发:
  • \f x -> (f &&& id >>> arr snd) x
  • \f x -> (snd . (\y -> (f y, id y))) x
  • \f x -> (\y -> snd (f y, y)) x
  • \f x -> (\y -> y) x
  • \f x -> x .

  • 所以我们回到 flip const ,正如预期的那样!

    简而言之,您的函数是 (>>) 的一些变体。或 flip const ,但在某种程度上依赖于差异——前者同时使用 ReaderT环境和 (>>)底层 monad,后者使用特定 Arrow 的隐式副作用和期望 Arrow副作用以特定顺序发生。由于这些细节,不可能有任何概括或简化。从某种意义上说,您使用的定义与它需要的一样简单,这就是为什么我给出的替代定义更长和/或涉及一定数量的包装和展开的原因。

    像这样的函数将是某种“monad 实用程序库”的自然补充。虽然 Control.Monad沿着这些方向提供了一些组合器,它远非详尽无遗,我既找不到也找不到标准库中此函数的任何变化。然而,在 hackage 上的一个或多个实用程序库中找到它我一点也不感到惊讶。

    基本上没有存在的问题,除了你可以从上面关于相关概念的讨论中得到的之外,我真的不能提供太多关于命名的指导。

    最后,还要注意您的函数没有基于一元表达式结果的控制流选择,因为无论主要目标是什么,都将执行表达式。拥有独立于参数内容的计算结构(即 a 中的 Monad m => m a 类型的东西)通常表明您实际上并不需要完整的 Monad ,并且可以使用更一般的概念 Applicative .

    关于haskell - 这样的功能是否已经存在? (或者,这个函数有什么更好的名字?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7518589/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com