Как читать упрощенный вывод? - PullRequest
6 голосов
/ 26 октября 2019

Рассмотрим простую функцию из недавнего вопроса :

myButLast :: [a] -> a
myButLast [x, y] = x
myButLast (x : xs) = myButLast xs
myButLast _ = error "List too short"

Мы можем попросить GHC дать нам вывод упрощения с ghc -ddump-simpl. (возможно, с некоторыми дополнительными флагами вроде -dsuppress-module-prefixes -dsuppress-uniques.) Как я понимаю, это последний этап компиляции, где результат все еще имеет какое-то сходство с исходным высоким уровнемкод. Итак, вот что он говорит:

-- RHS size: {terms: 21, types: 22, coercions: 0, joins: 0/0}
myButLast :: forall a. [a] -> a
[GblId,
 Arity=1,
 Str=<S,1*U>,
 Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
         WorkFree=True, Expandable=True, Guidance=IF_ARGS [30] 100 0}]
myButLast
  = \ (@ a) (ds :: [a]) ->
      case ds of {
        [] -> myButLast1 @ a;
        : x ds1 ->
          case ds1 of {
            [] -> myButLast1 @ a;
            : y ds2 ->
              case ds2 of {
                [] -> x;
                : ipv ipv1 -> myButLast_$smyButLast1 @ a y ipv ipv1
              }
          }
      }

Что здесь происходит? Давайте посмотрим.

  1. К сигнатуре типа, теперь с явным квантификатором, прикреплены какие-то аннотации. Я могу предположить, что они говорят «глобальный идентификатор, унарный, верхний уровень» , что все верно для этой функции. Другие аннотации, такие как WorkFree=True, Str=<S,1*U>, для меня загадочны.

  2. Определение "value" теперь является лямбда-выражением, которое принимает, кроме того,в список, аргумент типа переменная, и приступает к изучению списка на основе анализа кейсов. [] -> myButLast1 @ a - это прославленный вызов ошибки, поэтому давайте пока проигнорируем его. Интересной частью является вызов myButLast_$smyButLast1 (Что это за имя? Я думал, что знак $ не может быть частью идентификатора.) , который оказывается хвостовой рекурсивной функцией. который фактически пересекает список.

  3. И вот он, единственный элемент того, что мы распознаем как взаимно рекурсивный блок:

    Rec {
    -- RHS size: {terms: 13, types: 12, coercions: 0, joins: 0/0}
    myButLast_$smyButLast1 [Occ=LoopBreaker]
      :: forall a. a -> a -> [a] -> a
    [GblId,
     Arity=3,
     Caf=NoCafRefs,
     Str=<L,1*U><L,1*U><S,1*U>,
     Unf=OtherCon []]
    myButLast_$smyButLast1
      = \ (@ a) (sc :: a) (sc1 :: a) (sc2 :: [a]) ->
          case sc2 of {
            [] -> sc;
            : ipv ipv1 -> myButLast_$smyButLast1 @ a sc1 ipv ipv1
          }
    end Rec }
    

    Это довольно ясно,но у него есть некоторые новые для нас особенности, такие как рекурсивный разделитель блоков Rec ... end Rec и загадочное замечание [Occ=LoopBreaker]. Аннотации также различны: массив Unf пуст, и вместо него появляется поле Caf. Я могу только заключить, что интересующее поле Unf является качеством имен, определенных программистом, а компилятор создает myButLast_$smyButLast1.

Итак, я могу понятьполовина того, что дает мне упрощитель, по количеству строк, но в некоторых частях я даже не могу догадаться о значении.

  • Предполагается, что упрощательвывод обычно является наиболее полезным промежуточным представлением, верно?

  • Правильно ли мое чтение до сих пор?

  • Есть ли руководство для всех этих загадочныхзамечания? Что они означают?

1 Ответ

2 голосов
/ 26 октября 2019

Мне не известно ни руководство, ни отдельная документация по Core, которая бы детализировала то, что вы ищете. Конечно, есть эта страница на Core из Wiki, но она просто объясняет язык Core на высоком уровне и делает это главным образом в терминах структур данных компилятора, используемых для представления дерева абстрактного синтаксиса Core,а не конкретный синтаксис «симпатичной печати».

Единственный способ получить нужные детали - это загрузить копию исходного кода GHC и начать просматривать код в ghc/compiler/coreSyn/.

* 1007. * GHC большой и сложный, но большая часть написана на Haskell, и большая часть кода очень высокого уровня и вполне читабельна, а исходный код тщательно прокомментирован с отличными пояснениями и примечаниями, разбросанными повсюду. * Если вы хотите знать, что означает, например, WorkFree=True, вы должны:
  1. Найти код в PprCore.hs, который генерирует аннотацию, чтобы определить, что это uf_is_work_freeполе CoreUnfolding.

  2. Проверьте определение CoreUnfolding в CoreSyn.hs и связанных с ним комментариях, где вы можете увидеть, что Unfolding является представителемИдентификатор, который может быть заменен при вставке, и флаг uf_is_work_free является кэшированной копией exprIsWorkFree, которая как-то указывает, что вставка развертываемого файла не «тратит впустую».

  3. Просмотрите комментарии в CoreSyn.hs, CoreUnfold.hs и CoreUtils.hs, чтобы найти дополнительное объяснение exprIsWorkFree и его значения:

    exprIsWorkFree используется при принятии решения о том,встроить что-то;мы не включаем его, если это может дублировать работу, снимая полную копию выражения.

    Пример приведен:

    let x = a #+ b in x +# x  -- I think `#+` is a typo and should be `+#`
    

    , где указаночто x не является «свободной работой», так как если бы оно было встроено в RHS, это привело бы к двойной оценке операции a +# b

В вашем случае версияиз myButLast в выводе вашего Core не требует работы, потому что он не оценивает выражения независимо от аргументов, которые могут быть повторно использованы каждый раз, когда применяется myButLast.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...