gpt4 book ai didi

haskell - 没有包装值(value)的单子(monad)?

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

大多数 monad 解释都使用 monad 包装值的示例。例如。 Maybe a ,其中 a类型变量是包装的。但我想知道从不包装任何东西的单子(monad)。

举一个人为的例子,假设我有一个可以控制但没有传感器的真实世界机器人。也许我想像这样控制它:

robotMovementScript :: RobotMonad ()
robotMovementScript = do
moveLeft 10
moveForward 25
rotate 180

main :: IO ()
main =
liftIO $ runRobot robotMovementScript connectToRobot

在我们想象的 API 中, connectToRobot将某种句柄返回给物理设备。此连接成为 RobotMonad 的“上下文”。 .因为我们与机器人的连接永远不会向我们发送值,所以 monad 的具体类型总是 RobotMonad () .

一些问题:
  • 我做的例子看起来对吗?
  • 我是否正确理解了 monad 的“上下文”的概念?我将机器人的连接描述为上下文是否正确?
  • 有一个单子(monad)是否有意义——例如 RobotMonad --从不包装一个值?还是这与单子(monad)的基本概念相反?
  • 幺半群更适合这种应用吗?我可以想象将机器人控制 Action 与 <> 连接起来。 .虽然 do符号似乎更具可读性。
  • 在 monad 的定义中,会/可能有一些东西可以确保类型始终为 RobotMonad () ?

  • 我看过 Data.Binary.Put举个例子。它似乎与我的想法相似(或者可能相同?)。但它也是 involves the Writer monad and the Builder monoid .考虑到这些增加的皱纹和我目前的技能水平,我认为 Put monad 可能不是最有启发性的例子。

    编辑

    我实际上并不需要像这样构建机器人或 API。这个例子完全是人为的。我只需要一个例子,永远不会有理由从 monad 中提取一个值。所以我不是在寻求解决机器人问题的最简单方法。相反,这个关于没有内在值(value)的单子(monad)的思想实验是试图更好地理解单子(monad)。

    最佳答案

    TL; DR Monad 没有它的包装值并不是很特别,你可以将它建模为一个列表。

    有一种东西叫做 Free单子(monad)。它很有用,因为它在某种意义上是所有其他 monad 的良好代表——如果你能理解 Free 的行为的话。 monad 在某些情况下,您可以很好地了解 Monad s 通常会在那里表现。

    看起来像这样

    data Free f a = Pure a
    | Free (f (Free f a))

    并且每当 fFunctor , Free fMonad
    instance Functor f => Monad (Free f) where
    return = Pure
    Pure a >>= f = f a
    Free w >>= f = Free (fmap (>>= f) w)

    那么当 a 时会发生什么?总是 () ?我们不需要 a参数不再
    data Freed f = Stop 
    | Freed (f (Freed f))

    显然这不可能是 Monad不再是因为它有错误的种类(类型的类型)。
    Monad f ===> f       :: * -> *
    Freed f :: *

    但是我们仍然可以定义像 Monad 这样的东西。 ic 功能通过摆脱 a部分
    returned :: Freed f
    returned = Stop

    bound :: Functor f -- compare with the Monad definition
    => Freed f -> Freed f -- with all `a`s replaced by ()
    -> Freed f
    bound Stop k = k Pure () >>= f = f ()
    bound (Freed w) k = Free w >>= f =
    Freed (fmap (`bound` k) w) Free (fmap (>>= f) w)

    -- Also compare with (++)
    (++) [] ys = ys
    (++) (x:xs) ys = x : ((++) xs ys)

    看起来是(并且是!) Monoid .
    instance Functor f => Monoid (Freed f) where
    mempty = returned
    mappend = bound

    Monoid s 最初可以通过列表建模。我们使用列表的通用属性 Monoid如果我们有一个函数 Monoid m => (a -> m)那么我们可以翻个列表 [a]进入 m .
    convert :: Monoid m => (a -> m) -> [a] -> m
    convert f = foldr mappend mempty . map f

    convertFreed :: Functor f => [f ()] -> Freed f
    convertFreed = convert go where
    go :: Functor f => f () -> Freed f
    go w = Freed (const Stop <$> w)

    因此,对于您的机器人,我们只需使用 Action 列表即可
    data Direction = Left | Right | Forward | Back
    data ActionF a = Move Direction Double a
    | Rotate Double a
    deriving ( Functor )

    -- and if we're using `ActionF ()` then we might as well do

    data Action = Move Direction Double
    | Rotate Double

    robotMovementScript = [ Move Left 10
    , Move Forward 25
    , Rotate 180
    ]

    现在,当我们将其转换为 IO我们显然正在将此方向列表转换为 Monad我们可以看到,我们的初始 Monoid并将其发送至 Freed然后治疗 Freed fFree f ()并将其解释为初始 Monad超过 IO我们想要的 Action 。

    但很明显,如果您没有使用“包装”值,那么您并没有真正使用 Monad。结构体。你也可以有一个 list 。

    关于haskell - 没有包装值(value)的单子(monad)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19884548/

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