考虑这个模糊类型推断的简单示例:
#! /usr/bin/env stack
{- stack runghc -}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
main :: IO ()
main = do
-- This will fail to compile without additional type info
-- let w = read "22"
-- print w
-- My go-to for this is type signatures in the expressions
let x = read "33" :: Integer
print x
-- Another possibility is ScopedtypeVariables
let y :: Integer = read "44"
print y
-- How does TypeApplications differ from the code above? When should this be chosen instead?
let z = read @Integer "55"
print z
Run Code Online (Sandbox Code Playgroud)
我的问题是,在这种情况下,使用 有优势TypeApplications吗?
在几乎所有情况下,这只是一种审美选择。我提出以下补充意见供您考虑:
在所有情况下,如果一个事物使用一些类型签名集合进行类型检查,则有一个相应的类型应用程序集合也会导致该术语进行类型检查(并且具有相同的实例字典选择等)。
在可以使用签名或应用程序的情况下,GHC 生成的代码将是相同的。
一些不明确的类型无法通过签名解决,必须使用类型应用程序。例如,foo :: (Monoid a, Monoid b) => b不能给出确定 的类型签名a。(这颗子弹激发了这个答案第一句中的“几乎”。没有其他子弹激发“几乎”。)
类型应用程序在语法上通常比类型签名更轻。例如,当类型很长时,或者多次提到一个类型变量。一些比较:
showsPrec :: Int -> Bool -> String -> String
showsPrec @Bool
sortOn :: Ord b => (Int -> b) -> [Int] -> [Int]
sortOn @Int
Run Code Online (Sandbox Code Playgroud)
有时可以将类型签名混洗到不同的子项,这样您只需要给出一个简短的签名,重复很少。但话又说回来......有时不是。
有时,签名或应用程序旨在向读者传达一些信息或鼓励以某种方式思考一段代码(即并非严格用于编译器消耗)。如果该信息的一部分涉及在特定代码位置附加注释,则您的选择可能会受到一定的限制。