TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

The AST Typing Problem

74 点作者 smasher164将近 2 年前

11 条评论

rowanG077将近 2 年前
An even easier way is to add type parameter to every AST that resolves to either a type or null. It can be relatively easily implemented using type families in Haskell. And it doesn&#x27;t suffer the tying the knot shenanigans. The main advantage is that you can still reason about your AST in a straight forward generic manner. In fact this is not only useful for typing. Any kind of metadata that needs to be added can be described in this way.<p>In a compiler I build you had a simple ASTState type parameter for every node in the AST which indicated what is available in the AST. But you could traverse over the entire AST generically as long as you did not depend on any of the additional information.<p>You can basically lift the implementation from this paper: <a href="https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;research&#x2F;uploads&#x2F;prod&#x2F;2016&#x2F;11&#x2F;trees-that-grow.pdf" rel="nofollow noreferrer">https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;research&#x2F;uploads&#x2F;prod&#x2F;2016&#x2F;1...</a>
评论 #37117153 未加载
nemo1618将近 2 年前
I&#x27;ll contribute another solution, used in the Go compiler: build a big map from AST node -&gt; typeinfo. This approach is very flexible (e.g. you can add more maps later without changing the AST struct), but, like Nullable, it&#x27;s not correct-by-construction -- and worse, you &quot;just have to know&quot; that this map exists and how to access&#x2F;update it. Seems to be working ok for them, though!
评论 #37117913 未加载
评论 #37117657 未加载
barrkel超过 1 年前
The way I prefer to solve this problem these days is to use a fairly generic tree node structure (fairly close to sexprs) and implement tree grammars which check for correctness of different phases (e.g. verify that nodes are annotated with types, or syntax sugar has been decomposed).<p>Being playful, I&#x27;ve generally implemented the tree grammars as literal constructions of the tree nodes themselves, so the grammars can self-check.<p>I&#x27;ve use this approach in Ruby and Java though, not functional languages with pattern matching and destructuring, so I wasn&#x27;t leaving those powerful tools on the table.<p>In particular I have given up trying to represent ASTs using classes. I don&#x27;t think they deliver a lot of value. Behavior generally belongs outside the class and not inside, transformations should be collected together as an algorithm, rather than distributed across multiple definitions. Double dispatch for type-safe tree navigation is unpleasant and you lose control over typing of context. You end up with explicit typecasts and you&#x27;ve lost what little type safety you had, and you still can&#x27;t enforce constraints around how the tree changes from phase to phase.
chrisaycock将近 2 年前
The beauty of separate IRs is that each can represent a separate stage of compilation. For example, Rust goes from abstract syntax tree (AST) to high-level IR (HIR) to typed high-level IR (THIR):<p><a href="https:&#x2F;&#x2F;rustc-dev-guide.rust-lang.org&#x2F;part-3-intro.html" rel="nofollow noreferrer">https:&#x2F;&#x2F;rustc-dev-guide.rust-lang.org&#x2F;part-3-intro.html</a><p>Additionally, Many language implementations use a mid-level IR to sit above LLVM: Rust has MIR, Swift has SIL, Julia has an SSA-form IR, etc. The article mentions GHC&#x27;s Core, which is also an example of an IR that comes after type checking but before LLVM.
评论 #37120887 未加载
评论 #37118258 未加载
jeswin将近 2 年前
&gt; How does one go about adding “extra information” (e.g. types) to an AST?<p>Can someone who&#x27;s more familiar with compilers ELI10 the problem? A layman like me is left wondering - Why not add (say) a type field to each node?
评论 #37117099 未加载
评论 #37117295 未加载
评论 #37117072 未加载
TOGoS将近 2 年前
Make each AST node an RDF node and then you can cram whatever information into it you want. That&#x27;s the approach I&#x27;ve been taking with <a href="https:&#x2F;&#x2F;github.com&#x2F;TOGoS&#x2F;TOGVM-Spec&#x2F;">https:&#x2F;&#x2F;github.com&#x2F;TOGoS&#x2F;TOGVM-Spec&#x2F;</a>, anyway.<p>Of course, for conveniently and safely manipulating in memory in $programming_language, you&#x27;re probably going to want to define some structs&#x2F;ADTs&#x2F;whatever that only contain the data a given compilation stage is actively working with.<p>I&#x27;ve been thinking that what I need is a system that allows me to quickly define different lower-level datatypes for representing different views of the conceptual types and automate, to some degree, translation between them, so then each part of the system can work with objects designed specifically to be processed by it with minimal fuss.<p>A technical reason for avoiding those specialized types might be that the computer then has to spend more time transforming from one schema to the next. I would think that in practice this isn&#x27;t any worse than having to do a lot of null checks.<p>A more human reason is that it could bean a combinatorical explosion of AST types. I guess this is where my idea about lightweight variations comes in.<p>In TypeScript this kind of thing might not be so bad, since any object can be downcast with no cost to a type that contains a subset of the information, and variations on types can be easily defined without even necessarily being named, e.g. `ASTNode &amp; HasResultType &amp; HasSourceLocation`.
nextaccountic将近 2 年前
This article really needs an (2013), because of<p>&gt; Both GHC’s core IR, Ur&#x2F;Web&#x27;s core and Coq are explicitly typed in this way.<p>This is how GHC used to do, but in 2018 GHC adapted the pattern &quot;trees that grow&quot;<p>The paper <a href="https:&#x2F;&#x2F;www.jucs.org&#x2F;jucs_23_1&#x2F;trees_that_grow&#x2F;jucs_23_01_0042_0062_najd.pdf" rel="nofollow noreferrer">https:&#x2F;&#x2F;www.jucs.org&#x2F;jucs_23_1&#x2F;trees_that_grow&#x2F;jucs_23_01_00...</a> (the paper is actually from 2017)<p>Light GHC docs on it <a href="https:&#x2F;&#x2F;gitlab.haskell.org&#x2F;ghc&#x2F;ghc&#x2F;-&#x2F;wikis&#x2F;implementing-trees-that-grow&#x2F;trees-that-grow-guidance" rel="nofollow noreferrer">https:&#x2F;&#x2F;gitlab.haskell.org&#x2F;ghc&#x2F;ghc&#x2F;-&#x2F;wikis&#x2F;implementing-tree...</a> (I recall seeing more through docs somewhere). I will quote<p>&gt; The new HsSyn AST supporting the TTG idiom (from now on referred to as TTG HsSyn) is designed to subsume five different representations of Haskell syntax:<p>&gt; AST GhcPs: the AST used in GHC&#x27;s parsing phase &gt; AST GhcRn: the AST used in GHC&#x27;s renaming phase &gt; AST GhcTc: the AST used in GHC&#x27;s typechecking phase &gt; AST TH: the AST used in Template Haskell &gt; AST HSE: the AST used in an external tool such as Haskell-Src-Exts<p>This means that you have a single generic tree for all of those passes, but each pass has a different instantiation with has custom data<p>Also, some discussion of applications <a href="https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;haskell&#x2F;comments&#x2F;7zo9mb&#x2F;any_application_of_trees_that_grow&#x2F;" rel="nofollow noreferrer">https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;haskell&#x2F;comments&#x2F;7zo9mb&#x2F;any_applica...</a> and slides here <a href="http:&#x2F;&#x2F;data.tmorris.net&#x2F;talks&#x2F;trees-that-grow&#x2F;bc4ae902ca1d3bf37af86be8021d64a1de2b5116&#x2F;trees-that-grow.pdf" rel="nofollow noreferrer">http:&#x2F;&#x2F;data.tmorris.net&#x2F;talks&#x2F;trees-that-grow&#x2F;bc4ae902ca1d3b...</a><p>The thing about this is to observe that, in the blog post in the OP, when you added data to a given type, it was inserted as a concrete type (named Type here), that is, you went from<p><pre><code> data Exp = Num Int | Bool Bool | Var Var | If Exp Exp Exp | Lambda Var Exp | App Exp Exp </code></pre> to<p><pre><code> type TExp = (TExp&#x27;, Type) data TExp&#x27; = TNum Int | TBool Bool | TVar Var | TIf TExp TExp TExp | TLambda Var TExp | TApp TExp TExp </code></pre> But you <i>could</i> have kept it a generic type (here called a, rather than Type). This is a common pattern in both Haskell and Rust (and maybe other languages), and isn&#x27;t the Trees That Grow pattern just yet, but it&#x27;s like this:<p><pre><code> type GenericTExp a = (GenericTExp&#x27; a, a) data GenericTExp&#x27; a = TNum Int | TBool Bool | TVar Var | TIf TExp (GenericTExp a) (GenericTExp a) | TLambda Var (GenericTExp a) | TApp TExp (GenericTExp a) </code></pre> Now you can recover the original TExpr with<p><pre><code> type TExpr = GenericTExpr Type </code></pre> And you can recover the bare Expr with no added data type by putting () (called unit type) there<p><pre><code> type Expr = GenericTExpr () </code></pre> Now, the paper also deals with the problem of, how to add new variants to a data type (it has a | Something that is open ended; this is called &quot;extension constructor&quot; in the ghc wiki, and denoted by XExpr), and how to add different data types to different nodes (you need to, instead of directly adding Type to the generic parameter, add a marker and use type-level functions - called type families in Haskell - to select the data type you&#x27;re adding). When you figure out all those complexities, you end up with a type-level machinery that will inevitably be similar to the TTG idiom
teo_zero将近 2 年前
Am I the only one lost at the very first paragraph? It shows something that looks like a grammar, but I can&#x27;t understand what &quot;Num&quot;, &quot;Int&quot;, &quot;If&quot; are... Are they literals? terms? types? And what does &quot;data&quot; mean?<p>So it&#x27;s not a grammar. &quot;data Exp&quot; might describe each internal node of the AST, where the first word is the node&#x27;s variety and the following words describe the children nodes. But what does &quot;data Type&quot; mean, then?
评论 #37118991 未加载
评论 #37118970 未加载
评论 #37119146 未加载
评论 #37118915 未加载
评论 #37118942 未加载
评论 #37119580 未加载
gpderetta将近 2 年前
What about separate IR that incrementally add to the base IR without replacing it? They will have the same shape so they can be encoded efficiently I think, and can be traversed in parallel.
BSEdlMMldESB将近 2 年前
2013!<p>what&#x27;s the difference between running a program (interpreting it), and compiling it?<p>where&#x27;s (and what&#x27;s) the line that distinguishes the program from the output of the program where both parts are just software?<p>--<p>the most interesting thing about turning machines is the universal turing machine because it actually invents&#x2F;defines&#x2F;showcases software as we understand it nowadays; this makes me reflect on how the busy beaber problem just misses out on this realization
3cats-in-a-coat将近 2 年前
Restrictive static typing creates a lot of problems that wouldn&#x27;t exist... if you didn&#x27;t use restrictive static typing. Maybe because my language designs are toys I use in my projects (for now), but everything is nested heterogenous sparse sequences, so it&#x27;s a list, but also a map, and I can attach any associative keys to it without breaking the pattern match on the rest of it, and I&#x27;m never out of place to add information should I need it.<p>I think the problem is we write compilers like it&#x27;s the 90s, and it&#x27;s not the 90s.<p>But also it&#x27;s full of data structure solutions to this even for static types: give nodes a simple autoincrementing id (if your language has no object maps), then put any extra information in a sidecar if you want. Think relations and joins. It&#x27;s easy.
评论 #37120140 未加载