Как я могу уменьшить синтаксический беспорядок при пометке полиморфного дерева в Haskell?

Итак, мне нужен тип, представляющий деревья, содержащие набор node -типов. Мне также нравятся типы, которые представляют собой аналогичное дерево, определенное над перекрывающимися наборами. Это еще одна версия проблемы Typed AST. Скажем, что мой пул node -типов:

data Lit = Lit Int
data Var = Var String
data Hole = Hole Int

Дерево разбора может содержать Lit или Var, но не Hole s. Второе дерево вида, называемое шаблоном, может содержать Lit s, Var или Hole s.

Чтобы все было просто, существует один тип рекурсивного node, называемый Add.

data Parse = A Lit | B Var
data Template = C Lit | D Var | E Hole
data Tree a = Leaf a 
            | Add (Tree a) (Tree a)

Итак, теперь я могу объявить данные, и я могу по-прежнему сопоставлять их с шаблонами, единственная проблема - синтаксический беспорядок.

aParse = Add (A Lit 3) (B Var "x")
aTemplate = Add (C Lit 4) (E Hole 3)
fun (Add (A lit) (B var) = ...

Я бы хотел немного сахара, похожего на:

ParseLit = A . Lit
TempLit = C . Lit 

Очевидно, что псевдонимы для композиций конструкторов (не типов) не являются законными в Haskell. Но каков самый чистый способ написать это, избегая как можно большего количества котельной плиты?

Ответ 1

Здесь может помочь расширение языка PatternSynonyms. Он позволяет указать псевдонимы для шаблонов:

{-# LANGUAGE PatternSynonyms #-}

pattern ParseLit x = A (Lit x)

someFunc :: Parse -> Int
someFunc p = case p of
    ParseLit x -> x
    _ -> 0

Существует два варианта синонимов шаблонов: двунаправленный (как в примере) и однонаправленный. Двунаправленные могут также использоваться в качестве конструкторов:

*Main> :t ParseLit
ParseLit :: Int -> Parse

*Main> ParseLit 77