Spec-Zone.ru › Haskell 9

6.2.5. Квалифицированная do-нотация

QualifiedDo
С версии:

9.0.1

Разрешает использовать квалифицированную нотацию do.

QualifiedDo позволяет квалифицировать блок do именем модуля, чтобы управлять тем, какие операции используются для монадических комбинаторов, в которые разворачивается нотация do. Когда включено -XQualifiedDo, вы можете квалифицировать нотацию do, записав modid.do, где modid — это имя модуля в области видимости:

{-# LANGUAGE QualifiedDo #-}
import qualified Some.Module.Monad as M

action :: M.SomeType a
action = M.do x <- u
              res
              M.return x

Дополнительное имя модуля (здесь M) называется квалификатором do-выражения.

Неквалифицированный синтаксис do удобен для написания монадического кода, но работает только с типами данных, для которых предоставлен экземпляр класса типов Monad. Существуют и другие типы, которые «подобны монадам», но не могут предоставить экземпляр Monad (например, индексированные монады, градуированные монады или относительные монады), однако они всё равно могли бы использовать синтаксис do, если бы он не был жёстко привязан к методам класса типов Monad. -XQualifiedDo позволяет настраивать синтаксис do в этом отношении. Это даёт возможность комбинировать блоки do разных типов, используя для каждого подходящие операции:

{-# LANGUAGE QualifiedDo #-}
import qualified Control.Monad.Linear as L

import MAC (label, box, runMAC)
import qualified MAC as MAC

f :: IO ()
f = do
  x <- runMAC $           -- (Prelude.>>=)
                          --   (runMAC $
    MAC.do                --
      d <- label "y"      --     label "y" MAC.>>= \d ->
      box $               --
                          --       (box $
        L.do              --
          r <- L.f d      --         L.f d L.>>= \r ->
          L.g r           --         L.g r L.>>
          L.return r      --         L.return r
                          --       ) MAC.>>
      MAC.return d        --       (MAC.return d)
                          --   )
  print x                 --   (\x -> print x)

Семантика операторов нотации do с -XQualifiedDo такова:

  • Оператор x <- u использует (M.>>=)

    M.do { x <- u; stmts }  =  u M.>>= \x -> M.do { stmts }
    
  • Оператор u использует (M.>>)

    M.do { u; stmts }  =  u M.>> M.do { stmts }
    
  • Оператор pat <- u использует M.fail для случая ошибки, если такой случай возможен

    M.do { pat <- u; stmts }  =  u M.>>= \case
      { pat -> M.do { stmts }
      ; _ -> M.fail "…"
      }
    

    Если сопоставление с образцом не может завершиться неудачей, то M.fail использовать не нужно.

    M.do { pat <- u; stmts }  =  u M.>>= \case pat -> M.do { stmts }
    
  • Разворачивание -XApplicativeDo использует M.fmap, (M.<*>) и M.join (после группировки applicative-do)

    M.do { (x1 <- u1 | … | xn <- un); M.return e }  =
      (\x1 … xn -> e) `M.fmap` u1 M.<*> … M.<*> un
    
    M.do { (x1 <- u1 | … | xn <- un); stmts }  =
      M.join ((\x1 … xn -> M.do { stmts }) `M.fmap` u1 M.<*> … M.<*> un)
    

Обратите внимание, что M.join требуется только в том случае, если конечное выражение не распознаётся как return. При включённом -XQualifiedDo -XApplicativeDo ищет только квалифицированные return/pure в квалифицированном do-блоке.

  • При включённом -XRecursiveDo блоки rec и mdo используют M.mfix и M.return:

    M.do { rec { x1 <- u1; … ; xn <- un }; stmts }  =
      M.do
      { (x1, …, xn) <- M.mfix (\~(x1, …, xn) -> M.do { x1 <- u1; …; xn <- un; M.return (x1, …, xn)})
      ; stmts
      }
    

Если имя M.op требуется для разворачивания (и только если оно действительно требуется!), но не находится в области видимости, об этом сообщается как об ошибке.

Типы операций, выбранных для разворачивания, должны давать выражение, принимаемое проверкой типов. В остальном никаких особых требований к типам нет.

Если при включённом -XQualifiedDo квалификатор не указан, по умолчанию используются операции, определённые в Prelude, или, если включено -XRebindableSyntax, любые операции, находящиеся в области видимости.

Обратите внимание, что для работы QualifiedDo квалифицируемые операции должны находиться в области видимости. То есть import MAC (label) в приведённом выше примере вызвало бы ошибку, поскольку MAC.>>= и MAC.>> не находились бы в области видимости.

6.2.5.1. Примеры

-XQualifiedDo не влияет на return в монадической нотации do.

import qualified Some.Monad.M as M

boolM :: (a -> M.M Bool) -> b -> b -> a -> M.M b
boolM p a b x = M.do
    px <- p x     -- M.>>=
    if px then
      return b    -- Prelude.return
    else
      M.return a  -- M.return

-XQualifiedDo не влияет на явный (>>=) в монадической нотации do.

import qualified Some.Monad.M as M
import Data.Bool (bool)

boolMM :: (a -> M.M Bool) -> M b -> M b -> a -> M.M b
boolMM p ma mb x = M.do
    p x >>= bool ma mb   -- Prelude.>>=

Вложенные блоки do не влияют на смысл друг друга.

import qualified Some.Monad.M as M

f :: M.M SomeType
f = M.do
    x <- f1                 -- M.>>=
    f2 (do y <- g1          -- Prelude.>>=
           g2 x y)
  where
    f1 = ...
    f2 m = ...
    g1 = ...
    g2 x y = ...

Тип (>>=) также можно изменить, как показано здесь на примере градуированной монады:

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
module Control.Monad.Graded (GradedMonad(..)) where

import Data.Kind (Constraint)

class GradedMonad (m :: k -> * -> *) where
  type Unit m :: k
  type Plus m (i :: k) (j :: k) :: k
  type Inv  m (i :: k) (j :: k) :: Constraint
  (>>=) :: Inv m i j => m i a -> (a -> m j b) -> m (Plus m i j) b
  return :: a -> m (Unit m) a

-----------------

module M where

import Control.Monad.Graded as Graded

g :: GradedMonad m => a -> m SomeTypeIndex b
g a = Graded.do
  b <- someGradedFunction a Graded.>>= someOtherGradedFunction
  c <- anotherGradedFunction b
  Graded.return c

© 2002–2007 The University Court of the University of Glasgow. All rights reserved.
Licensed under the Glasgow Haskell Compiler License.
https://downloads.haskell.org/~ghc/9.14.1/docs/users_guide/exts/qualified_do.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API