Как напечатать тип полиморфной функции (или значение) в ghci с примененными правилами по умолчанию?

Когда я вхожу :t Команда в GHCi я вижу полиморфный тип:

ghci> :t 42
42 :: Num t => t
ghci> :t div
div :: Integral a => a -> a -> a

Но после того, как я на самом деле оцениваю такие функции, я вижу результат правил по умолчанию типа. Есть ли какая-то команда или возможность наблюдать в ghci, как тип будет изменен после правил по умолчанию для типов, применяемых в соответствии с отчетом Haskell и / или реализацией ghc?

4 ответа

Решение

Начиная с GHC 8.4.1 можно использовать :type +d (или же :t +d для краткости) опция для печати типа выражения, по умолчанию переменные типа, если это возможно.

ghci> :t 42
42 :: Num p => p
ghci> :t +d 42
42 :: Integer
ghci> :t div
div :: Integral a => a -> a -> a
ghci> :t +d div
div :: Integer -> Integer -> Integer

Вы можете сделать это, включив ограничение мономорфизма и затем связав его с новым именем:

Prelude> :set -XMonomorphismRestriction 
Prelude> let n = 42
Prelude> :t n
n :: Integer
Prelude> let p = (^)
Prelude> :t p
p :: Integer -> Integer -> Integer
Prelude> let e = (**)
Prelude> :t e
e :: Double -> Double -> Double
Prelude> let d = div
Prelude> :t d
d :: Integer -> Integer -> Integer

Если вам не нужно всегда определять новую переменную, вы можете обойти это, используя

Prelude> :def monotype (\e -> return $ ":set -XMonomorphismRestriction\nlet defaulted = "++e++"\n:t defaulted")

(вы можете поместить это в свой .ghci файл, чтобы всегда иметь команду в наличии), а затем

Prelude> :monotype (^)
defaulted :: Integer -> Integer -> Integer

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

Не идеальное решение, но это может быть первым шагом.

> import Data.Typeable
> let withType x = (x, typeOf x)
> withType []
([],[()])
> withType 56
(56,Integer)

Обратите внимание, что, так как тип a превращается в (a,TypeRep)GHCi не будет использовать всю свою магию по умолчанию. Тем не менее, некоторые из них могут быть показаны.

GHCi-х :set +t опция также интересна, но, по-видимому, печатает полиморфный тип до дефолта GHCi.

В действительности ghci не может дать вам поведение по умолчанию, подобное GHC, именно поэтому ограничение мономорфизма (сейчас) отключено по умолчанию в ghci.

Как показано в ответе @Shersh, теперь вы можете спросить GHCi, для чего оно будет задано по умолчанию для данного выражения.

Prelude> :t 2^100 `div` 2
2^100 `div` 2 :: Integral a => a

Prelude> :t +d 2^100 `div` 2
2^100 `div` 2 :: Integer

Prelude> 2^100 `div` 2
633825300114114700748351602688

Но это не обязательно отражает то, что GHC будет делать с тем же выражением, поскольку GHC компилирует выражение в контексте полного модуля. GHC может учитывать все варианты использования выражения, поскольку GHCi имеет доступ только к составным частям выражения. GHC по умолчанию только вещи, которые остаются неоднозначными после рассмотрения всего этого дополнительного контекста, поэтому не гарантируется использование типа для выражения, которое вы увидите с :t +d в GHCi.

Например:

n = 2^100 `div` 2

xs = "ABCD"

main = print $ xs !! n

Это печатает 'A'что, очевидно, не является 633825300114114700748351602688-м элементом этого (4-элементного) списка. Потому что выражение 2^100 `div` 2 используется в качестве аргумента !! (нелокально, через n обязательный), и (!!) :: [a] -> Int -> a тип выбран как Int, а не тип, который будет выбран по умолчанию без этого контекста (Integer). Оценивая это выражение как Int имеет другой результат (0, из-за переполнения).

Это означает, что когда вы царапаете голову над ошибкой типа в GHC и используете :t +d в GHCi, чтобы попытаться получить больше информации, вы должны знать, что вы все еще не видите того типа, который фактически использует GHC. Гарантируется, что полиморфный тип будет совместим с тем, который использует GHC, но его дефолт в любом другом контексте может привести к другому, который не совместим.

Другие вопросы по тегам