PureScript - 什么是新类型?

PureScript - What is a newtype?

PureScript 手册定义了一个练习如下:

Define a Show instance for Point. Match the same output as the showPoint function from the previous chapter. Note: Point is now a newtype (instead of a type synonym), which allows us to customize how to show it. Otherwise, we'd be stuck with the default Show instance for records. (https://book.purescript.org/chapter6.html)

但是,没有提供有关 newtype 是什么的信息。

什么是新类型?它是做什么用的?为什么它在这里可以工作,但不能打字?

Newtype 只是一种(或多种)类型的包装器。我们通常使用它来将预定义或已经可用的类型公开为新类型。

我们可以用 newtype 做很多有用的事情,其中​​之一是作者在你上面分享的章节中指定的。

当您指定

type Point = 
  { x :: Number
  , y :: Number
  }

您实际上不能定义自己的类型 class 实例(例如显示实例),因为 Point 只是 Record 类型的类型同义词,它在 purescript 的 Prim 模块中预定义。

所以你用新类型包装它,并使它成为用户定义的类型。现在您可以派生任何您想要的实例。

您还可以使用它来创建更严格的值。例如,你想创建一个类型名称,当它的大小小于 6 时,你想强调一个错误。 所以你要做的是,

type Name = Either String String

然后你创建一个函数,

type Name = Either String String 
createName' :: String -> Name 
createName' name 
 | (length name) >= 6 =   (Right name)
 | otherwise          =   (Left "Invalid Name")

你现在很开心。但这并不限制其他开发人员做类似

的事情
 myName :: Name
 myName = (Right "a")

那么如何让 Name 仅由函数创建?

  1. 通过仅导入函数。
  2. 不导入 Name

问题是您不能执行第 2 步,因为 Name 只是一个类型同义词。

name :: Name 等同于 name :: Either String String

所以你用新类型

包装 Either String String
 newtype StrictName = MkName (Either String String)
 createName :: String -> StrictName 
 createName name 
    |  (length name) >= 6 =  MkName (Right name)
    |  otherwise          =  MkName (Left "Invalid Name")

现在你只导入函数和 StrictName(没有它的构造函数) 例如

module Data.MyTypes.Name
(StrictName,createName)

现在没有其他方法可以在不使用 createName 函数的情况下创建 StrictName 值。 (这也适用于 ADT 类型,但我更喜欢 newtype)

Newtype 还用于处理 orphan instances 和更多有用的东西。

Note : Newtype adds runtimes overhead as theres one more wrapper around the original type.