Haskell 作为纯函数式静态强类型编程语言,其类型系统是设计的核心基石,核心设计思想围绕编译期类型安全、无副作用的纯函数抽象、泛型代码的高度复用、最小化显式类型标注展开——通过静态强类型在编译期拦截几乎所有类型相关错误,结合强大的类型推断机制减少冗余的类型代码,以多态类型实现跨类型的代码复用,同时通过灵活的类型扩展特性平衡“类型安全”与“开发效率”,最终让代码兼具严谨性、复用性和简洁性,这也是Haskell能成为函数式编程典范、广泛应用于高可靠系统开发的核心原因。
类型与类型签名
类型是Haskell中描述值的特征与行为边界的核心标识,类型签名则是显式标注变量、函数类型的语法规范,是沟通程序员与编译器的重要桥梁,也是静态类型检查的基础,核心包含三个关键知识点,所有知识点均附可运行代码示例:
:: 含义
:: 是Haskell的类型标注符,核心作用是显式指定“标识符(变量/函数/表达式)对应的类型”,语法格式为标识符 :: 类型。编译器会根据::标注的类型进行严格校验,若代码逻辑与标注类型冲突则直接报编译错误;同时::也可辅助编译器完成复杂场景的类型推断,避免歧义。
-- 1. 变量类型标注
age :: Int -- 整数类型
age = 20
name :: String -- 字符串类型(等价于[Char])
name = "Haskell"
nums :: [Double] -- 浮点数列表类型
nums = [1.2, 3.4, 5.6]
person :: (String, Int) -- 二元元组类型
person = ("Tom", 25)
-- 2. 函数类型标注
add :: Int -> Int -> Int
add x y = x + y
-- 3. 表达式类型标注(解决推断歧义)
num :: Double
num = (1 + 2 * 3) :: Double -- 显式指定表达式结果为Double
函数类型-> 右结合
-> 是Haskell函数类型的核心表示符,用于描述“函数的输入类型与输出类型的映射关系”,且->具有天然的右结合特性(编译器默认按从右到左的顺序嵌套解析),即a -> b -> c等价于a -> (b -> c),这一特性与Haskell的柯里化(Currying) 特性深度绑定——Haskell中所有多参数函数的本质,都是“接收一个参数、返回一个新函数”的单参数函数,新函数再接收下一个参数,直至返回最终结果。
-- 1. 右结合示例1:双参数函数的本质是返回函数的单参数函数
f :: Int -> String -> Bool
f x y = x > length y -- 等价于 f :: Int -> (String -> Bool)
-- 调用方式1:常规调用(隐式柯里化)
f1 = f 5 "abc" -- 结果:True(5 > 3)
-- 调用方式2:分步调用(显式柯里化,体现右结合本质)
f' :: String -> Bool
f' = f 5 -- f接收Int参数5,返回一个"String -> Bool"的新函数
f2 = f' "abc" -- 结果:True
-- 2. 右结合示例2:三参数函数的嵌套解析
g :: a -> b -> c -> (b, c, a)
g x y z = (y, z, x) -- 等价于 g :: a -> (b -> (c -> (b, c, a)))
-- 分步调用:每层接收一个参数,返回新函数
g1 :: b -> c -> (b, c, Int)
g1 = g 10 -- 接收a=Int,返回b->c->(b,c,Int)
g2 :: c -> (String, c, Int)
g2 = g1 "hello" -- 接收b=String,返回c->(String,c,Int)
g3 = g2 3.14 -- 接收c=Double,返回最终结果 ("hello",3.14,10)
多参数函数类型
基于Haskell的柯里化特性和->的右结合规则,不存在专门的多参数函数类型表示,所有多参数函数的类型,均通过->的嵌套(右结合)实现——参数的数量对应->的数量(n个参数对应n个->),最右侧为函数的最终返回类型,左侧依次为各参数的类型。
-- 1. 一元函数(1个参数,1个->)
square :: Int -> Int
square x = x * x
showInt :: Int -> String
showInt = show
-- 2. 二元函数(2个参数,2个->)
plus :: Num a => a -> a -> a
plus x y = x + y
compareStr :: String -> String -> Ordering
compareStr s1 s2 = compare (length s1) (length s2)
-- 3. 三元函数(3个参数,3个->)
addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z
lookupByKey :: Eq k => k -> [(k, v)] -> Maybe v
lookupByKey k [] = Nothing
lookupByKey k ((k',v):xs) | k == k' = Just v
| otherwise = lookupByKey k xs
-- 4. 带类型类约束的多参数函数
printIfEq :: (Eq a, Show a) => a -> a -> IO ()
printIfEq x y = if x == y then print x else print "Not Equal"
类型推断机制
类型推断是Haskell的核心特性之一,指编译器无需显式的类型标注,通过分析表达式的语法结构、运算关系和类型类约束,自动推导出变量/函数/表达式的最通用类型,这一特性让Haskell兼具“静态类型的安全性”和“动态类型的简洁性”,其底层依托经典算法实现,同时存在明确的推断规则和限制,所有知识点均附代码示例:
Hindley-Milner 算法
Hindley-Milner(HM)算法是Haskell类型推断的底层核心算法,也是绝大多数纯函数式语言(OCaml、Scala等)类型推断的基础,该算法于1969年提出,核心优势是能在无显式标注时,推导出表达式的“最一般通用类型(Most General Unifier, MGU)” ,且推导过程高效、无歧义,兼顾通用性和编译效率。
核心原理:通过为表达式中的未知类型分配类型变量(如a、b、c),再根据表达式的运算规则建立类型等式,最终通过合一(Unification) 算法求解类型变量,得到最通用的类型。
-- 无需任何类型标注,HM算法自动推导最通用类型
-- 1. 恒等函数:推导为 a -> a(a为任意类型变量)
id x = x
-- 可适配任意类型,无约束
id1 = id 10 -- Int -> Int
id2 = id "abc" -- String -> String
id3 = id [1,2,3] -- [Int] -> [Int]
-- 2. 列表映射函数:推导为 (a -> b) -> [a] -> [b]
map' f [] = []
map' f (x:xs) = f x : map' f xs
-- 自动适配不同的类型映射
map1 = map' (+1) [1,2,3] -- (Int->Int) -> [Int]->[Int],结果[2,3,4]
map2 = map' show [1,2,3] -- (Int->String) -> [Int]->[String],结果["1","2","3"]
-- 3. 元组交换函数:推导为 (a, b) -> (b, a)
swap (x, y) = (y, x)
swap1 = swap (10, "abc") -- (Int,String) -> (String,Int),结果("abc",10)
swap2 = swap ([1,2], 3.14)-- ([Int],Double) -> (Double,[Int]),结果(3.14,[1,2])
无约束/约束推断
根据表达式是否涉及类型类操作,Haskell的类型推断分为无约束推断和约束推断两类,二者均基于HM算法,核心差异是推导结果是否带有类型类约束,覆盖所有常规编码场景:
无约束推断
针对无类型类依赖、无特殊行为限制的表达式,推导出纯泛型类型(仅包含类型变量,无任何约束),该类型可适配任意具体类型,是Haskell参数多态的核心实现。
-- 无需类型标注,编译器无约束推断纯泛型类型
-- 1. 单参数泛型函数:推断为 a -> [a]
repeatOne x = [x]
r1 = repeatOne 10 -- [Int]
r2 = repeatOne 'a' -- [Char]
r3 = repeatOne [1,2] -- [[Int]]
-- 2. 双参数泛型函数:推断为 a -> b -> (a, b, [a])
combine x y = (x, y, [x, x])
c1 = combine 5 "abc" -- (Int, String, [Int])
c2 = combine 'a' 3.14 -- (Char, Double, [Char])
-- 3. 列表操作泛型函数:推断为 [a] -> Int
listLen xs = length xs
l1 = listLen [1,2,3] -- Int
l2 = listLen "hello" -- Int
约束推断
针对涉及类型类操作(如+、==、show) 的表达式,推导结果会带上对应的类型类约束,限制类型变量的适用范围——仅实现了该类型类的具体类型,才能使用该表达式,兼顾泛型和类型安全,是Haskell约束多态的核心实现。
-- 无需类型标注,编译器自动推导带类型类约束的类型
-- 1. 涉及Num类型类(+、*、0):推断为 Num a => a -> a -> a
add' x y = x + y * 2
a1 = add' 10 5 -- Int(10+5*2=20)
a2 = add' 1.2 3.4 -- Double(1.2+3.4*2=8.0)
-- 2. 涉及Eq类型类(==、/=):推断为 Eq a => a -> a -> Bool
isEqual x y = x == y
e1 = isEqual 5 5 -- Bool(True)
e2 = isEqual "abc" "def" -- Bool(False)
-- 3. 涉及Show类型类(show):推断为 Show a => a -> String
show' x = "Value: " ++ show x
s1 = show' 100 -- String("Value: 100")
s2 = show' 3.14 -- String("Value: 3.14")
-- 4. 多类型类约束:推断为 (Num a, Show a) => a -> String
calcAndShow x = show (x * x + 1)
cs1 = calcAndShow 5 -- "26"
cs2 = calcAndShow 2.5 -- "7.25"
类型推断限制
Haskell的类型推断能力强大,但并非万能,受HM算法本身和语言设计的限制,在部分复杂场景下无法完成自动推断,此时编译器会报“类型歧义”或“无法推导类型”错误,需要程序员添加显式类型标注辅助编译器完成推断,核心限制场景均附代码示例:
-- 1. 复杂递归函数:嵌套递归导致推断失败,需标注类型
-- 未标注:编译报错,标注后正常
fact :: Int -> Int -- 显式标注类型
fact 0 = 1
fact n = n * fact (n-1)
-- 2. 多参数类型类的歧义场景:无函数依赖导致无法推断返回类型
class Convert a b where
convert :: a -> b
-- 实现Int到String的转换
instance Convert Int String where
convert = show
-- 调用时无标注:编译报错(无法推断b的类型),需显式标注
cvt :: String
cvt = convert 123 :: String -- 显式指定返回类型为String
-- 3. 数值类型的极端歧义:无上下文的泛型数值,需标注
-- 未标注:编译报错(Num a => a 存在歧义)
num1 :: Int
num1 = 10 :: Int
-- 或通过上下文约束(如赋值给指定类型变量)
num2 :: Double
num2 = 3.14
-- 4. 高阶类型的复杂组合:多构造器嵌套导致推断歧义
import Data.Maybe
-- 未标注:编译警告,标注后清晰
process :: Maybe [Int] -> [Int]
process mxs = maybe [] (map (+1)) mxs
p1 = process (Just [1,2,3]) -- [2,3,4]
p2 = process Nothing -- []
多态类型
多态(Polymorphism)意为“一个接口,多种实现”,是Haskell实现泛型编程、代码高度复用的核心机制,让代码脱离具体类型的限制,适配不同的类型场景。Haskell中的多态类型分为参数多态、约束多态、Ad-hoc 多态三类,三者层层递进,覆盖从“纯通用抽象”到“类型特化实现”的所有泛型需求,所有知识点均附可运行代码示例:
参数多态
参数多态是Haskell最基础、最核心的泛型多态,也被称为“泛型多态”或“无约束多态”,通过类型变量(如a、b、c) 实现“一份代码适配所有类型”,无任何类型约束和行为限制,仅依赖类型的结构特征(如列表、元组的嵌套结构),是Haskell代码复用的最主要手段。
-- 1. 基础参数多态:恒等函数(a -> a),适配任意类型
id :: a -> a
id x = x
id1 = id 10 -- Int
id2 = id "abc" -- String
id3 = id [1,2,3] -- [Int]
id4 = id (Just 5) -- Maybe Int
-- 2. 列表通用操作:(a -> b) -> [a] -> [b],适配任意类型列表
map' :: (a -> b) -> [a] -> [b]
map' f [] = []
map' f (x:xs) = f x : map' f xs
-- 适配Int->Int
m1 = map' (+1) [1,2,3] -- [2,3,4]
-- 适配Int->String
m2 = map' show [1,2,3] -- ["1","2","3"]
-- 适配String->Int
m3 = map' length ["a", "ab", "abc"] -- [1,2,3]
-- 3. 元组通用操作:(a, b) -> (b, a),适配任意二元元组
swap' :: (a, b) -> (b, a)
swap' (x, y) = (y, x)
s1 = swap' (10, "abc") -- ("abc", 10)
s2 = swap' (3.14, [1,2])-- ([1,2], 3.14)
约束多态
约束多态是带类型类约束的参数多态,也被称为“有界多态”,在参数多态的基础上,通过=>为类型变量添加类型类约束,限制类型变量的适用范围——仅实现了该类型类的具体类型,才能使用该代码,让泛型代码可以调用类型类定义的抽象行为(如+、==、show),兼顾“泛型复用”和“类型专属行为”。
-- 1. 带Num约束:Num a => a -> a -> a,仅适配数值类型
sumTwo :: Num a => a -> a -> a
sumTwo x y = x + y
s1 = sumTwo 10 20 -- Int(30)
s2 = sumTwo 1.2 3.4 -- Double(4.6)
s3 = sumTwo 5 3.14 -- Double(8.14,自动类型统一)
-- 2. 带Eq约束:Eq a => [a] -> a -> Bool,仅适配可比较相等的类型
contains :: Eq a => [a] -> a -> Bool
contains [] _ = False
contains (x:xs) y | x == y = True
| otherwise = contains xs y
c1 = contains [1,2,3] 2 -- True
c2 = contains ["a","b"] "c"-- False
c3 = contains [Just 1, Nothing] Nothing -- True
-- 3. 带Num+Show约束:(Num a, Show a) => a -> String,多约束组合
numToStr :: (Num a, Show a) => a -> String
numToStr x = "Number: " ++ show (x * 2)
n1 = numToStr 5 -- "Number: 10"
n2 = numToStr 2.5 -- "Number: 5.0"
-- 4. 列表通用求和:Num a => [a] -> a,仅适配数值列表
sum' :: Num a => [a] -> a
sum' [] = 0
sum' (x:xs) = x + sum' xs
su1 = sum' [1,2,3,4] -- Int(10)
su2 = sum' [1.1,2.2,3.3]-- Double(6.6)
Ad-hoc 多态
Ad-hoc 多态也被称为特化多态,指“同一函数名(或操作符),根据参数的具体类型不同,拥有完全不同的实现逻辑”,即“接口统一,实现特化”。在Haskell中,Ad-hoc 多态完全通过类型类和实例实现,区别于参数多态的“一份通用实现适配所有类型”,是实现“类型专属行为”的核心方式。
-- 1. 自定义类型类实现Ad-hoc多态:同一接口,不同类型不同实现
class Printable a where
printVal :: a -> String -- 统一接口
-- 为Int实现实例:特化实现1
instance Printable Int where
printVal x = "Integer Value: " ++ show x
-- 为String实现实例:特化实现2
instance Printable String where
printVal x = "String Value: "" ++ x ++ """
-- 为Double实现实例:特化实现3
instance Printable Double where
printVal x = "Double Value: " ++ show (x * 100) ++ "%"
-- 统一调用,编译器自动选择对应实现
p1 = printVal 100 -- "Integer Value: 100"(Int实例)
p2 = printVal "Haskell" -- "String Value: "Haskell""(String实例)
p3 = printVal 0.95 -- "Double Value: 95.0%"(Double实例)
-- 2. 标准库类型类的Ad-hoc多态:show函数(统一接口,特化实现)
s1 = show 123 -- "123"(Int的Show实例)
s2 = show 'a' -- "'a'"(Char的Show实例)
s3 = show [1,2,3] -- "[1,2,3]"([Int]的Show实例)
s4 = show (Just 5) -- "Just 5"(Maybe Int的Show实例)
-- 3. 操作符的Ad-hoc多态:+号(Num类型类的特化实现)
add1 = 1 + 2 -- 整数加法(Int的Num实例)
add2 = 1.2 + 3.4 -- 浮点数加法(Double的Num实例)
add3 = 5 + 3.14 -- 混合类型加法(自动统一为Double实例)
类型别名与新类型
在实际开发中,常会遇到“复杂类型冗余”“基础类型混用导致歧义”“需要为类型添加专属标识”的问题,Haskell提供了type和newtype两个核心关键字,分别用于类型别名定义和零成本类型包装,同时newtype与常用的data关键字存在明确差异,三者各司其职,解决类型设计中的不同问题:
type 关键字简化类型
type关键字用于定义类型别名,核心作用是对复杂、冗长的原有类型进行重命名,仅做语法层面的简化和可读性提升,不生成任何新的类型标识,编译期编译器会将类型别名自动还原为原类型,无任何运行时成本,也无类型安全的隔离效果。
-- 1. 简化基础嵌套类型
type IntList = [Int]
type StringMap = [(String, String)] -- 字符串键值对列表
type Person = (String, Int, Bool) -- 姓名、年龄、是否成年
-- 直接使用类型别名,与原类型完全等价
intList :: IntList
intList = [1,2,3,4]
-- 原类型可直接赋值给别名类型,无隔离
intList' :: [Int]
intList' = intList
person :: Person
person = ("Tom", 25, True)
-- 解包时按原元组方式,无差异
getName :: Person -> String
getName (n, _, _) = n
-- 2. 简化高阶/业务相关类型
type Request = String
type Response = String
-- 业务处理函数:原类型 (String -> IO String),别名更易读
type Handler = Request -> IO Response
-- 实现处理函数,使用别名提升可读性
helloHandler :: Handler
helloHandler req = return $ "Hello, " ++ req
-- 3. 标准库经典别名:String = [Char]
type String = [Char] -- Haskell标准库中的定义
str :: String
str = "Haskell"
-- 与原类型[Char]完全等价
str' :: [Char]
str' = str
newtype 零成本包装
newtype关键字用于创建新类型,核心作用是将现有的单个基础类型进行“包装”,生成一个全新的、与原类型完全独立的类型标识,且编译器会对newtype做极致优化——编译期消除包装层,无任何运行时成本(零成本) ,既实现了类型的隔离和安全,又不影响程序执行效率。
-- 1. 基础定义:newtype 新类型 = 构造器 原类型(单构造器+单字段)
newtype Age = Age Int -- 年龄:包装Int
newtype Score = Score Int -- 分数:包装Int
newtype UserID = UserID String -- 用户ID:包装String
newtype Weight = Weight Double -- 体重:包装Double
-- 2. 创建新类型值:通过构造器包装
age :: Age
age = Age 25 -- 必须通过Age构造器,直接写25会报类型错误
score :: Score
score = Score 95
uid :: UserID
uid = UserID "user_123456"
-- 3. 解包使用:模式匹配或构造器直接调用
getAge :: Age -> Int
getAge (Age a) = a -- 模式匹配解包
getScore :: Score -> Int
getScore (Score s) = s
-- 4. 类型安全:不同newtype无法混用,编译期拦截错误
-- 合法:同类型操作
ageAdd :: Age -> Int -> Age
ageAdd (Age a) n = Age (a + n)
-- 非法:不同newtype混用(编译报错)
-- wrong = Age 25 + Score 95 -- 编译器直接报错,避免类型混淆
-- 5. 零成本特性:运行时无包装层,与原类型执行效率一致
-- 编译后,Age 25 直接等价于 25,无额外开销
newtype vs data
newtype和data都是Haskell中定义自定义类型的核心关键字,二者均可实现类型包装和自定义类型设计,但在语法限制、运行时成本、类型类实例、使用场景上存在核心差异,不可混用,通过代码示例对比核心区别:
-- ==================== newtype 实现 ====================
-- 仅支持:单构造器 + 单字段
newtype Age = Age Int -- 合法
-- newtype Point = Point Int Int -- 非法:多字段,newtype不支持
-- 零成本:编译期无包装层
age :: Age
age = Age 25
-- 解包简单
unAge :: Age -> Int
unAge (Age a) = a
-- ==================== data 实现 ====================
-- 无限制:多构造器、多字段、递归类型均支持
-- 单构造器单字段(合法,但有运行时开销)
data Score = Score Int deriving (Show)
-- 多构造器(合法,描述不同状态)
data Shape = Circle Double | Rectangle Double Double deriving (Show)
-- 多字段(合法,描述复杂结构)
data Point = Point Double Double deriving (Show)
-- 递归类型(合法,描述树形结构)
data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show)
-- 创建data类型值
score :: Score
score = Score 95
circle :: Shape
circle = Circle 5.0 -- 圆:半径5.0
rect :: Shape
rect = Rectangle 3.0 4.0 -- 矩形:长3.0,宽4.0
point :: Point
point = Point 10.0 20.0
tree :: Tree Int
tree = Node 1 (Node 2 Empty Empty) (Node 3 Empty Empty)
-- 解包data类型:模式匹配
getCircleRadius :: Shape -> Double
getCircleRadius (Circle r) = r
getCircleRadius _ = 0.0
getPointX :: Point -> Double
getPointX (Point x _) = x
-- ==================== 核心差异对比 ====================
-- 1. 类型类实例:newtype可自动继承,data需手动实现
-- newtype:为Age实现Num实例,可直接操作
instance Num Age where
(+) (Age a1) (Age a2) = Age (a1 + a2)
(*) (Age a1) (Age a2) = Age (a1 * a2)
fromInteger n = Age (fromInteger n)
-- 其他方法可默认推导
-- data:为Score实现Num实例,需手动实现所有方法
instance Num Score where
(+) (Score s1) (Score s2) = Score (s1 + s2)
(*) (Score s1) (Score s2) = Score (s1 * s2)
(-) (Score s1) (Score s2) = Score (s1 - s2)
abs (Score s) = Score (abs s)
signum (Score s) = Score (signum s)
fromInteger n = Score (fromInteger n)
-- 2. 运行时成本:newtype无开销,data有轻微开销
-- 编译后:Age 25 → 25(无包装),Score 95 → 包装为Score构造器(有开销)
10.5 类型系统基础特性
在类型推断和实际使用中,Haskell提供了两个实用的基础特性,分别解决数值类型的默认推导歧义和编译器的类型推断歧义问题,平衡“类型推断的简洁性”和“类型指定的灵活性”,是日常开发中高频使用的特性,所有知识点均附可运行代码示例:
类型默认规则
Haskell中数值类型基于Num类型类实现泛型(如Int、Integer、Double、Float均实现Num),当表达式的类型为泛型数值类型(Num a => a) 且无上下文明确指定具体类型时,会出现数值类型歧义,编译器无法确定应推导为哪种具体数值类型,此时Haskell的类型默认规则会自动将其推导为预设的具体数值类型(默认优先Integer > Double > Int > Float),避免编译错误,同时支持自定义默认类型。
-- 1. 默认规则:无上下文时,Num a => a 优先推导为Integer/Double
-- 示例1:整数泛型 → 默认推导为Integer
num1 = 100 -- 类型:Integer
-- 示例2:浮点数泛型 → 默认推导为Double
num2 = 3.14 -- 类型:Double
-- 示例3:列表求和 → 默认推导为Integer
sumInt = sum [1,2,3,4] -- 类型:Integer,结果10
-- 示例4:混合数值 → 默认推导为Double(精度更高)
mixNum = 1 + 2.0 -- 类型:Double,结果3.0
-- 2. 上下文约束:覆盖默认规则,按上下文推导具体类型
-- 赋值给Int变量 → 推导为Int
intNum :: Int
intNum = 100 -- 类型:Int,而非默认Integer
-- 作为Int参数传入函数 → 推导为Int
addInt :: Int -> Int -> Int
addInt x y = x + y
addRes = addInt 10 20 -- 10和20均推导为Int
-- 3. 自定义默认类型:通过default语句修改默认优先级
-- 自定义:优先推导为Int → Float → Double
default (Int, Float, Double)
-- 自定义后,列表求和推导为Int(而非默认Integer)
sumInt' = sum [1,2,3] -- 类型:Int,结果6
-- 浮点数推导为Float(而非默认Double)
floatNum = 3.14 :: Float -- 类型:Float
显式类型应用TypeApplications 扩展
TypeApplications(类型应用)是Haskell的扩展特性(非默认开启),核心作用是允许程序员在调用函数时,显式指定类型推断中的类型变量,直接为函数的泛型类型变量绑定具体类型,彻底解决编译器的类型推断歧义问题,替代繁琐的显式类型标注,让代码更简洁、更易读。
-- 必须在代码最顶部开启扩展
{-# LANGUAGE TypeApplications #-}
import Data.Maybe
import Data.Eq
-- 1. 解决read函数的类型歧义:read :: Read a => String -> a
-- 无TypeApplications:需显式标注返回类型,繁琐
readInt1 :: Int
readInt1 = read "123" :: Int
readDouble1 :: Double
readDouble1 = read "3.14" :: Double
-- 有TypeApplications:直接在函数后@指定类型,简洁
readInt2 = read @Int "123" -- 类型:Int,结果123
readDouble2 = read @Double "3.14" -- 类型:Double,结果3.14
readList = read @[Int] "[1,2,3,4]" -- 类型:[Int],结果[1,2,3,4]
-- 2. 解决pure函数的容器类型歧义:pure :: Applicative f => a -> f a
-- 无TypeApplications:需标注容器类型,繁琐
pureList1 :: [Int]
pureList1 = pure 5 :: [Int]
pureMaybe1 :: Maybe Int
pureMaybe1 = pure 5 :: Maybe Int
-- 有TypeApplications:@指定容器类型f,直接明了
pureList2 = pure @[] 5 -- 容器:[](列表),结果[5]
pureMaybe2 = pure @Maybe 5 -- 容器:Maybe,结果Just 5
pureIO = pure @IO 5 -- 容器:IO,结果IO 5
-- 3. 多类型变量:按类型签名顺序@指定多个类型变量
-- 示例:lookup :: Eq k => k -> [(k, v)] -> Maybe v
-- 显式指定k=String,v=Int
lookupRes = lookup @String @Int "name" [("name", 123), ("age", 25)]
-- 结果:Just 123
-- 4. 结合类型类约束:精准指定约束中的类型变量
-- 示例:show . read :: (Read a, Show a) => String -> String
-- 指定a=Double,将字符串转Double再转字符串
showRead = show . read @Double $ "3.1415926"
-- 结果:"3.1415926"
-- 5. GHCi中使用:先开启扩展,再调用
-- :set -XTypeApplications
-- > read @Int "456"
-- 456
-- > pure @Maybe 10
-- Just 10