部分应用数据构造函数

时间:2016-08-31 01:57:23

标签: haskell currying partial-application

我不明白为什么以下练习在 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 -> Sentence))的含义吗?更具体地说,如何遗漏Object中的s1字符串会导致(Object -> Sentence)这个问题?

我确信这很简单,但我认为这本书并没有让我理解这一点......

2 个答案:

答案 0 :(得分:8)

  

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

正如@Alec所说,它被称为currying。查看正在发生的事情的一种方法是让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 -> Sentence))没有实例”的含义吗?

当您评估GHCI中的某些内容时,它与向Haskell询问print它基本相同。也就是说,

ghci> 3+4

实际上与:

相同
ghci> print (3+4)

(此规则不适用于getLine甚至print等IO操作。在这些情况下,Haskell只运行IO操作。)

为了print,必须有一个类型的Show实例。 但正如我们上面所见,s1Object -> 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实例才能打印它。

答案 1 :(得分:0)

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的接口,更容易获得对类型类的直觉。

那么,为什么函数没有ShowHaskell 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。

也就是说,您可以使用pragma(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")

和voilà

> s1
Object -> Sentence