Я играл с компилятором Elm, который написан в Haskell.
Я хотел бы начать реализацию некоторых оптимизаций для него, а часть этого включает в себя обход AST и добавление "аннотации" к определенным узлам, например, к хвостовым вызовам и т.д.
Я знаю, что могу использовать SYB или uniplate для обхода, но мне интересно, есть ли способ без шаблонов справиться с типами.
Итак, предположим, что у нас есть группа алгебраических типов для нашего AST:
data Expr = PlusExpr Expr Expr ...
data Def = TypeAlias String [String] Type ...
Если бы я писал шаблон, я бы сделал новые типы следующим образом:
data AnnotatedExpr = PlusExpr Expr Expr [Annotation] ...
data AnnotatedDef = TypeAlias String [String] Type [Annotation] ...
Это большой набор команд для написания, и это кажется хорошей практикой, чтобы избежать этого.
Я мог бы написать что-то вроде этого:
Data AnnotationTree = Leaf [Annotation]
| Internal [AnnotationTree] [Annotation]
Тогда у меня просто будет дерево аннотаций, идущее параллельно АСТ. Но нет никакой гарантии, что эти деревья будут иметь одинаковую структуру, поэтому мы теряем безопасность типов.
Итак, мне интересно, есть ли элегантное/рекомендуемое решение, чтобы избежать шаблона, но все же аннотировать дерево безопасным типом? Чтобы заменить каждый node на эквивалентный, плюс список аннотаций, которые будут использоваться позже в компиляции?