【问题标题】:Using dynamic typing in D, a statically typed language在静态类型语言 D 中使用动态类型
【发布时间】:2011-10-29 11:31:53
【问题描述】:

当我遇到一个有趣的问题时,我正在为 D 实现一个动态类型库。

现在,我已经成功创建了一个名为 dynamic() 的函数,它返回对象的动态版本。

例如:

import std.stdio, std.dynamic.core;

class Foo
{
    string bar(string a) { return a ~ "OMG"; }
    int opUnary(string s)() if (s == "-") { return 0; }
}

void main(string[] argv)
{
    Dynamic d = dynamic(new Foo());
    Dynamic result = d.bar("hi");
    writeln(result);  // Uh-oh
}

我遇到的问题是 writeln 尝试使用 compile-time 反射来弄清楚如何处理 result

它尝试的第一件事是什么? isInputRange!(typeof(result))

问题是,它返回 true!为什么?因为我必须假设它需要的所有成员都存在,除非我可以在运行时证明不存在——这为时已晚。因此程序尝试在result 上调用frontpopFrontempty,导致我的程序崩溃。

我想不出办法来解决这个问题。有人有想法吗?

【问题讨论】:

    标签: d dynamic-typing


    【解决方案1】:

    您是否研究过 std.variant?

    import std.stdio, std.variant;
    
    class Foo {
        string Bar(string a) {
            return a ~ " are Cool!";
        }
    }
    
    void main() {
        Variant foo = new Foo();
        Variant result = foo.peek!Foo.Bar("Variants");
    
        writeln(result); // Variants are Cool!
    }
    

    http://www.d-programming-language.org/phobos/std_variant.html

    【讨论】:

      【解决方案2】:

      您正在尝试使两个根本不同的概念一起工作,即模板和动态类型。模板非常依赖静态类型,isInputRange 通过检查类型具有哪些属性或方法来工作。您的动态类型在编译时被视为具有每个属性或方法,因此它被视为实现 every 静态鸭子类型接口。 因此,要使 Dynamic 在静态类型的环境中工作,您必须在某些地方提供更多的静态信息。

      我可以看到的一些解决方案:

      1. 为大量使用的函数提供您自己的动态类型实现。您遇到的整个问题是由于您尝试使用假定静态类型与动态类型的泛型函数。

      2. 明确地将动态范围设为 char,并自行负责将基础数据转换为字符串。 (如果 isInputRange 问题不存在,则无论如何您都必须有一个自定义的 toString 方法,否则它的结果将再次是 Dynamic 类型)。这可能会使 writeln(d);工作。

      3. 为动态提供包装器,允许您将动态类型传递给各种模板化函数。 (那些只会展示一个静态接口并将所有调用转发给 Dynamic)。

      例如:

      Dynamic d;
      // wrap d to turn it into a compile-time input range (but NOT eg a forward range)
      Dynamic d2=dynamic(map!q{a*2}(dynInputRange(d))); 
      // profit
      

      4 。为 Dynamic 添加成员模板,允许静态禁用某些成员函数名称。

      例如:

      static assert(!isForwardRange!(typeof(d.without!"save")));
      

      【讨论】:

        【解决方案3】:

        正如您所说,对于一般情况,Dynamic 必须在编译时接受任何方法查找。假设您可以阻止 isInputRange 谓词评估为真,现在当您尝试从输入范围创建动态时将生成错误代码。

        我不认为这是可以解决的,至少不能以一般的方式解决。在这种特殊情况下,我能想到的最佳解决方案是 Dynamic 提供它自己的 toString 版本,而 writeln 更喜欢 inputRange 专业化。我相信 writeln 目前不这样做,至少不是针对结构,但它可能应该这样做。

        另一个折衷方案是在 opDispatch 约束中禁止一些方法,例如 popFront,而 Dynamic 将提供 opIndex 或成员对象来访问这些特殊情况。这可能不像听起来那么糟糕,因为特殊情况很少见,使用它们会导致明显的编译器错误。

        我认为为 Dynamic 挽救这种方法解析的最佳方法是修复 writeln 并接受 Dynamic 不适用于所有模板化代码。

        【讨论】:

        • 让 writeln 更喜欢 toString 而不是 isInputRange 的问题在于,每个类都从 Object 继承了一个通用的 toString 方法,它只输出类名。因此,如果 writeln 发生了变化,就必须区别对待结构和类。
        【解决方案4】:

        你能为 isInputRange 提供一个重载吗?像这样的东西(注意我没有看过isInputRange的实现):

        template isInputRange(T : Dynamic) {
            enum isInputRange = false;
        }
        

        如果这是由您的 dynamic.core 提供的,我认为应该在 std lib 之前选择这个重载。

        【讨论】:

        • 对,但问题是这需要提前了解各种预防措施。显然,它不适用于最终使用我的图书馆的人......
        • 不幸的是这个技巧不起作用,std.stdio 无法获得专业化。
        【解决方案5】:

        使用std.variant 有什么问题,它实现了动态类型所需的一切(以及相当多的语法糖)

        【讨论】:

        • std.variant 不支持具有任意字段的类型。
        • OP 希望创建一个对象,其中obj.anything 在编译时有效(即使它在运行时可能无效)。正如我所见,std.variant 中没有任何内容允许这样做。
        猜你喜欢
        • 2016-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-03
        • 1970-01-01
        相关资源
        最近更新 更多