在D中使用动态类型,这是一种静态类型语言

时间:2011-08-18 05:08:56

标签: d dynamic-typing

当我遇到一个有趣的问题时,我正在为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尝试使用编译时反射来弄清楚如何对待result

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

问题是,它返回 true !为什么?因为我必须假设它所需要的所有成员都存在,除非我能在运行时证明这一点 - 这为时已晚。因此,该程序会尝试在front上调用popFrontemptyresult,从而导致程序崩溃。

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

5 个答案:

答案 0 :(得分:2)

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

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

  1. 为频繁使用的函数提供自己的动态类型实现。您遇到的整个问题是由于您正在尝试使用通用函数来假设使用动态类型进行静态类型。

  2. 显式地使一系列char变为动态,并自己关心转换为基础数据的字符串。 (如果isInputRange问​​题不存在,你必须有一个自定义的toString方法,否则它的结果将再次是动态类型)。这可能会使writeln(d);工作

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

  4. 例如:

    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")));
    

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

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

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

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

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

答案 4 :(得分:0)

你有没有看过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