Можно ли использовать SYB для преобразования типа? - PullRequest
5 голосов
/ 25 марта 2011

Я хочу написать rename функцию для замены String имен (которые представляют иерархические идентификаторы) в моих AST именами GUID (целыми числами) из таблицы символов, переносимой как скрытое состояние в Renamer монада.

У меня есть тип AST a, который параметризован по типу имени. Имена в листах АСТ имеют тип Name a:

data Name a = Name a

Что облегчает нацеливание на них с помощью трансформатора SYB.

Парсер набирается (игнорируя возможность ошибки для краткости):

parse :: String -> AST String

и я хочу, чтобы набиралась функция rename:

rename :: AST String -> Renamer (AST GUID)

Можно ли использовать SYB для преобразования всех Name String в Name GUID с помощью преобразователя:

resolveName :: Name String -> Renamer (Name GUID)

и все другие значения от c String до c GUID путем преобразования их потомков и вставки их обратно вместе с тем же конструктором, хотя и с другим параметром типа?

Функция everywhereM близка к тому, что я хочу, но она может преобразовывать только c a -> m (c a), а не c a -> m (c b).

Мое альтернативное решение (кроме написания вручную) - удалить параметр типа из AST и определить Name следующим образом:

data Name = StrName String
          | GuidName GUID

, чтобы переименование было набрано:

rename :: AST -> Renamer AST

заставить его работать с everywhereM. Тем не менее, это оставляет возможность того, что AST может все еще содержать StrName после переименования. Я хотел использовать систему типов, чтобы формально зафиксировать тот факт, что переименованный AST может содержать только GUID имен.

1 Ответ

2 голосов
/ 18 июня 2011

Одним из решений (возможно, менее эффективным, чем вы ожидали) было бы сделать ваш AST экземпляром Functor, Data и Typeable (GHC 7, вероятно, может получить все это для вас), а затем выполнить

import Data.Generics.Uniplate.Data(universeBi) -- from the uniplate package
import qualified Data.Map as Map

rename :: AST String -> Renamer (AST GUID)
rename x = do
    let names = nub $ universeBi x :: [Name String]
    guids <- mapM resolveName names
    let mp = Map.fromList $ zip names guids
    return $ fmap (mp Map.!) x

Два очка:

  1. Я предполагаю, что легко удалить бит Name из resolveName, но я подозреваю, что это так.
  2. Вы можете переключить universeBi на что-то эквивалентное в SYB, но я считаю, что гораздо проще понять версии Uniplate.
...