我正在做一个lisp 学习生锈。
作为评估者步骤我需要创建一个关联结构映射字符串(或其他)来函数。在我更熟悉(ruby,clojure)的语言中,我只会在hashmap中定义匿名函数,例如
{ :+ (fn [a b] (+ a b))
:- (fn [a b] (- a b)) } ; etc
在rust中,这是不可能的,因为类型错误。
let repl_env = HashMap::new();
repl_env.insert("+", |a, b| a + b);
repl_env.insert("-", |a, b| a - b); // expected closure, found a different closure
我想这里发生的是
- 编译器正在推断
hashmap
中的类型,因为< string,whyttertytytytytypethefirstclosureis>
- 第二个关闭时间与编译器没有相同的时间错误继续说,
即使相同的类型
也没有两个关闭,即使没有两个封闭?
我可以将类型添加到参数中,从而使返回类型可依靠,但这无济于事 - 我认为我需要以某种方式注释封闭本身的类型吗?我找不到在文档中这样做的方法。此错误建议拳击,但将封闭式包装在框中:: new
无济于事。 (我以前从未 box
ed)。
我还想知道, hashmap
中的生锈是否是在这里使用的正确的东西。我应该使用某种枚举
吗? (当我将用户定义的函数添加到LISP时,我将稍后动态添加内容,因此我认为不是)。
I am working on Make a Lisp to learn Rust.
As part of the evaluator step I need to create an associative structure mapping strings (or whatever) to functions. In languages I'm more familiar with (Ruby, Clojure) I would simply define anonymous functions in a hashmap, eg
{ :+ (fn [a b] (+ a b))
:- (fn [a b] (- a b)) } ; etc
In Rust, this isn't possible because of the type error expected closure, found a different closure
.
let repl_env = HashMap::new();
repl_env.insert("+", |a, b| a + b);
repl_env.insert("-", |a, b| a - b); // expected closure, found a different closure
I guess what's happening here is
- the compiler is inferring the types in the
HashMap
as <String, WhateverTypeTheFirstClosureIs>
- the second closure doesn't have the same time because as the compiler error goes on to say,
no two closures, even if identical, have the same type
?
I can add types to the arguments and thus make the return type inferrable, but this doesn't help — I assume I need to somehow annotate the types of the closures themselves? I can't find a way to do this in the docs. The error suggests Boxing, but wrapping the closures in Box::new
doesn't help. (I've never Box
ed before).
I'm also wondering whether Rust closures in a HashMap
are the right thing to use here. Should I maybe use an enum
of some sort? (I'll want to dynamically add things later when I'm adding user-defined functions to my lisp, so I assume not).
发布评论
评论(1)
您实际上不需要关闭,因为您没有关闭任何内容,所以您只是在使用匿名静态功能。
这意味着您只能施放匿名功能来函数指针,例如,
如果您确实需要闭合,则可以使用某种动态调度(因为每个闭合都是完全不同的类型),通常是某些东西沿着“框的线路”的行,例如
后一个版本是错误消息所说的:拳击闭合[和](在这种情况下)使其成为特征对象。
使用枚举可能是一个好主意,因为最终您将拥有具有不同签名的功能:在这里您仅限于
(i32,i32)的签名 - &gt; i32
。尽管您可以将通用签名更新为EG(values) - &gt;值< / code>并在内部执行派遣 / typechecking / ...
如果要保持“本机”和“用户”功能之间的区别,则可以始终拥有一个存储在枚举中评估用户功能的结果的变体。无论如何,您都需要一种方法来区分它们,因为最终,评估者不会以相同的方式将它们称为。
尽管我认为通常的方法是使所有可见的功能均匀地“用户领域”,并以内置的方式实施这些功能(可能是语法上的区分,或特殊形式,甚至由编译器匹配,并且可能不会或可能不会或可能不会用户可以访问)。
我不知道Lisps通常是如何以根本来完成的,但是例如,您可以让口译员评估术语
,并检查
+
是已知/标准函数, a 和B
是已知的兼容类型,然后在内部进行操作而无需实际调用任何内容。有趣的一点是,您可以提供正确定义的
+
函数,因为它应该自行重新浏览,但它可以自行重现,但是内部节点可以由解释器进行模式匹配,因此它可以使用作为高阶功能的钩子。
例如,这就是其中有多少个小词来处理此操作。非文字发送将降落到蹦床方法中,然后看不见地将其反弹到内置的操作中。
You don't actually need closures here since you're not closing over anything, you're just using anonymous static functions.
That means you can just cast your anonymous functions to function pointers and it'll work fine e.g.
If you did need closures, then you'd have to use some sort of dynamic dispatch (as each closure is an entirely different type), generally something along the lines of `Box e.g.
That latter version is what the error message talks about: boxing the closure [and] (in this case) making it a trait object.
Using an enum might be a good idea as eventually you'll have functions with different signatures: here you're limited to the signature of
(i32, i32) -> i32
. Though you can alternatively update the universal signature to e.g.(Values) -> Value
and internally perform dispatch / typechecking / ...If you want to keep the distinction between "native" and "user" functions, you can always have a variant which stores the result of evaluating a user function in your enum. You'll need a way to distinguish them anyway since at the end of the day the evaluator won't call them the same way.
Though I would think the usual method is to make all visible functions uniformly "userland", and implement those in terms of builtins (which may be distinguished syntactically, or special forms, or even pattern-matched by the compiler, and may or may not be accessible to userland).
I don't know how Lisps usually do it at the root, but e.g. you could have the interpreter evaluate the terms of
and check that
+
is the known / standard function anda
andb
are known compatible types, then do the operation internally without actually calling anything.The fun bit is that you can provide a properly defined
+
function aswhich looks nonsensical at first glance since it should recurse on itself, but the inner node can be pattern-matched by the interpreter and so this serves as a hook for higher-order functions.
That is how many Smalltalks handled this, for instance. Non-literal sends would land into the trampoline method, which would then bounce into the built-in for the operation, invisibly.