数据类型已声明 - haskell 中的 newtype

Pis*_*tor 0 haskell functional-programming newtype

我很难理解该newtype声明。我正在尝试 LYAH 中的练习:http://learnyouahaskell.com/functors-applicative-functors-and-monoids#the-newtype-keyword

并希望使用newtype来进行自定义类型实例化,Tofu Frank如下所示,遵循 LYAH 的配方:http://learnyouahaskell.com/making-our-own-types-and-typeclasses,但 newtype我自己添加了 -part 。

class Tofu t where
   tofu :: j a -> t a j

instance Tofu Frank where
   tofu x = Frank x

data Frank a b = Frank {frankField :: b a} deriving (Show)

newtype Frank a b = Frank{frankField :: (b a)} deriving Show

OUTPUT:

Multiple declarations of `Frank'
    Declared at: tryouts.hs:563:1   
                 tryouts.hs:572:1 

Run Code Online (Sandbox Code Playgroud)

在 LYAH 的描述中newtypehttp://learnyouahaskell.com/functors-applicative-functors-and-monoids#the-newtype-keyword指出:

使用 newtype 关键字代替 data 关键字。这是为什么呢?其一,newtype 速度更快。如果您使用 data 关键字来包装类型,则程序运行时所有包装和解包都会产生一些开销。但是如果你使用 newtype,Haskell 知道你只是用它来将现有类型包装成新类型(因此得名),因为你希望它内部相同但具有不同的类型。考虑到这一点,一旦 Haskell 解析出哪个值是什么类型,就可以摆脱包装和解包。

但是我newtype在这里是否误解了,只要前者在构造函数中只有一个字段(如我的示例中所示),newtype就不会覆盖现有类型吗?data如果我省略 -data声明部分,它似乎确实有效。

K. *_*uhr 7

请注意, LYAH中的描述表示newtype使用代替data这就是关键。两者datanewtype声明一个新的数据类型(Frank在您的情况下),并且您应该只使用其中之一,而不是同时使用相同的新数据类型。所以,你要么写:

data Frank a b = Frank {frankField :: b a} deriving (Show)
Run Code Online (Sandbox Code Playgroud)

如果你想Frank成为常规data类型,或者你写:

newtype Frank a b = Frank {frankField :: b a} deriving (Show)
Run Code Online (Sandbox Code Playgroud)

如果你想Frank成为一个newtype

一般来说,Haskell 不支持“覆盖”(或重新定义或重新声明)数据类型,无论您使用datanewtype或某种混合。其他语言在这方面可能更灵活。例如,Python 将允许您定义一个class Frank,在某些代码中引用它,然后在更多代码中重新定义class Frank和使用新类:

class Frank:
    def __init__(self):
        self.name = "Frank Burns"

frank1 = Frank()

class Frank:
    def __init__(self):
        self.name = "Frankenstein"

frank2 = Frank()

print(frank1.name)   # Frank Burns
print(frank2.name)   # Frankenstein
Run Code Online (Sandbox Code Playgroud)

然而,Haskell 要求在一个地方定义新的数据类型。这类似于 Haskell 也要求只在一个地方定义一个(全局)函数。连续的多个定义行被视为单个定义的一部分:

-- this defines only one "frank"
frank 0 = "foo"
frank 1 = "bar"
Run Code Online (Sandbox Code Playgroud)

并且拒绝多个不连续的定义:

frank n = n*2
burns c = (c, c)
frank x = x-5     -- rejected
Run Code Online (Sandbox Code Playgroud)