Удаление условных обозначений для монад

#haskell #monads #do-notation

#хаскелл #монады #условные обозначения

Вопрос:

Когда я изучаю Haskell, я понимаю, что do нотация — это просто синтаксический сахар:

 a = do x <- [3..4]
       [1..2]
       return (x, 42)
  

Переводится в

 a = [3..4] >>= (x -> [1..2] >>= (_ -> return (x, 42)))
  

Я понимаю, что, вероятно, буду использовать обозначение do, но я хотел бы понять, что происходит при переводе. Итак, чисто по педагогическим соображениям, есть ли способ для ghc / ghci предоставить мне соответствующие инструкции привязки для довольно сложной монады, написанной в do-нотации?

Редактировать. Оказывается, lambdabot на #haskell может это сделать:

 <Guest61347> @undo do x <- [3..4] ; [1..2] ; return (x, 42)
<lambdabot> [3 .. 4] >>=  x -> [1 .. 2] >> return (x, 42)
  

Вот исходный код плагина отмены.

Комментарии:

1. на самом деле m >>= (_ -> k) это m >> k

2. У Lambdabot есть плагин «отменить», который удаляет обозначения

3. Я просто учусь и чувствую, что это ключ к реальному пониманию деталей конкретного экземпляра — все они написаны в терминах >>=

Ответ №1:

Вы можете запросить выходные данные у GHC desugarer , однако это также приведет к удалению множества других синтаксисов.

Сначала мы поместим ваш код в модуль Foo.hs :

 module Foo where

a = do x <- [3..4]
       [1..2]
       return (x, 42)
  

Далее мы попросим GHC скомпилировать его и вывести результат после этапа десугаринга:

 $ ghc -c Foo.hs -ddump-ds
  

Вывод может выглядеть довольно беспорядочно, потому что это вариант Haskell под названием Core, который используется в качестве промежуточного языка GHC. Однако, это не слишком сложно читать, как только вы к этому привыкнете. В середине некоторых других определений мы находим ваше:

 Foo.a :: [(GHC.Integer.Type.Integer, GHC.Integer.Type.Integer)]
LclIdX
[]
Foo.a =
  >>=_agg
    @ GHC.Integer.Type.Integer
    @ (GHC.Integer.Type.Integer, GHC.Integer.Type.Integer)
    (enumFromTo_ag7
       (GHC.Integer.smallInteger 3) (GHC.Integer.smallInteger 4))
    ( (x_adf :: GHC.Integer.Type.Integer) ->
       >>_agn
         @ GHC.Integer.Type.Integer
         @ (GHC.Integer.Type.Integer, GHC.Integer.Type.Integer)
         (enumFromTo_ags
            (GHC.Integer.smallInteger 1) (GHC.Integer.smallInteger 2))
         (return_aki
            @ (GHC.Integer.Type.Integer, GHC.Integer.Type.Integer)
            (x_adf, GHC.Integer.smallInteger 42)))
  

Ядро не слишком красиво, но возможность его чтения очень полезна при работе с GHC, поскольку вы можете прочитать дамп после более поздних этапов, чтобы увидеть, как GHC оптимизирует ваш код.

Если мы удалим _xyz суффиксы, добавленные переименователем, а также приложения типа @ Xyz и вызовы GHC.Integer.smallInteger , и снова сделаем операторы инфиксными, у вас останется что-то вроде этого:

 Foo.a :: [(GHC.Integer.Type.Integer, GHC.Integer.Type.Integer)]
Foo.a = enumFromTo 3 4 >>= x -> enumFromTo 1 2 >> return (x, 42)
  

Комментарии:

1. Что вызывает вопрос… есть ли что-нибудь, что автоматически удалит суффиксы _xyz, приложения типа, вызовы GHC. Integer.SmallInteger и снова сделать операторы инфиксными?

2. @missingno: Ничего, о чем я знаю. Тем не менее, существуют библиотеки, доступные для работы с Haskell или Core в Hackage, поэтому не должно быть слишком сложно взломать что-то вместе, если вы хотите этого достаточно сильно.

3. Проблема в том, что к тому времени, когда вы напишете код для этого, он вам больше не понадобится.

4. Просто для удовольствия, быстрый и грязный разделитель условных обозначений для Haskell 98 .

5. @отсутствует флаг для подавления достаточности, ghc -ddump-ds -dsuppress-uniques Foo.hs который удалит их из выходных данных. Начиная с ghc-7.2, также есть -ddump-to-file флаг, который заставит ghc сбрасывать выходные данные в, в данном случае Foo.dump-ds . Очень полезно, если вам нужно больше одного дампа, например, ghc -ddump-ds -ddump-simpl -ddump-to-file Foo создаст Foo.dump-ds и Foo.dump.simpl (кстати., -ddump-simpl также обеспечивает разделяющую запись, если вы компилируете без оптимизации).