Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 没有包装值的Monad?_Haskell_Functional Programming_Monads - Fatal编程技术网

Haskell 没有包装值的Monad?

Haskell 没有包装值的Monad?,haskell,functional-programming,monads,Haskell,Functional Programming,Monads,大多数monad解释都使用monad封装值的示例。例如,可能是一个,其中a类型变量是包装的。但我想知道的是,单子从不包任何东西 举一个人为的例子,假设我有一个真实世界的机器人,可以控制,但没有传感器。也许我想这样控制它: robotMovementScript :: RobotMonad () robotMovementScript = do moveLeft 10 moveForward 25 rotate 180 main :: IO () main = liftIO $

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

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

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

main :: IO ()
main = 
  liftIO $ runRobot robotMovementScript connectToRobot
在我们想象的API中,
connectorobot
向物理设备返回某种句柄。此连接成为
机器人单子的“上下文”。因为我们与机器人的连接永远无法向我们发送值,所以monad的具体类型总是
RobotMonad()

一些问题:

  • 我做作的例子似乎正确吗
  • 我是否正确理解单子的“上下文”概念?我将机器人的连接描述为上下文对吗
  • 有一个从未封装过值的monad(比如
    RobotMonad
    )有意义吗?或者这与单子的基本概念相反
  • 幺半群更适合这种应用吗?我可以想象将机器人控制动作与
    连接起来。虽然
    do
    符号看起来更具可读性
  • 在monad的定义中,是否有某种东西可以确保类型总是
    RobotMonad()
  • 我以
    Data.Binary.Put
    为例。它似乎与我的想法相似(或者完全相同?)。但它也是。考虑到这些增加的皱纹和我目前的技能水平,我认为
    Put
    monad可能不是最有启发性的例子

    编辑

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

    data Useless a = Useless
    instance Monad Useless where
      return = const Useless
      Useless >>= f = Useless
    
    但正如我指出的,这是没有用的


    您需要的是
    编写器
    monad,它将一个幺半群包装为一个monad,这样您就可以使用do表示法了。

    看起来您有一个类型只支持

    (>>) :: m a -> m b -> m b
    
    但是您进一步指定您只希望能够使用
    m()
    s。在这种情况下,我会投赞成票

    foo = mconcat
          [ moveLeft 10
          , moveForward 25
          , rotate 180]
    
    作为简单的解决方案。另一种选择是做类似的事情

    type Robot = Writer [RobotAction]
    inj :: RobotAction -> Robot ()
    inj = tell . (:[])
    
    runRobot :: Robot a -> [RobotAction]
    runRobot = snd . runWriter
    
    foo = runRobot $ do
      inj $ moveLeft 10
      inj $ moveForward 25
      inj $ rotate 180
    
    runRobotT :: (Monad m) => RobotMonadT m () -> RobotHandle -> IO (m ())
    
    使用
    Writer
    monad

    不包装该值的问题是

    return a >>= f === f a
    
    假设我们有一个monad忽略了这个值,但是包含了其他有趣的信息

    newtype Robot a = Robot {unRobot :: [RobotAction]}
    
    addAction :: RobotAction -> Robot a -> Robot b
    
    f a = Robot [a]
    
    现在如果我们忽略这个值

    instance Monad Robot where
      return = const (Robot [])
      a >>= f = a -- never run the function
    
    然后

    所以我们没有单子。因此,如果您想让monad有任何有趣的状态,让
    =
    返回false,那么您需要存储该值。

    TL;没有包装值的Monad博士并不特别,你可以将它作为一个列表进行建模

    有一种东西叫做免费的单子。它很有用,因为它在某种意义上是所有其他单子的一个很好的代表——如果你能理解
    Free
    monad在某些情况下的行为,你就能很好地理解
    monad
    s通常在那里的行为

    看起来像这样

    data Free f a = Pure a
                  | Free (f (Free f a))
    
    每当
    f
    函子时,
    Free f
    就是
    Monad

    instance Functor f => Monad (Free f) where
      return       = Pure
      Pure a >>= f = f a
      Free w >>= f = Free (fmap (>>= f) w)
    
    那么当
    a
    总是
    ()
    时会发生什么呢?我们不再需要
    参数了

    data Freed f = Stop 
                 | Freed (f (Freed f))
    
    显然,这不能再是
    Monad
    ,因为它的种类(类型)不正确

    但我们仍然可以通过去掉
    a
    部分来定义类似于
    Monad
    ic的功能

    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最初可以通过列表建模。我们使用list
    Monoid
    的通用属性,其中如果我们有一个函数
    monoidm=>(a->m)
    ,那么我们可以将list
    [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)
    
    现在,当我们将其转换为
    IO
    时,我们很清楚地将这个方向列表转换为
    Monad
    ,我们可以看到,作为初始
    Monoid
    并将其发送到
    Freed
    ,然后将
    Freed f
    视为
    Free f()
    并将其解释为我们想要的
    IO
    操作的初始
    Monad


    但很明显,如果您没有使用“包装”值,那么您就没有真正使用
    Monad
    结构。你最好有一个列表。

    我将尝试对以下部分给出部分答案:

    • 有一个从未封装过值的monad(比如
      RobotMonad
      )有意义吗?或者这与单子的基本概念相反
    • 幺半群更适合这种应用吗?我可以想象将机器人控制动作与
      连接起来。虽然do符号看起来更具可读性
    • 在monad的定义中,是否有某种东西可以确保类型总是
      RobotMonad()
    monads的核心操作是monadic绑定操作

    (>>=) :: (Monad m) => m a -> (a -> m b) -> m b
    
    这意味着一个动作取决于(或可以取决于)前一个动作的值。因此,如果你有一个概念,它本身有时并不包含可以被视为值的东西(即使是在一个复杂的形式中,比如延续单子),单子不是一个好的抽象

    如果我们放弃
    >=
    我们基本上只剩下
    Applicative
    。它还允许我们组合动作,但它们的组合不能依赖于前面动作的值

    正如您所建议的,还有一个不带值的
    Applicative
    实例:。其
    a
    类型的动作必须是幺半群
    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
                          ]
    
    (>>=) :: (Monad m) => m a -> (a -> m b) -> m b
    
    runRobot :: RobotMonad () -> RobotHandle -> IO ()
    
    runRobotT :: (Monad m) => RobotMonadT m () -> RobotHandle -> IO (m ())
    
    runRobotT :: (MonadIO m) => RobotMonadT m () -> RobotHandle -> m ()