使用特定类定义类型同义词

时间:2015-07-09 10:12:55

标签: haskell types

我仍然有时会失去Haskell的语法,但这次我不确定是否有可能做我想做的事。

我想拥有的是

import System.IO

class (Eq l, Show l) => Label l -- assume there might be some `where ...`

type Edge l = Label l => (Int, l, Int) -- my attempt on restricting l to be Label
type Graph l = [Edge l] -- inherently requiring l to be Label

addEdge :: Graph l -> Edge l -> Graph l
addEdge g e = e : g

data MyLabel = A | B | C deriving (Eq, Show)
instance Label MyLabel

main :: IO ()
main = do
    putStrLn $ show $ addEdge [] (1, A, 2) -- producing Graph MyLabel
    putStrLn $ show $ addEdge [] (1, 3, 2) -- not passing type checks

我可以做的是将Label l =>上下文添加到addEdge等所有函数中,但仍然允许Graph的实例标签不符合类Label它也会复制约束。另一方面,在类型中使用约束(以某种方式)可以节省大量工作(如果没有正确的标签类型,就不可能只有Edge / Graph的任何实例。)

我能以某种方式在Haskell中做到吗?

1 个答案:

答案 0 :(得分:3)

首先,Haskell中的type不会声明新类型。 type严格来说是类型别名,您至少要求的内容需要使用datanewtype获得的实际新类型。

现在,说出你想要的东西不能完全在haskell中完成 - 也就是说,你不能说"任何时候你看到Edge l,那就是' s对l (Label l)"。

的隐式约束

}限制GADTs。你这样做:

Edge

请注意,我必须为(Label l)添加一个实例 - 如果您需要,您还需要为{-# LANGUAGE GADTs #-} class (Eq l, Show l) => Label l -- assume there might be some `where ...` data Edge l where Edge :: (Label l) => (Int, l, Int) -> Edge l type Graph l = [Edge l] -- does not implicitly provide a (Label l) -- constraint on the type, but any actual object -- will necessarily meet that type instance Show (Edge l) where show (Edge a) = "Edge " ++ show a addEdge :: Graph l -> Edge l -> Graph l addEdge g e = e : g data MyLabel = A | B | C deriving (Eq, Show) instance Label MyLabel main :: IO () main = do print $ addEdge [] $ Edge (1, A, 2) -- producing Graph MyLabel -- print $ addEdge [] $ Edge (1, 3, 2) -- not passing type checks Show (Edge l)手动创建实例。

现在,我在上面所说的关于构造函数但不是类型的约束:让我们想象一下这样的方法添加到Eq

Ord

Label l的实例声明修改为:

class (Eq l, Show l) => Label l where
  defLabel :: l

然后,如果您尝试编写此方法,则无法工作:

Label MyLabel

相反,您需要将instance Label MyLabel where defLabel = A 约束添加为:

newGraph :: Int -> Graph l  -- won't work with this type
newGraph n = zipWith (\f t -> Edge (f, defLabel, t)) [1..n-1] [2..]

但是,如果您只处理已构建的(Label l),则可以依赖构造newGraph :: (Label l) => Int -> Graph l -- works with this type newGraph n = zipWith (\f t -> Edge (f, defLabel, t)) [1..n-1] [2..] 时使用的隐式Edge。例如,这会编译:

(Label l)

样式注释:大多数人会将Edge构造函数编写为:

isAllDefault :: Graph l -> Bool  -- Look, no Label constraint
isAllDefault [] = True
isAllDefault (Edge (_, l, _):gs) = l == defLabel && isAllDefault gs
                        -- but I can still use defLabel here
                        -- and can rely on (Eq l) from the Label definition

以便您以Edge方式调用它。我选择使用一个元组来制作一些代码,而不是这个答案的主要内容更短,但是作为一般Haskell风格的问题,你应该知道作为输入类型的元组是不受欢迎的。