是否可以使用SYB来转换类型?

发布于 2024-10-26 12:41:30 字数 1286 浏览 1 评论 0原文

我想编写一个 rename 函数,用 GUID 替换 AST 中的 String 名称(代表分层标识符)来自符号表的名称(整数)作为 Renamer monad 中的隐藏状态携带。

我有一个 AST a 类型,它通过名称类型进行参数化。 AST 叶子中的名称属于 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 Stringc GUID 的所有其他值与相同的构造函数一起使用,尽管具有不同的类型参数?

everywhereM 函数很接近我想要的,但它只能转换 ca ->; m (ca) 而不是 ca ->米(CB)。

我的后备解决方案(除了手动编写样板之外)是从 AST 中删除类型参数,并像这样定义 Name

data Name = StrName String
          | GuidName GUID

以便键入重命名:

rename :: AST -> Renamer AST

使其与 everywhereM 一起工作。但是,这可能会导致 AST 在重命名后仍然可以保存 StrName。我想使用类型系统来正式捕获重命名的 AST 只能保存 GUID 名称这一事实。

I want to write a rename function to replace String names (which represent hierarchical identifiers) in my AST with GUID names (integers) from a symbol table carried as hidden state in a Renamer monad.

I have an AST a type that is parameterized over the type of name. Names in the leaves of the AST are of type Name a:

data Name a = Name a

Which makes it easy to target them with a SYB transformer.

The parser is typed (ignoring the possibility of error for brevity):

parse :: String -> AST String

and I want the rename function to be typed:

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

Is it possible to use SYB to transform all Name String's into Name GUID's with a transformer:

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

and all other values from c String to c GUID by transforming their children, and pasting them back together with the same constructor, albeit with a different type parameter?

The everywhereM function is close to what I want, but it can only transform c a -> m (c a) and not c a -> m (c b).

My fallback solution (other than writing the boiler-plate by hand) is to remove the type parameter from AST, and define Name like this:

data Name = StrName String
          | GuidName GUID

so that the rename would be typed:

rename :: AST -> Renamer AST

making it work with everywhereM. However, this would leave the possibility that an AST could still hold StrName's after being renamed. I wanted to use the type system to formally capture the fact that a renamed AST can only hold GUID names.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

千年*琉璃梦 2024-11-02 12:41:30

一种解决方案(可能比您希望的效率低)是让您的 AST 成为 FunctorDataTypeable 的实例 (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. 我假设很容易从 resolveName 中消除 Name 位,但我怀疑确实如此。
  2. 您可以将 universeBi 切换为 SYB 中的等效项,但我发现 Uniplate 版本更容易理解。

One solution (perhaps less efficient than you were hoping for) would be to make your AST an instance of Functor, Data and Typeable (GHC 7 can probably derive all of these for you) then do:

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

Two points:

  1. I'm assuming it's easy to eliminate the Name bit from resolveName, but I suspect it is.
  2. You can switch universeBi for something equivalent in SYB, but I find it much easier to understand the Uniplate versions.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文