【问题标题】:determining function behavior from the type of the function从函数的类型确定函数的行为
【发布时间】:2011-12-15 18:23:35
【问题描述】:

Haskell 新手非常抱歉,如果这是非常基本的

这个例子取自《Real World Haskell》——

ghci> :type fst  
fst :: (a, b) -> a

它们显示fst 函数的类型,然后在它后面加上这一段...

fst 的结果类型是 a。我们已经提到参数多态性使实际类型无法访问:fst 没有足够的信息来构造值a 类型的,也不能将 a 变成 b。所以唯一可能的有效行为(省略无限循环或崩溃)它可以有的是返回对的第一个元素。”

我觉得我错过了该段落的基本点,也许还有一些关于 Haskell 的重要内容。为什么fst 函数不能返回类型b?为什么它不能将元组作为参数,而只是返回一个Int(或任何其他不是a的类型)?我不明白为什么它必须返回类型 a

谢谢

【问题讨论】:

    标签: haskell


    【解决方案1】:

    如果它做了任何这些事情,它的类型就会改变。引用的意思是,鉴于我们知道fst(a, b) -> a 类型,我们可以对它进行这些推断。如果它有不同的类型,我们将无法这样做。

    例如,看到那个

    snd :: (a, b) -> a
    snd (x, y) = y
    

    不进行类型检查,因此我们知道 (a, b) -> a 类型的值不能像 snd 那样表现。

    参数化基本上是这样一个事实,即某种类型的多态函数必须通过构造遵守某些规则——即,没有不遵守这些规则的该类型的良类型表达式。所以,要想用它来证明fst,我们必须先知道fst的类型。

    特别注意那里的多态这个词:我们不能对非多态类型做同样的推论。例如,

    myFst :: (Int, String) -> Int
    myFst (a, b) = a
    

    类型检查,但也是如此

    myFst :: (Int, String) -> Int
    myFst (a, b) = 42
    

    甚至

    myFst :: (Int, String) -> Int
    myFst (a, b) = length b
    

    参数化主要依赖于这样一个事实,即多态函数不能“查看”它被调用的类型。所以fst 知道的唯一类型 a 的值就是它给出的值:元组的第一个元素。

    【讨论】:

      【解决方案2】:

      关键是一旦你有了那个类型,实现选项就会受到很大的限制。如果您返回Int,那么您的类型将是(a,b) -> Int。由于a 可以是任何东西,我们不能在不使用undefined 的情况下在实现中凭空创建一个,因此必须返回调用者给我们的那个。

      【讨论】:

        【解决方案3】:

        您应该阅读Theorems for Free 文章。

        【讨论】:

        • 在完成 RWH 第 2 章之前?
        • @ehird 他想要一个深入的解释,所以在这里。其他人已经给出了基本的挥手解释
        • 很公平。不过,我尽量避免在我的回答中挥手,但我当然不是完美的:)
        【解决方案4】:

        让我们尝试在 Real World Haskell 已经给出的基础上添加更多的挥手。让我们试着说服自己,假设我们有一个函数 fst 类型为 (a,b) -> a,它唯一的总函数是以下一个:

        fst (x,y) = x
        

        首先,我们不能返回除a 类型之外的任何值,前提是fst 的类型为(a,b) -> a,所以我们不能有fst (x,y) = yfst (x,y) = 1,因为它没有正确的类型。

        现在,正如 RWH 所说,如果我给 fst 一个 (Int,Int)fst 不知道这些是 Ints,此外,ab 不需要属于任何类型类,所以fst 没有与 ab 关联的可用值或函数。

        所以fst 只知道a 值和我给它的b 值,我不能将b 变成a(它不能生成b -> a 函数) 所以它必须返回给定的a 值。

        这实际上不仅仅是神奇的挥手,人们实际上可以推断出给定多态类型的可能表达式。实际上有一个名为djinn 的程序就是这样做的。

        【讨论】:

          【解决方案5】:

          这里的重点是ab 都是类型变量(可能相同,但不需要)。无论如何,因为对于两个元素的给定元组,fst 总是返回第一个元素,返回的类型必须始终与第一个元素的类型相同。

          【讨论】:

            【解决方案6】:

            您可能缺少的基本内容是:

            • 在大多数编程语言中,如果你说“此函数返回任何类型”,则意味着函数可以决定它实际返回的值的类型。

              李>
            • 在 Haskell 中,如果你说“这个函数返回任何类型”,这意味着 调用者 可以决定应该是什么类型。 (!)

            所以如果我写foo :: Int -> x,它不能只返回String,因为我可能不会要求它提供String。我可能会要求Customer,或ThreadId,或任何东西

            显然,foo 无法知道如何创建每种可能类型的值,即使是尚不存在的类型。总之,不可能写foo。你尝试的一切都会给你类型错误,并且不会编译。

            (警告:有 a 方法可以做到这一点。foo 可以永远循环,或者抛出异常。但它不能返回有效值。)

            函数无法创建任何可能类型的值。但是完全有可能一个函数移动数据而不关心它是什么类型。因此,如果您看到一个函数接受任何类型的数据,它唯一能做的就是移动它。

            或者,如果类型必须属于特定类,则函数可以使用该类的方法。 (可以。它不是必须的,但它可以如果它愿意。)

            从根本上说,这就是为什么您实际上可以通过查看函数的类型签名来了解函数的作用。类型签名告诉您函数“知道”它将被提供的数据,以及它可能执行的可能操作。这就是为什么通过类型签名搜索 Haskell 函数非常有用的原因。

            你听说过“在 Haskell 中,如果它可以编译,它通常可以正常工作”这句话吗?嗯,这就是为什么。 ;-)

            【讨论】:

            • 这很好地解释了参数化背后的思想!现在我们只需要对“免费定理”中的 Wadler 证明进行类似可理解的翻译。
            • @dfeuer 如果你找到了,请告诉我!我一直看到这句话,但不知道它是什么意思......
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-04-14
            • 1970-01-01
            • 1970-01-01
            • 2016-10-18
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多