我想在一个涉及在图像上排列文本和对象的应用程序中使用Haskell自定义运算符。例如,我将有一个垂直构图的运算符:将一个元素放在另一个元素上面。我们称之为|||。
让我们声明一个数据类型Element来表示可视对象。一种可视元素可能是字符串中指定的某些文本。在视觉上,这会创建一些默认字体和大小的单词。另一种视觉元素可能是JPEG文件。所以我们有
type Filename = String
data Element = EString String
| EJpeg Filename
| EVerticalComposition Element Element
我可以声明|||运算符如下:
(|||) :: Element -> Element -> Element
但是,为简洁起见,我想写一些类似的东西
composedElem = "some words" ||| "some words that appear below"
||| "and more words"
请注意,我不想在每个字符串前面添加EString。
所以我觉得我需要|||成为包含String实例和Element实例的类型类的一部分。我可能会有这样的事情:
class ImageClass a where
(|||) :: a -> a -> a
instance ImageClass String where
(|||) x y = EVerticalComposition x y
但假设(|||)
是左关联的,我在上面几个术语的表达中使用它,我还需要(|||)
来操作Elmeents,比如
(|||) :: Element -> String -> Element
这甚至可能吗?或者,如果是这样,值得吗?我也可以这样编写我的小程序:
composedElem = EString "some Words" ||| "some words that appear below"
||| "and more words"
但是如果用(|||)
来放置其他类型的元素呢?我不想把构造函数放在所有东西面前,只是为了简洁起见,除非这太复杂而无法实现。
答案 0 :(得分:3)
最好的方法是使用类将自定义类型转换为Element
,然后让运算符|||
获取该类的泛型参数。
例如:
{-# LANGUAGE FlexibleInstances #-}
module Main where
type Filename = String
data Element = EString String
| EJpeg Filename
| EVerticalComposition Element Element
deriving (Show)
class AsElement a where
toElement :: a -> Element
instance AsElement Element where
toElement = id
instance AsElement String where
toElement = EString
(|||) :: (AsElement a, AsElement b) => a -> b -> Element
a ||| b = EVerticalComposition (toElement a) (toElement b)
infixl 4 |||
testElement1 :: Element
testElement1 = "this" ||| "that"
testElement2 :: Element
testElement2 = "this" ||| EJpeg "lol" ||| "another"
结果:
λ> testElement1
EVerticalComposition (EString "this") (EString "that")
λ> testElement2
EVerticalComposition (EVerticalComposition (EString "this") (EJpeg "lol")) (EString "another")
只需添加AsElement
的更多实例即可添加更多类型。