使用约束创建自定义数据类型

时间:2015-08-20 01:54:13

标签: haskell

我正在尝试创建自定义数据类型。作为一个例子

data Time  = Second Int deriving (Show)

但是,这太限制了(我们可以说以后需要几毫秒)。我想改为定义这样的东西:

data Time  = Second Num deriving (Show)

这不会编译,因为Num* -> ghc-prim-0.4.0.0:GHC.Prim.Constraint

如何设置Time以使Second可能包含任何Num

2 个答案:

答案 0 :(得分:3)

在类和类型的Wikibooks部分中,found here的最佳例子之一可能不是那么令人满意。他们说:

  

数据声明中的类型约束没有最初看起来那么有用。考虑:

data (Num a) => Foo a = F1 a | F2 a String
     

这里,Foo是一个带有两个构造函数的类型,它们都接受一个必须在Num中的类型a的参数。但是,(Num a)=>约束仅对F1和F2构造函数有效,而不适用于涉及Foo的其他函数。因此,在以下示例中......

fooSquared :: (Num a) => Foo a -> Foo a
fooSquared (F1 x)   = F1 (x * x)
fooSquared (F2 x s) = F2 (x * x) s
     

...即使构造函数确保将在Num中使用某种类型,我们也无法避免在fooSquared的签名中重复约束

这表明,合理的选择是使用泛型参数创建Time,然后确保对Time数据进行操作的模块函数始终具有{{}的必要约束。 1}}。

如果有人沮丧地愚蠢地制造Num或其他东西,那就不用担心了 - 如果他们这样做了,那么所提供的模块功能都不会对他们有所帮助,所以它无所谓。

还可以选择使用GADT,Time String编译指示和{-# LANGUAGE GeneralizedNewtypeDeriving #-}编译指示。但通常情况下这些开始会产生不必要的额外复杂程度,特别是如果你是像我这样的Haskell新手。

答案 1 :(得分:2)

有一个名为Datatype Contexts的弃用功能允许您这样做:

{-# LANGUAGE DatatypeContexts #-}
data Num a => Time a = Second a deriving (Show)
t = Second (5 :: Int)
main = print t

这是在GHC 7.8.3上执行的(抱歉,没有要检查的是7.10),但警告你有关弃用的信息:

t.hs:1:14: Warning:
    -XDatatypeContexts is deprecated: It was widely considered a
misfeature, and has been removed from the Haskell language.
Second 5

不推荐使用的方法是使用Generalized Algebraic Datatypes(GADT)(并且您还需要独立的deriving):

{-# LANGUAGE GADTs, StandaloneDeriving #-}
data Time a where
    Second :: Num a => a -> Time a
deriving instance Show a => Show (Time a)
t = Second (5 :: Int)
main = print t

如果您尝试使用非Num的内容创建变量,则会出现编译错误:

t = Second "a"

t.hs:12:5:
    No instance for (Num [Char]) arising from a use of ‘Second’
    In the expression: Second "a"
    In an equation for ‘t’: t = Second "a"