GHC 8 - Правила параметризации ограниченного типа с переименованными функциями - PullRequest
0 голосов
/ 26 октября 2018

Я озадачен, казалось бы, ошибочным поведением GHC, происходящим с довольно простыми программами на Haskell.

Рассмотрим следующий код:

import System.IO
output :: [String] -> IO()
output stringList = sequence_ $ map putStrLn stringList

main :: IO ()

s = show

main = output [
    s 42,
    s True
  ]

В GHC 8.4.3 выдает следующий вывод:

$ runghc parameterize.hs
.hs:9:7: error:
    • No instance for (Num Bool) arising from the literal ‘42’
    • In the first argument of ‘s’, namely ‘42’
      In the expression: s 42
      In the first argument of ‘output’, namely ‘[s 42, s True]’
  |
9 |     s 42,
  |

GHC 8.0.2 выдает ту же ошибку.

Это также происходит со следующими вариантами:

  • Использование where

    main :: IO ()
    main = output [
        s 42,
        s True
      ]
      where s = show
    
  • Использование let ... in

    main :: IO ()
    main = let s = show in output [
        s 42,
        s True
      ]
    

Но в трех случаях замена s = show на s x = show x решает проблему:

main :: IO ()

s x = show x

main = output [
    s 42,
    s True
  ]

$ runghc u.hs
42
True

Это не относится только к show.Вот тот, который не работает с функцией succ, работающей с элементами Enum:

main :: IO ()
main = let s = succ in putStrLn $ showList [
    show $ s 41,
    show $ s False
  ] ""

Замена s = succ на s x = succ x все еще исправляет проблему.

У меня нетудалось найти объяснение этому неожиданному поведению.Это ошибка?Если это не так, пожалуйста, объясните, что происходит.

1 Ответ

0 голосов
/ 26 октября 2018

Вас укусило ограничение мономорфизма , и это не ошибка.Первые два абзаца этой страницы:

«Ограничение мономорфизма» является нелогичным в выводе типа Хаскеля.Если вы забыли предоставить сигнатуру типа, иногда это правило будет заполнять переменные свободного типа определенными типами, используя правила «тип по умолчанию».Результирующая сигнатура типа всегда менее полиморфна, чем вы ожидаете, поэтому часто это приводит к тому, что компилятор выдает ошибки типа в вас, когда вы ожидаете, что он выведет совершенно нормальный тип для полиморфного выражения.

AПростой пример это плюс = (+).Без явной подписи для плюса компилятор не будет выводить тип (+) :: (Num a) => a -> a -> a для plus, но применит правила по умолчанию для указания plus :: Integer ->Целое число -> Целое число.При применении к плюс 3.5 2.7, GHCi будет выдавать ошибку, которая выглядит несколько вводящей в заблуждение, нет экземпляра для (Fractional Integer), возникающего из литерала '3.5'.

Просто замените (+) здесь наshow и это ваш пример.

С этим кодом, например:

import System.IO
output :: [String] -> IO()
output stringList = sequence_ $ map putStrLn stringList

main :: IO ()

s = show

s2 x = show x

main = do
  output [
    s False,
    s True
   ]
  output [
    s2 42,
    s2 True
    ]

Вы получите этот результат в ghci после загрузки файла, содержащего это:

Prelude> :l test.hs
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, one module loaded.
*Main> :t s
s :: Bool -> String
*Main> :t s2
s2 :: Show a => a -> String
*Main>

Так что в вашем первом примере тип, выведенный для s, не позволяет вам использовать его с числом.

Точные правила, когда применяется ограничение мономорфизма, определены здесь Раздел 4.5.5 из Haskell Report 2010 , но довольно технически.

Также обратите внимание, что ограничение мономорфизма деактивируется в приглашении ghci и применяется только к скомпилированным модулям.

...