是否可以使用新值扩展数据类型?
例如:以下编译:
data Axes2D = X | Y
data Axes3D = Axes2D | Z
Run Code Online (Sandbox Code Playgroud)
但是,以下内容:
data Axes2D = X | Y deriving (Show, Eq)
data Axes3D = Axes2D | Z deriving (Show, Eq)
type Point2D = (Int, Int)
type Point3D = (Int, Int, Int)
move_along_axis_2D :: Point2D -> Axes2D -> Int -> Point2D
move_along_axis_2D (x, y) axis move | axis == X = (x + move, y)
| otherwise = (x, y + move)
move_along_axis_3D :: Point3D -> Axes3D -> Int -> Point3D
move_along_axis_3D (x, y, z) axis move | axis == X = (x + move, y, z)
| axis == y = (x, y + move, z)
| otherwise = (x, y, z + move)
Run Code Online (Sandbox Code Playgroud)
给出以下编译错误(move_along_axis_3D注释掉不会出错):
Prelude> :l expandTypes_test.hs
[1 of 1] Compiling Main ( expandTypes_test.hs, interpreted )
expandTypes_test.hs:12:50:
Couldn't match expected type `Axes3D' with actual type `Axes2D'
In the second argument of `(==)', namely `X'
In the expression: axis == X
In a stmt of a pattern guard for
an equation for `move_along_axis_3D':
axis == X
Failed, modules loaded: none.
Run Code Online (Sandbox Code Playgroud)
那么,有可能使X和Y类型Axes2D以及类型Axes3D?如果有可能:我做错了什么?另外:为什么不可能?
ehi*_*ird 10
与Daniel Fischer所说的一样,为了扩展为什么这是不可能的:你想要的那种子类型的问题比命名歧义更深入; 一般来说,他们使类型推理变得更加困难.由于这个原因,我认为Scala的类型推断比Haskell更受限制和局部化.
但是,您可以使用类型系统对此类事物进行建模:
class (Eq t) => HasAxes2D t where
axisX :: t
axisY :: t
class (HasAxes2D t) => HasAxes3D t where
axisZ :: t
data Axes2D = X | Y deriving (Eq, Show)
data Axes3D = TwoD Axes2D | Z deriving (Eq, Show)
instance HasAxes2D Axes2D where
axisX = X
axisY = Y
instance HasAxes2D Axes3D where
axisX = TwoD X
axisY = TwoD Y
instance HasAxes3D Axes3D where
axisZ = Z
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用警卫对这些值进行"模式匹配":
displayAxis :: (HasAxes2D t) => t -> String
displayAxis axis
| axis == axisX = "X"
| axis == axisY = "Y"
| otherwise = "Unknown"
Run Code Online (Sandbox Code Playgroud)
这与子类型具有许多相同的缺点:使用axisX,axisY并且axisZ将具有变得模糊的倾向,需要类型注释来破坏练习的点.与使用具体类型相比,使用这些类型类约束编写类型签名也是一件相当丑陋的事.
还有另一个缺点:具体类型,当你编写一个函数时Axes2D,一旦你处理X并且Y你知道你已经涵盖了所有可能的值.使用类型类解决方案,没有什么可以阻止您传递Z给期望实例的函数HasAxes2D.你真正想要的是关系反过来,所以你可以传递X和Y期望3D轴的函数,但不能传递Z给期望2D轴的函数.我认为没有办法用Haskell的类型系统正确建模.
这种技术偶尔会很有用 - 例如,将一个像GUI工具包这样的OOP库绑定到Haskell - 但一般来说,使用具体类型并明确支持OOP术语中的组合优于继承,即明确包装"子类型" 更自然在构造函数中.处理构造函数包装/展开通常不是很麻烦,而且它更灵活.
这不可能.请注意
data Axes2D = X | Y
data Axes3D = Axes2D | Z
Run Code Online (Sandbox Code Playgroud)
将Axes2D在Axes3D类型是值构造不带参数,所以Axes3D有两个构造,Axes2D和Z.
不同的类型不能具有相同名称的值构造函数(在相同的范围内),因为这会使类型推断变得不可能.什么会
foo X = True
foo _ = False
Run Code Online (Sandbox Code Playgroud)
作为一种类型?(它与参数类型有点不同,它们都Maybe a具有相同名称的值构造函数,并且可以工作.但这是因为Maybe它接受一个类型参数,并且名称仅在使用相同(一元)类型构造函数构造的类型之间共享.适用于nullary类型的构造函数.)