Desugaring do-обозначения для монад
Когда я изучаю Хаскель, я понимаю, что do
обозначение просто синтетический сахар:
a = do x <- [3..4]
[1..2]
return (x, 42)
Переводит на
a = [3..4] >>= (\x -> [1..2] >>= (\_ -> return (x, 42)))
Я понимаю, что, вероятно, буду использовать do-notation, но я бы хотел понять, что происходит в переводе. Итак, чисто по педагогическим причинам, есть ли способ для ghc / ghci дать мне соответствующие операторы связывания для довольно сложной монады, написанной в нотации?
Редактировать. Оказывается, что lambdabot на #haskell может сделать это:
<Guest61347> @undo do x <- [3..4] ; [1..2] ; return (x, 42)
<lambdabot> [3 .. 4] >>= \ x -> [1 .. 2] >> return (x, 42)
Вот исходный код плагина Undo.
1 ответ
Вы можете запросить вывод десугарера GHC, однако это также приведет к десагару многих других синтаксисов.
Сначала мы поместим ваш код в модуль 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)