Skip to content

Latest commit

 

History

History
95 lines (59 loc) · 3.46 KB

NewType.md

File metadata and controls

95 lines (59 loc) · 3.46 KB

为了让类型签名更加易懂,很多时候我们会用type为现有的类型起个别名

type Position = (Int,Int)

emm,一个在平面上的坐标,用2个整数表示,非常显然。但是如果不写类型标注,你就看不出二元组到底是 Position 还是用于其他用途的二元组

mkPos :: Int -> Int -> Position
mkPos x y = (x, y)
-- mkPos = (,)

这样呢?我们用抽象数据类型的方法,弄出一个构造子来,这样构造 Position 是很明显了。但是,还不够,这样的类型约束毕竟还是弱的,为了让这一切更加明确,我们使用 newtype

newtype Position = Position (Int,Int)

以上,这是一个好方案,如果你忘记了显式调用 Position,类型检查不会轻易放过你,假如你有需要复用 (Int,Int),那 newtype 是避免混淆的好方法,举个例子,扫雷!

我们在一个 9×9 的 Field 上进行游戏,同时对每个格子,用 Position 类型表达它们的位置。同时,我们使用一个非常简单的方法放置地雷,任取一个随机数并且取n的模,余数会严格限定在 [0, n) 之间。同时随机数本身也可用很简单的方式解决,一个方程和一个初始种子,再加上状态就行了。

newtype State = State (Int,Int)

我承认 System.RandomState Monad 的存在使得我举的例子有点傻气,但是我也不是一个特别聪明的人,而且我真的这样做。总之 newtype 的封装使得同一类型可以安全地用于不同概 念的抽象,并且,newtype 定义的“新类型”实际上在运行时没有多余成本。该是二元组的还是二元组。

更“好”的例子:Data.Monoid 模块中的 SumProduct 类型

参考:GHC/Coercible - HaskellWiki

给出一个 newtype 定义

newtype HTML = MkHTML String

-- 在 HTML 类型和 String 之间的转换非常简单。

toHTML :: String -> HTML
toHTML s = MkHTML s

fromHTML :: HTML -> String
fromHTML (MkHTML s) = s

-- 这两个函数都是没有运行时负担的。

-- 但是要如何转换出一整个列表的 HTML 呢? 我们可以试着写出转换函数

toHTMLs :: [String] -> [HTML]
toHTMLs = map MkHTML


-- 不妙了,虽然 MkHTML 啥都没干,但是 map 对原 list 的遍历和重建副本是有代价的。

-- 解决方案是 GHC7.8.1 引入的 Coercible

import Data.Coerce
toHTMLs :: [String] -> [HTML]
toHTMLs = coerce

-- 这是 GHC 留下的一个类型系统上的后门,由 GHC 本身实现,但是妙处在于,虽然它 hack 了类型系统,但是它的类型转换是安全的。

-- 论文:http://research.microsoft.com/en-us/um/people/simonpj/papers/ext-f/coercible.pdf

-- 太长不看版:

-- newtype 定义的封装可以与原类型任意使用 coerce 转换,无运行时成本。(但是,这里没有讨论带类型参数的情况)

-- 对于使用了所谓“Phantom Type”的 newtype 定义,coerce 也能识别

newtype NT a = MkNT ()

trans :: NT Bool -> NT Int
trans = coerce

-- 对于藏在其他结构内部的 newtype 定义,当然 也没问题。

newtype Age = MkAge Int
newtype AgeRange = MkAR (Int,Int)
newtype BigAge = MkBig Age

-- (<=>)意为可用 coerce 转换

-- 以下都没问题

-- Either Int Age <=> Either Int Int
-- (Int -> Age) <=> (Age -> Int)
-- Map k Age <=> Map k Int
-- (Age,Age) <=> AgeRange <=> (Int,Int)

-- 具体的实现和规则可见论文。