haskell - 具有多个数据构造函数的单一类型与多个类型

标签 haskell

考虑以下描述某些二维形状结构的类型:

data DrawingElem
    = Rect     Pos Size
    | Circle   Pos Radius
    | Ellipse  Pos Radius Radius
    | Line     Pos Pos
    | Polygon  [Pos]
    | Polyline [Pos]

    | Group    [DrawingElem]
    | Drawing  [DrawingElem]

使用这些定义:

data Vec    = Vec Double Double

type Pos    = Vec
type Size   = Vec
type Radius = Double

DrawingElem 的最后两个数据构造函数有些特殊,因为它们使其他类型的树状排列成为可能。

mydrawing = Drawing [Rect (Vec 0 0) (Vec 10 10),
                     Group (Vec 30 40) [Circle (Vec 0 0) 90,
                                        Line (Vec 0 0) (Vec 50 50)]]

这样的数据结构最终应该被转换成可渲染的 SVG 字符串:

toSvg :: DrawingElem -> String
toSvg (Drawing xs)               = "<svg>" ++ concatMap toSvg xs ++ "</svg>"
toSvg (Group xs)                 = "<g>" ++ concatMap toSvg xs ++ "</g>"
toSvg (Rect (Vec x y) (Vec w h)) = "<rect x='" ++ x ... "</rect>"

出于这个目的,在我看来有必要将不同的形状包装在 DrawingElem 类型中。它们必须具有相同的类型才能嵌套并最终呈现。

在其他一些情况下,我希望它们是不同的类型:比如说一个设置矩形大小的函数(这只对矩形有意义,其他的没有大小的概念) :

setSize :: Size -> Rect -> Rect

这当然不适用于上述定义,必须是:

setSize :: Size -> DrawingElem -> DrawingElem
setSize (Rect p s) = ..
setSize x          = x

所以我必须实现一个使函数完整的通配符。然而,在没有类型错误的情况下编写 setSize someSize someCircle 对我来说看起来有问题。

所以最后我努力将绘图元素包装在类型 VS 中。让他们成为不同的类型。如上所述,在不同情况下都需要这两个属性。 有人对此有建议吗?是非此即彼,还是有一种方法可以利用这两种方式对其进行建模?

最佳答案

一个选择是使用另一个间接层,并为每个元素指定一个精确的类型:

data DrawingElem
    = DERect     Rect
    | DECircle   Circle
    ...

data Rect   = Rect Pos Size
data Circle = Circle Pos Radius

toSvg :: DrawingElem -> String
...

setSize :: Size -> Rect -> Rect
...

作为这里的一个小缺点,我们需要对两个层进行模式匹配,例如

toSvg (DERect (Rect pos size)) = ...

更高级的替代方法可能是使用 GADT。不过,这对您的任务来说可能有点过头了。

{-# LANGUAGE GADTs, DataKinds #-}

data ElemType = Rect | Circle | ...

data DrawingElem (t :: ElemType) where
   DERect   :: Pos -> Size   -> DrawingElem Rect
   DECircle :: Pos -> Radius -> DrawingElem Circle
   ...

-- this works on all element types t
toSvg :: DrawingElem t -> String
...

-- this works only on a rectangle element
setSize :: Size -> DrawingElem Rect -> DrawingElem Rect
setSize size (DERect pos _) = DERect pos size

我不确定您是否真的需要这个。如有疑问,请坚持使用更简单的替代方案。

关于haskell - 具有多个数据构造函数的单一类型与多个类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40739071/

相关文章:

python - Zope 测试浏览器的 Haskell 等价物

shell - haskell 和 Unix shell 脚本

haskell - zipper : mapping over last breadcrumb

haskell - Haskell LLVM-General 代码生成中的 Segfault

arrays - Haskell Data.Vector.Storable.unsafeFromForeignPtr 与 C 结构指针/数组字段

haskell - 使用镜头库构建复杂的功能

haskell - 在 Haskell 中移动到新行 - 更新

haskell - 如何用haskell处理Windows上的信号?

haskell - Haskell类型函数?

haskell - Sum 新类型对非数字类型有意义吗?