我不明白为什么以下练习在 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)
这个问题?
我确信这很简单,但我认为这本书并没有让我理解这一点......
答案 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实例。
但正如我们上面所见,s1
是Object -> 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的接口,更容易获得对类型类的直觉。
那么,为什么函数没有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 f
和show 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
由于Object
是String
的别名(使用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