В Haskell очень легко писать алгебраические типы данных (ADT) с функциями. Это позволяет нам писать интерпретаторы, которые полагаются на нативные функции для подстановок, т. Е. Абстрактный синтаксис более высокого порядка (HOAS), который, как известно, очень эффективен. Например, это простой переводчик -calculus, используя этот метод:
data Term
= Hol Term
| Var Int
| Lam (Term -> Term)
| App Term Term
pretty :: Term -> String
pretty = go 0 where
go lvl term = case term of
Hol hol -> go lvl hol
Var idx -> "x" ++ show idx
Lam bod -> "λx" ++ show lvl ++ ". " ++ go (lvl+1) (bod (Hol (Var lvl)))
App fun arg -> "(" ++ go lvl fun ++ " " ++ go lvl arg ++ ")"
reduce :: Term -> Term
reduce (Hol hol) = hol
reduce (Var idx) = Var idx
reduce (Lam bod) = Lam (\v -> reduce (bod v))
reduce (App fun arg) = case reduce fun of
Hol fhol -> App (Hol fhol) (reduce arg)
Var fidx -> App (Var fidx) (reduce arg)
Lam fbod -> fbod (reduce arg)
App ffun farg -> App (App ffun farg) (reduce arg)
main :: IO ()
main
= putStrLn . pretty . reduce
$ App
(Lam$ \x -> App x x)
(Lam$ \s -> Lam$ \z -> App s (App s (App s z)))
Обратите внимание, как используются собственные функции, а не индексы Bruijn. Это делает переводчика значительно быстрее, чем это было бы, если бы мы заменяли приложения вручную.
Я знаю, что у Rust есть замыкания и много типов Fn()
, но я не уверен, что они работают точно так же, как закрытие Haskell в этой ситуации, тем более, как выразить эту программу, учитывая низкий уровень Rust. Возможно ли представлять HOAS в Rust? Как будет отображаться тип данных Term
?