自定义类型
data
有点像C里面的struct
用来定义数据的结构
data Position = MakePosition Double Double -- MakePosition是构造函数
MakePosition 1.5 2 :: Position
-- 中缀构造函数
data Position = Double :+ Double -- 必须加冒号
1.5 :+ 2 :: Position
-- 空构造函数(可以视为一个该类型的值)
data Color = Red -- 此处Red就是空构造函数
-- 多个构造函数
data Color = Red | Green | Blue
-- 参数化的数据类型
data UserInfo a b = NoInfo | NameInfo a | AgeInfo b
-- 记录
data Person =
Student {
name :: String, -- 这个叫记录投影(record projection)
age :: Int -- 相当于age :: Person -> Int,是一个从记录中解构数据的函数
}
| Teacher {
name :: String, -- 如果只有一个投影,则在实例化时不需要指定投影名
rank :: String -- 比如只有name,那就Teacher "John" :: Person
}
johnDoe :: Person
johnDoe = Student { name = "John Doe", age = 15 } -- 创建记录
johnsName :: String
johnsName = name johnDoe -- 访问记录的字段
olderJohn :: Person
olderJohn = johnDoe { age = 16 } -- 更新记录
-- GADTs(通用代数数据类型):显式精确地指定不同构造函数类型
data Expr a where
IntLit :: Int -> Expr Int
BoolLit :: Bool -> Expr Bool
Add :: Expr Int -> Expr Int -> Expr Int
class
类型类
有点像interface
为一类数据类型定义一组功能
定义
注意:类型类不是数据类型,类型类是事先约定好的一组函数
然后用instance为数据类型实现这个类型类中的函数(一般来说必须实现全部函数)
类型类不仅仅是为了让多种数据类型适配同一个函数
更多的是为了解决多态和重载的问题
注意:类型类中定义的函数也属于所处模块的全局命名空间内,也就是说跟你的其他函数不能重名
而且类型类中定义的函数只能由类型类的语法去绑定,不能直接在全局绑定
data Color = Red | Green | Blue | Transparent | NoColor
class Describe a where -- 定义Describe行为这个类型类,这里的a是任意可能的数据类型
describe :: a -> String
-- 这里也可以直接对函数进行绑定,比如describe x = "This is x."
instance Describe Color where -- 为Color数据类型实现Describe类型类(只能绑定Describe中出现过的函数)
describe Red = "This is red."
describe Green = "This is green."
describe Blue = "This is blue."
describe _ = "Unknown color."
类型约束
约束某种数据类型必须实现某个类型类
可以高度抽象出一种语法,如下:
TypeClass1 a, TypeClass2 b, ... => ActualType
就是说,在左边规定a是实现了TypeClass1的数据类型,然后就可以在右边使用a类型
-- 函数签名
functionName :: TypeClass1 a => a -> ...
-- 数据类型定义
data DataType a where
ConstructorName :: TypeClass1 a => a -> DataType a
-- 类型类定义
class TypeClass1 a => TypeClass2 a where
...
-- 类型实例定义
instance (TypeClass1 a, TypeClass2 a) => TypeClass3 [a] where
...
type
类型别名
type Position = (Double, Double)
newtype
定义新的类型,但是只有一个构造函数
用于创建只有一个成分的新类型,没有运行时的额外成本
适合于当你想让某个类型有特殊含义但又不想增加额外的处理成本时使用
newtype Position = MakePosition (Double, Double)
MakePosition (1.5, 2) :: Position