我在为家庭作业提供的代码中读到了这句话: -
newtype STR a = STR (Store -> (Result a, Store))
上面的链接看起来像是:
a === (Store -> (Result a, Store))
这怎么可能是一个有效的陈述?这是否意味着a
是一个以Store为参数并返回('the same function wrapped in Result', Store)
?
答案 0 :(得分:9)
newtype
定义有点令人困惑,因为符号STR
有两种不同的含义:即名称类型(第一次出现)和构造函数(第二次出现)。将两者重命名为不同的东西会导致等效的
newtype STRType a = STRConstructor (Store -> (Result a, Store))
换句话说,这会引入一种STRType a
类型,其结构与Store -> (Result a, Store)
相同(但需要包含在STRConstructor
中)
我希望您的课程/书籍已经涉及type
,data
和newtype
之间的差异;否则这将保持相当神秘,我害怕......
答案 1 :(得分:3)
因此,对于数据/新类型定义,左侧包含类型定义,包括名称和某些类型变量,而右侧包含构造函数列表通常还包括名称和类型。一个例子是:
data List x = Nil | Cons x (List x)
请注意,Nil
和Cons
是您的构造函数,x
和List x
是Cons
构造函数的参数类型,而List
是类型的名称(具有种类* -> *
;它需要一个类型参数___到"在它可以描述___ s的列表之前将其填入"。
有时我们想要为一个类型添加别名。有两种方法可以做到这一点。首先,type
,使类型保持相称 - 所以如果你写type DirectoryPath = [String]
,那么当你有DirectoryPath
时,你可以像字符串列表一样操纵它;特别是您可以使用:
或++
附加到其中;所以"apps" : base_directory
完全合法的Haskell。
有时候,你只是想用一个大警告标志来破坏这些功能,如果你不知道自己在做什么,就不要使用它。"为此,您可以帮助编写data FilePath = FilePath [String]
。请注意,我们正在滥用符号,将类型命名为与该类型的唯一构造函数相同。现在做同样的事情,你必须写:
case base_directory of FilePath bd -> FilePath $ "apps" : bd
你为什么要这样做?好吧,首先,在上面的语法中,目录结构从右到左增长,而大多数人从左到右编写目录。其次,您可能希望将.
添加为无操作(即bd ++ []
)和..
作为父指针(即tail bd
)。您可能还有一些奇怪的约定(例如,如果列表以&#34开始;"那么它是一个绝对目录,否则它是相对于当前目录的)需要维护。最后,您可能希望稍后将代码转换为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
构造函数中(因此无法在没有解构赋值的情况下直接用作函数)。