Spec-Zone.ru › Haskell 9

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

QualifiedDo
Since:

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.12.1/docs/users_guide/exts/qualified_do.html

Spec-Zone.ru

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