Почему опечатка в объявлении функции Haskell приводит к тому, что GHCi выдает ошибки вокруг ранее скомпилированного кода?

Вот такой странный вопрос. Изучение Haskell через LearnYouaHaskell, отличная книга, кстати, и я собираюсь реализовать различные примеры.

Компилируется в GHCi

cylinder :: (RealFloat a) => a -> a -> a
cylinder r h =
    let sideArea = 2 * pi * r * h
    topArea = pi * r ^2
    in  sideArea + 2 * topArea

Компилируется в GHCi

zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]  
zipWith' _ [] _ = []  
zipWith' _ _ [] = []  
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys 

Если я намеренно сделаю опечатку и объявлю вторую функцию таковой

zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]  
zipWith' _ [] _ = []  
zipWith' _ _ [] = []  
zipWith' f (x:xs) (y:ys) = f xs y : zipWith' f xs ys 

Затем первая и вторая функции выдают ошибки во время компиляции — по крайней мере, я так думаю.

Заранее извините за дамп кода.

Он выдает это ранее невидимое сообщение об ошибке о функции цилиндра, которую я не изменил

Prelude> :l functions2.hs
[1 of 1] Compiling Main             ( functions2.hs, interpreted )

functions2.hs:4:26:
    Could not deduce (Integral b0) arising from a use of ‘^’
    from the context (RealFloat a)
      bound by the type signature for
                 cylinder :: RealFloat a => a -> a -> a
      at functions2.hs:1:13-40
    The type variable ‘b0’ is ambiguous
    Note: there are several potential instances:
      instance Integral Int -- Defined in ‘GHC.Real’
      instance Integral Integer -- Defined in ‘GHC.Real’
      instance Integral GHC.Types.Word -- Defined in ‘GHC.Real’
    In the second argument of ‘(*)’, namely ‘r ^ 2’
    In the expression: pi * r ^ 2
    In an equation for ‘topArea’: topArea = pi * r ^ 2

functions2.hs:4:27:
    Could not deduce (Num b0) arising from the literal ‘2’
    from the context (RealFloat a)
      bound by the type signature for
                 cylinder :: RealFloat a => a -> a -> a
      at functions2.hs:1:13-40
    The type variable ‘b0’ is ambiguous
    Note: there are several potential instances:
      instance Num Double -- Defined in ‘GHC.Float’
      instance Num Float -- Defined in ‘GHC.Float’
      instance Integral a => Num (GHC.Real.Ratio a)
        -- Defined in ‘GHC.Real’
      ...plus three others
    In the second argument of ‘(^)’, namely ‘2’
    In the second argument of ‘(*)’, namely ‘r ^ 2’
    In the expression: pi * r ^ 2

А также это гораздо более разумное сообщение об ошибке во второй функции

functions2.hs:12:30:
    Couldn't match expected type ‘a’ with actual type ‘[a]’
      ‘a’ is a rigid type variable bound by
          the type signature for
            zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
          at functions2.hs:9:13
    Relevant bindings include
      xs :: [a] (bound at functions2.hs:12:15)
      x :: a (bound at functions2.hs:12:13)
      f :: a -> b -> c (bound at functions2.hs:12:10)
      zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
        (bound at functions2.hs:10:1)
    In the first argument of ‘f’, namely ‘xs’
    In the first argument of ‘(:)’, namely ‘f xs y’
Failed, modules loaded: none. 

Почему? Это распространенная ошибка? Я что-то сломал в первом, испортив второе? Пожалуйста, порекомендуйте.


person Joe Susnick    schedule 04.07.2015    source источник
comment
Я видел подобные проблемы. Ошибка типа в одной функции заставляет ghc путаться в другой функции. Я думаю, это потому, что он перестает применять значения по умолчанию после ошибки типа. Эта проблема только усугубилась в более новых версиях ghc и очень раздражает, поскольку реальная ошибка часто не является первой зарегистрированной ошибкой. Это действительно ошибка ghc.   -  person augustss    schedule 04.07.2015
comment
Правильно, это ошибка GHC, и это довольно раздражает. Однако обратите внимание, что ^, в частности, является немного проблемным ребенком: его сигнатура полностью разделяет экспоненту и базисные/результатные типы, делая полиморфный аргумент, такой как числовые литералы, неоднозначным. Поэтому я взял за привычку локально сужать (^) :: Num a => a -> Int -> a. Сделав это, вы на самом деле можете продвинуться довольно далеко, вообще не чувствуя необходимости в значениях по умолчанию, потому что большинство функций жестко связывают типы аргументов и результатов, что позволяет однозначно вывести все локальные типы из сигнатуры верхнего уровня.   -  person leftaroundabout    schedule 04.07.2015
comment
Я обнаружил, что GHC 7.10 лучше справляется с этим, чем GHC 7.8. Какую версию вы используете?   -  person chi    schedule 04.07.2015
comment
Хорошо, я только что проверил с ghc-7.10.1, и он просто показывает одну ошибку (более разумную вторую). Так что, думаю, сейчас все лучше, чем было раньше.   -  person Sibi    schedule 05.07.2015
comment
Это была заявка на ошибку GHC № 9033, исправленная в апреле 2014 г.   -  person Ørjan Johansen    schedule 05.07.2015
comment
@ØrjanJohansen Это можно было бы расширить в коротком ответе.   -  person chi    schedule 05.07.2015
comment
@chi Хорошо, я попытался собрать вместе основные упомянутые моменты.   -  person Ørjan Johansen    schedule 06.07.2015


Ответы (1)


Это была заявка на ошибку GHC № 9033, о которой сообщалось в апреле 2014 года и которая была быстро устранена.

По сути, всякий раз, когда файл содержал почти любую ошибку типа, GHC пропускал этап установки класса типа по умолчанию, что могло привести к тому, что другие части файла выдавали ложные неоднозначные ошибки типа.

Как отмечает @leftaroundabout, оператор ^ является частым триггером этого, поскольку его второй тип аргумента не связан с другими типами и поэтому очень часто требует установки по умолчанию.

Версия GHC, указанная в заявке, — 7.8.2, а версия 7.8.3 была выпущена в июле 2104 года, поэтому я предполагаю, что версии 7.8.3 и более поздние имеют исправление.

person Ørjan Johansen    schedule 05.07.2015
comment
Спасибо, это выглядело как ошибка, но как относительно новый программист я никогда не хотел быть мальчиком, который кричал дефект. - person Joe Susnick; 07.07.2015