手动确定表达式的类型

时间:2018-03-23 07:44:34

标签: haskell types

我如何手动确定表达式的类型,而不是在GHCi中使用class StringSignal : public QObject { Q_OBJECT public: Q_SIGNAL void signal(const QString &); }; void MainWindow::initializeInterests(const QStringList &personsIn) { auto in = personsIn; std::sort(in.begin(), in.end()); QStringList persons; persons.reserve(in.size()); for (int i = 0; i < persons_comboBox_->count(); ++i) { auto const combo = persons_comboBox->itemText(i); if (std::binary_search(in.begin(), in.end(), combo)) persons << combo; } QtConcurrent::run([persons = std::move(persons), w = this](){ StringSignal source; connect(&source, &StringSignal::signal, w, [w](const QString & person){ w->initalizeMBox->setText( QStringLiteral("%1 <div><img src=...> %2: %3.</div>") .arg(w->initalizeMBox->text()) .arg(tr("Interests analyzed for the person")) .arg(person) ); }); for (auto &person : persons) { // persons is const // create a specific object etc. QThread::sleep(1); // let's pretend we work hard here source.signal(person); } }); }

对于基本表达式,我们可以通过查看表达式并提出某种类型来实现。

对于像:type这样的更复杂的表达式,是否存在某种算法方法?

2 个答案:

答案 0 :(得分:5)

知道Hindle-Milner肯定是可能的,但在很多情况下,只需要一点直觉就可以得到正确的结果。

正如你所说,对于基本表达式来说,这很容易。假设您有一个像f :: Integer -> Integer -> Integer这样的简单函数。将此函数应用于Integer(例如f 42之类的内容)时,您可以立即看到其类型为Integer -> Integer。微不足道的。

一旦开始引入类型变量和约束,乐趣就开始了。尽管基本概念仍然相同 - 将a -> b类型的函数应用于类型为a的表达式会产生b - 您必须小心不要混淆所有输入变量,不要忘记约束。

让我们以(.) . (.)为例,逐步完成它。

  • 首先,让我们将其重写为((.) (.)) (.) - 这是(.)(.)的应用,其结果将应用于另一个(.)。让我们只关注第一个应用程序,然后再处理第二个应用程序。
  • 如您所知,(.)的类型为(b -> c) -> (a -> b) -> a -> c,在这种情况下我们将其应用于自身 - 因此,我们必须统一第一个参数的类型(即。{{ 1}})类型为b -> c。这是你必须要小心你的类型变量的部分 - 因此,我将第二个(以及后来甚至第三个)(.)的类型变量重命名为(.) - 这有对实际类型没有影响,它只是让我们以后不会迷路。
  • 现在重命名,让我们统一(b1 -> c1) -> (a1 -> b1) -> a1 -> c1类型为b -> c的类型。 (b1 -> c1) -> (a1 -> b1) -> a1 -> c1b统一,b1 -> c1与其他人c统一。由于对任何类型变量都没有约束,我们不需要关心它们。
  • 现在我们知道了(a1 -> b1) -> a1 -> c1b所代表的实际类型,我们可以将它们替换为c的类型(忽略第一个参数,因为我们刚刚应用了)我们最终会得到这个:(.)
  • 现在我们可以继续将其应用到最后剩余的(a -> b1 -> c1) -> a -> (a1 -> b1) -> a1 -> c1
  • 我们需要统一上一个(.)的类型(我将其类型变量重命名为(.)a2b2,因此它变为{{1 } {}与c2。这又是一件容易的事。 (b2 -> c2) -> (a2 -> b2) -> a2 -> c2变为(a -> b1 -> c1)a变为b2 -> c2,最后b1变为a2 -> b2
  • 再一次,我们可以在统一之前将这些替换为我们的原始类型,忽略代表我们刚刚应用的c1的参数,我们将获得a2 -> c2并完成。< / LI>

忽略不同的名称,您可以看到这与使用(.)获得的结果完全相同。

我希望这会有所帮助。

答案 1 :(得分:2)

这是类型推断的图形视图。 我们想找到

的类型
(.)  .  (.) :: ?

为此我们首先列出每个变量的类型,使用新的类型变量。放下大量括号也有助于避免在接下来的步骤中出现错误。

    (.)     :: (a1 -> a2) -> ((a0 -> a1) -> (a0 -> a2))
(.)         :: (b1 -> b2) -> ((b0 -> b1) -> (b0 -> b2))
        (.) :: (c1 -> c2) -> ((c0 -> c1) -> (c0 -> c2))

然后我们对齐类型,以便参数类型与函数类型匹配。首先,对于一个更简单的示例,如果我们想要找到f x y的类型

f     :: a -> a -> a
  x   :: b
    y :: c

然后我们将对齐类型如下

f     :: a -> (a -> a)
  x   :: b             -- First argument
    y ::       c        -- Second argument
                 -- ^ Result type

因为xf的第一个参数,而y是第二个参数。这告诉我们将a = ba = c等同,结果类型为a

这是原始问题的图表,为拉伸表达式添加了大量空白,以便它们匹配。

    (.)     :: (a1         -> a2                        ) -> (a0         -> a1                        ) -> (a0 -> a2)
(.)         ::  (b1 -> b2) -> ((b0 -> b1) -> (b0 -> b2))
        (.) ::                                                (c1 -> c2) -> ((c0 -> c1) -> (c0 -> c2))
                                                                                                        -- ^ Result

通过查看这些表格的列,我们在类型之间获得以下等式:

a1 = (b1 -> b2)
a2 = ((b0 -> b1) -> (b0 -> b2))
a0 = (c1 -> c2)
a1 = ((c0 -> c1) -> (c0 -> c2))

从第一个和最后一个,我们进一步推导出另外两个方程式

b1 = (c0 -> c1)
b2 = (c0 -> c2)

结果类型为(a0 -> a2),即替换后:

(c1 -> c2) -> (b0 -> c0 -> c1) -> (b0 -> c0 -> c2)