Рассмотрим простую функцию из недавнего вопроса :
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
}
}
}
Что здесь происходит? Давайте посмотрим.
К сигнатуре типа, теперь с явным квантификатором, прикреплены какие-то аннотации. Я могу предположить, что они говорят «глобальный идентификатор, унарный, верхний уровень» , что все верно для этой функции. Другие аннотации, такие как WorkFree=True
, Str=<S,1*U>
, для меня загадочны.
Определение "value" теперь является лямбда-выражением, которое принимает, кроме того,в список, аргумент типа переменная, и приступает к изучению списка на основе анализа кейсов. [] -> myButLast1 @ a
- это прославленный вызов ошибки, поэтому давайте пока проигнорируем его. Интересной частью является вызов myButLast_$smyButLast1
(Что это за имя? Я думал, что знак $
не может быть частью идентификатора.) , который оказывается хвостовой рекурсивной функцией. который фактически пересекает список.
И вот он, единственный элемент того, что мы распознаем как взаимно рекурсивный блок:
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
.
Итак, я могу понятьполовина того, что дает мне упрощитель, по количеству строк, но в некоторых частях я даже не могу догадаться о значении.
Предполагается, что упрощательвывод обычно является наиболее полезным промежуточным представлением, верно?
Правильно ли мое чтение до сих пор?
Есть ли руководство для всех этих загадочныхзамечания? Что они означают?