Как напечатать тип полиморфной функции (или значение) в 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, но его дефолт в любом другом контексте может привести к другому, который не совместим.