2016-08-31 17 views
4

Я не понимаю, почему следующее упражнение «работает» в Haskell программирования из первых принципов:Частичное применение конструктора данных

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 показывает, что typechecks просто отлично, но почему что определение 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) вещь?

Я уверен, что это глупо легко, но я не думаю, что книга оборудовал мне понять это по этому вопросу ...

+4

Посмотрите функцию Карринг и иметь в виду что конструкторы данных, такие как «Sentence», рассматриваются как обычные функции. – Alec

ответ

8

, но почему это, что определение 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 -> Sentence))» означает?

Когда вы оцениваете что-то в GHCI, это в основном то же самое, что запрашивать у Haskell print. То есть,

ghci> 3+4 

фактически то же самое, как:.

ghci> print (3+4) 

(Это правило не относится к IO-действий, как getLine или даже самого print В тех случаях, Haskell просто запускает io- действие.)

Для того, чтобы print, должен быть экземпляр Show для данного типа. Но, как мы видели выше, s1 является функцией типа Object -> Sentence, и нет предопределенных экземпляров Show для функций.

Обратите внимание, что есть экземпляр Show для значений Sentence, потому что вы попросили 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, определенный для него, чтобы распечатать его.

+0

Вы также можете использовать ': t Sentence', чтобы увидеть, что конструктор имеет тип' Subject -> Verb -> Object -> Sentence'; ': t Sentence" dogs "' имеет тип 'Object -> Verb -> Sentence' и т. д. – chepner

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 insance для функций? Haskell wiki дает два ответа:

1.Practically, GHC не отслеживает имен переменных, т.е. следующие одинаковы для компилятора:

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

Кроме того, функции могут быть оптимизированы, например, следующее может иметь эквивалентные представления

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

2.Theoretically, функция определяется его графа, то есть набор (вход, выход) пар. например для

f x = x + x 

пары являются (1,2), (2,4) и т.д. Таким образом, функции с одной и той же графа идентичны GHC-накрест, например

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

, но вы бы ожидайте, что show f и show 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 

поскольку Object является псевдонимом String (используя ключевое слово 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