Haskell 新类型定义
Haskell newtype definition
我在作业提供的代码中读到这个语句:-
newtype STR a = STR (Store -> (Result a, Store))
上面的 link 看起来像:
a === (Store -> (Result a, Store))
这怎么可能是一个有效的陈述?这是否意味着 a
是一个将 Store 作为参数和 returns ('the same function wrapped in Result', Store)
的函数?
newtype
的定义有点混乱,因为符号 STR
有两种不同的含义:即类型名称(第一次出现)和构造函数(第二次出现) .将两者重命名为不同的东西会导致等效
newtype STRType a = STRConstructor (Store -> (Result a, Store))
换句话说,这引入了一个类型STRType a
,它在结构上与Store -> (Result a, Store)
相同(但需要包裹在STRConstructor
中)
希望您的 course/book 已经了解了 type
、data
和 newtype
之间的区别;否则这将保持相当神秘,恐怕...
所以对于 data/newtype 定义,左侧包含一个 类型定义 ,包括一个名称和一些类型变量,而右侧包含一个 构造函数列表 通常还包括名称和类型。一个例子是:
data List x = Nil | Cons x (List x)
请注意,Nil
和 Cons
是您的构造函数,x
和 List x
是 Cons
构造函数的参数类型,而 List
是类型的名称(它有种类 * -> *
;它需要一个类型参数 ___ 到 "fill it in" 才能描述 ___ 的列表)。
有时我们想给一个类型起别名。有两种方法可以做到这一点。首先,type
,这使得类型相称——所以如果你写 type DirectoryPath = [String]
,那么当你有一个 DirectoryPath
时,你可以像操作字符串列表一样操作它;特别是您可以使用 :
或 ++
附加到它;所以 "apps" : base_directory
是完全合法的 Haskell.
有时,您只是想用一个大的警告标志来破坏这些功能,"don't use this if you don't know what you're doing." 为此,您可能会有助于编写 data FilePath = FilePath [String]
。请注意,我们有点滥用符号,将 type 命名为与 only constructor for that type 相同。现在要做同样的事情,你必须写:
case base_directory of FilePath bd -> FilePath $ "apps" : bd
你为什么要这样做?好吧,首先,在上面的语法中,目录结构是从右到左增长的,而大多数人是从左到右编写目录的。其次,您可能希望将 .
附加为空操作(即 bd ++ []
),将 ..
附加为父指针(即 tail bd
)。您可能还有一些需要维护的奇怪约定(例如,如果列表以“”开头,那么它是一个绝对目录,否则它是相对于当前目录的)。最后,您可能希望稍后将代码转换为 Maybe [String]
,以便 Nothing
值可以表示执行疯狂操作的路径,例如 /../..
(绝对路径在根之上有两个父项)。
如果你只说:
,所有这一切都会变得更容易
FilePath xs ./ x
| x == "." = FilePath xs
| x == ".." = FilePath (tail xs)
| otherwise = ...
然后在其他任何地方都强制执行 base_directory ./ "apps"
。
另一个例子是newtype SanitizedString = Sanitized String
。由于我们没有使用 type
,我们得到一个编译时标签,它跟踪我们的代码,确保用户提供的字符串在进入数据库插入语句或用户界面或任何地方之前被正确转义。
您在这里使用它的可能是您可以为该类型编写一个 Monad
实例,从而将其与 do
符号一起使用。
如果您的代码仅包含一种现有类型,newtype
可避免引入额外的懒惰担忧和数据构造函数延迟等。否则就像 data
。所以在你的代码中:
newtype STR a = STR (Store -> (Result a, Store))
这不是 type
同义词,更像是一个 data
构造函数,它最终在编译后消失。 STR a
是 Store -> (Result a, Store)
的别名,它被包装在 STR
构造函数中(因此它不能在没有解构赋值的情况下直接用作函数)。
我在作业提供的代码中读到这个语句:-
newtype STR a = STR (Store -> (Result a, Store))
上面的 link 看起来像:
a === (Store -> (Result a, Store))
这怎么可能是一个有效的陈述?这是否意味着 a
是一个将 Store 作为参数和 returns ('the same function wrapped in Result', Store)
的函数?
newtype
的定义有点混乱,因为符号 STR
有两种不同的含义:即类型名称(第一次出现)和构造函数(第二次出现) .将两者重命名为不同的东西会导致等效
newtype STRType a = STRConstructor (Store -> (Result a, Store))
换句话说,这引入了一个类型STRType a
,它在结构上与Store -> (Result a, Store)
相同(但需要包裹在STRConstructor
中)
希望您的 course/book 已经了解了 type
、data
和 newtype
之间的区别;否则这将保持相当神秘,恐怕...
所以对于 data/newtype 定义,左侧包含一个 类型定义 ,包括一个名称和一些类型变量,而右侧包含一个 构造函数列表 通常还包括名称和类型。一个例子是:
data List x = Nil | Cons x (List x)
请注意,Nil
和 Cons
是您的构造函数,x
和 List x
是 Cons
构造函数的参数类型,而 List
是类型的名称(它有种类 * -> *
;它需要一个类型参数 ___ 到 "fill it in" 才能描述 ___ 的列表)。
有时我们想给一个类型起别名。有两种方法可以做到这一点。首先,type
,这使得类型相称——所以如果你写 type DirectoryPath = [String]
,那么当你有一个 DirectoryPath
时,你可以像操作字符串列表一样操作它;特别是您可以使用 :
或 ++
附加到它;所以 "apps" : base_directory
是完全合法的 Haskell.
有时,您只是想用一个大的警告标志来破坏这些功能,"don't use this if you don't know what you're doing." 为此,您可能会有助于编写 data FilePath = FilePath [String]
。请注意,我们有点滥用符号,将 type 命名为与 only constructor for that type 相同。现在要做同样的事情,你必须写:
case base_directory of FilePath bd -> FilePath $ "apps" : bd
你为什么要这样做?好吧,首先,在上面的语法中,目录结构是从右到左增长的,而大多数人是从左到右编写目录的。其次,您可能希望将 .
附加为空操作(即 bd ++ []
),将 ..
附加为父指针(即 tail bd
)。您可能还有一些需要维护的奇怪约定(例如,如果列表以“”开头,那么它是一个绝对目录,否则它是相对于当前目录的)。最后,您可能希望稍后将代码转换为 Maybe [String]
,以便 Nothing
值可以表示执行疯狂操作的路径,例如 /../..
(绝对路径在根之上有两个父项)。
如果你只说:
,所有这一切都会变得更容易FilePath xs ./ x
| x == "." = FilePath xs
| x == ".." = FilePath (tail xs)
| otherwise = ...
然后在其他任何地方都强制执行 base_directory ./ "apps"
。
另一个例子是newtype SanitizedString = Sanitized String
。由于我们没有使用 type
,我们得到一个编译时标签,它跟踪我们的代码,确保用户提供的字符串在进入数据库插入语句或用户界面或任何地方之前被正确转义。
您在这里使用它的可能是您可以为该类型编写一个 Monad
实例,从而将其与 do
符号一起使用。
如果您的代码仅包含一种现有类型,newtype
可避免引入额外的懒惰担忧和数据构造函数延迟等。否则就像 data
。所以在你的代码中:
newtype STR a = STR (Store -> (Result a, Store))
这不是 type
同义词,更像是一个 data
构造函数,它最终在编译后消失。 STR a
是 Store -> (Result a, Store)
的别名,它被包装在 STR
构造函数中(因此它不能在没有解构赋值的情况下直接用作函数)。