【问题标题】:Partial application of data constructor数据构造函数的部分应用
【发布时间】:2016-08-31 01:57:23
【问题描述】:

我不明白为什么以下练习在 Haskell Programming from First Principles 中“有效”:

type Subject = String
type Verb = String
type Object = String

data Sentence =
    Sentence Subject Verb Object
    deriving (Eq, Show)

s1 = Sentence "dogs" "drool"
s2 = Sentence "Julie" "loves" "dogs"

将其加载到 ghci 中表明它的类型检查很好,但为什么 s1 的定义甚至有意义?我对 Haskell 还是很陌生,所以起初我认为这是因为在 s1 Haskell 中隐含地让 Object 字符串为空。但是后来……

*Main> s1

<interactive>:13:1:
    No instance for (Show (Object -> Sentence))
      arising from a use of `print'
    Possible fix:
      add an instance declaration for (Show (Object -> Sentence))
    In a stmt of an interactive GHCi command: print it

我仍在学习如何正确解释这些错误消息,所以请多多包涵。但是有人能解释一下No instance for (Show (Object -&gt; Sentence)) 是什么意思吗?更具体地说,在s1 中省略Object 字符串如何导致(Object -&gt; Sentence) 事情?

我确信这很简单,但我不认为这本书已经让我能够理解这一点......

【问题讨论】:

  • 查找函数柯里化并记住像 Sentence 这样的数据构造函数的处理方式与常规函数非常相似。

标签: haskell currying partial-application


【解决方案1】:

但为什么s1 的定义甚至有意义?

正如@Alec 提到的,它被称为柯里化。查看发生了什么的一种方法是让 GHCI 告诉您 s1 的类型是什么:

ghci> :t s1
s1 :: Object -> Sentence

所以s1 是一个将Object 转换为Sentence 的函数。另一种思考方式是从定义开始:

s1 = Sentence "dogs" "drool"

并使用等式推理将两边应用于值x

s1 x = Sentence "dogs" "drool" x

因此,当您调用s1 x 时,它与调用Sentence 相同,前两个函数参数硬编码为"dogs""drool",而x 成为Sentence 函数的第三个参数.

有人能解释一下“没有(Show (Object -&gt; Sentence)) 的实例”是什么意思吗?

当你在 GHCI 中评估某些东西时,它基本上与要求 Haskell print 它相同。也就是说,

ghci> 3+4

实际上等同于:

ghci> print (3+4)

(此规则不适用于像 getLine 甚至 print 本身这样的 IO-action。在这些情况下,Haskell 只运行 IO-action。)

为了print,必须有一个 Show 类型的实例。 但正如我们在上面看到的,s1 是一个 Object -&gt; Sentence 类型的函数,并且函数没有预定义的 Show 实例。

请注意,Sentence 值有一个 Show 实例,因为您要求 GHC 用deriving (Eq, Show) 派生一个。因此,当您在 GHCI 提示符下键入时:

ghci> Sentence "Julie" "loves" "dogs"

你回来了:

Sentence "Julie" "loves" "dogs"

因为你真的要求 GHCI 运行 print (Sentence "Julie" "loves" "dogs")

注意print本身被定义为(link)

print x = putStrLn (show x)

而对show 的调用是一个值需要为它定义一个Show 实例才能打印它的原因。

【讨论】:

  • 也可以使用:t Sentence查看构造函数有Subject -&gt; Verb -&gt; Object -&gt; Sentence类型; :t Sentence "dogs"Object -&gt; Verb -&gt; Sentence 等类型。
【解决方案2】:
No instance for (Show (Object -> Sentence))
  arising from a use of `print'
Possible fix:
  add an instance declaration for (Show (Object -> Sentence))
In a stmt of an interactive GHCi command: print it

补充@ErikR 的回答:您可能想知道为什么GHC 没有内置的函数显示支持,即与整数和字符串不同,函数没有Show 类型类的实例(这些术语本书后面会深入解释,所以如果你不理解什么是类型类和实例也不要担心),除非你自己明确定义。作为一个正在学习 Haskell 并具有面向对象背景的人,我发现将类型类视为类似于 Java 的接口更容易获得对类型类的直觉。

那么,为什么没有 Show 函数? Haskell wiki 提供了两个答案:

1.实际上,GHC 不跟踪变量名,即编译器的以下内容相同:

addOne num = num + 1
f x = x + 1
f y = y + 1  

此外,还可以优化功能,例如以下可能有等价的表示

f x = x - x + x
f x = x

2.理论上,一个函数是由它的图定义的,即(输入,输出)对的集合。例如对于

f x = x + x

这些对是 (1,2)、(2,4) 等。 因此,具有相同图的函数在 GHC 方面是相同的,例如

f x = x + x
g y = 2 * y

但您会认为show fshow g 会有所不同,尤其是当您使用重要的变量名而不是 x 和 y 时。

也就是说,您可以使用仅显示函数类型的编译指示(GHC 编译器的扩展,包含一些超出 Haskell 语言标准的功能),如 this answer 中所述:

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Typeable

instance (Typeable a, Typeable b) => Show (a->b) where
  show _ = show $ typeOf (undefined :: a -> b)

这会得到你

> s1
[Char] -> Sentence

因为ObjectString 的别名(使用type 关键字,String 本身就是[Char] 的别名)。 如果你想明确地看到主体和客体的区别,你可以将Object转换为带有数据类型构造函数MkObject的类型:

newtype Object = MkObject String deriving (Eq, Show)

s1 = Sentence "dogs" "drool"
s2 = Sentence "Julie" "loves" (MkObject "dogs")

然后瞧

> s1
Object -> Sentence

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多